v3
veypi 4 weeks ago
parent 46a0f2f0fb
commit 3fb1e16f80

@ -5,6 +5,11 @@
[Demo](https://oa.veypi.com)
## Auth
code用来兑换本身应用token和兑换其他应用付出权限token
oa code 可以用来生成其他code和刷新本身code
### 依赖库

@ -30,18 +30,13 @@ gen_db:
@sqlx database create -D $(db)
@sqlx migrate --source ./protoc/sql run -D $(db)
gen_api:
gen_api: fmt
@goctl api format --dir ./protoc/api/
@goctl api go -api ./protoc/api/all.api -dir ./
ts_dir= ../oaweb/composables/api/
gen_ts:
@goctl api ts -api ./protoc/api/user.api -dir $(ts_dir)
@mv $(ts_dir)/main.ts $(ts_dir)/user.ts
@goctl api ts -api ./protoc/api/app.api -dir $(ts_dir)
@mv $(ts_dir)/main.ts $(ts_dir)/app.ts
gen_ts: fmt
@goctl api ts -api ./protoc/api/all.api -dir $(ts_dir)
@rm $(ts_dir)/main.ts
run:
@go run main.go -f ./etc/main.yaml

@ -1,14 +1,16 @@
package app
import (
"fmt"
"net/http"
"oa/errs"
"github.com/zeromicro/go-zero/rest/httpx"
"oa/internal/logic/app"
"oa/internal/svc"
"oa/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
)
func LoginHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
@ -18,6 +20,7 @@ func LoginHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
errs.Response(w, nil, err)
return
}
fmt.Printf("\n|%v|\n", req)
l := app.NewLoginLogic(r.Context(), svcCtx)
resp, err := l.Login(&req)

@ -15,7 +15,7 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
server.AddRoutes(
[]rest.Route{
{
Method: http.MethodPost,
Method: http.MethodGet,
Path: "/login/:pa",
Handler: app.LoginHandler(serverCtx),
},

@ -25,6 +25,5 @@ func NewLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LoginLogic
func (l *LoginLogic) Login(req *types.AppReq) (resp *types.AppResp, err error) {
// todo: add your logic here and delete this line
return
}

@ -3,6 +3,7 @@ package user
import (
"context"
"database/sql"
"fmt"
"strings"
"time"
@ -33,6 +34,7 @@ func NewLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LoginLogic
func (l *LoginLogic) Login(req *types.LoginReq) (string, error) {
// todo: add your logic here and delete this line
m := models.NewUserModel(l.svcCtx.Sqlx())
fmt.Printf("\n|%v|\n", req)
var u *models.User
var err error
switch req.Typ {

@ -2,9 +2,8 @@
package types
type AppReq struct {
Appname string `json:"username"`
Appname string `form:"username"`
Password string `json:"password"`
Pas string `query:"pas"`
Pa string `path:"pa"`
}
@ -17,9 +16,9 @@ type AppResp struct {
type LoginReq struct {
Id string `path:"id"`
Client string `json:"client"`
Pwd string `json:"pwd"`
Typ string `json:"typ,optional"`
Pwd string `form:"pwd"`
Client string `form:"client"`
Typ string `form:"typ,optional"`
}
type RegReq struct {

@ -3,9 +3,8 @@ import "base.api"
type (
// 定义登录接口的请求体
AppReq {
appname string `json:"username"`
appname string `form:"username"`
Password string `json:"password"`
Pas string `query:"pas"`
Pa string `path:"pa"`
}
// 定义登录接口的响应体
@ -33,6 +32,6 @@ service main {
// 路由为 /app/login
// 请求体为 LoginReq
// 响应体为 LoginResp响应体必须有 returns 关键字修饰
post /login/:pa (AppReq) returns (AppResp)
get /login/:pa (AppReq) returns (AppResp)
}

@ -3,9 +3,9 @@ import "base.api"
type (
LoginReq {
id string `path:"id"`
client string `json:"client"`
pwd string `json:"pwd"`
typ string `json:"typ,optional"`
pwd string `form:"pwd"`
client string `form:"client"`
typ string `form:"typ,optional"`
}
RegReq {
username string `json:"username"`

@ -6,7 +6,6 @@
//
use std::cell::RefCell;
use std::fmt;
use std::pin::Pin;
use std::rc::Rc;
use std::task::{Context, Poll};

@ -9,6 +9,5 @@ pub mod app;
pub mod appfs;
pub mod auth;
pub mod cors;
pub mod proxy;
pub mod task;
pub mod user;

@ -1,6 +0,0 @@
//
// proxy.rs
// Copyright (C) 2023 veypi <i@veypi.com>
// 2023-10-19 19:31
// Distributed under terms of the MIT license.
//

@ -81,117 +81,6 @@
backdrop-filter: blur(20px); /* 模糊效果 */
z-index: -1;
}
.header {
line-height: 2rem;
width: 100%;
display: flex;
justify-content: start;
align-items: center;
gap: 0.5rem;
.voa-logo {
height: 4rem;
width: 4rem;
}
.txt {
font-size: 1.5rem;
}
}
.username,
.password {
margin: 2rem 0;
position: relative;
width: 100%;
input {
height: 2.5rem;
line-height: 2.5rem;
font-size: 1.5rem;
width: 100%;
margin: 0 1rem;
border: none;
outline: none;
background: none;
}
&:after {
content: "";
position: absolute;
bottom: 0;
left: 1rem;
width: calc(100% - 2rem);
height: 0.1em;
background-color: #000;
transition: all 0.3s;
}
&:hover::after {
left: 0%;
width: 100%;
background-color: #00ffff;
}
}
.ok {
cursor: pointer;
line-height: 3rem;
font-size: 1.5rem;
height: 3rem;
margin: 2rem auto;
width: 40%;
background: #0a0;
border-radius: 1.5rem;
}
.last {
display: flex;
justify-content: space-between;
padding: 0 1rem;
height: 3rem;
.icos {
display: flex;
align-items: center;
gap: 1rem;
div {
opacity: 0.5;
cursor: pointer;
height: 2rem;
width: 2rem;
background-size: cover;
background-position: center;
&:hover {
opacity: 1;
}
}
.github {
background-image: url("../img/github.svg");
}
.google {
background-image: url("../img/google.svg");
}
.wechat {
background-image: url("../img/wechat.svg");
}
}
.txt {
height: 1.5rem;
line-height: 1.5rem;
font-size: 1rem;
div {
cursor: pointer;
opacity: 0.5;
&:hover {
opacity: 1;
}
}
}
}
.close {
height: 2rem;
width: 2rem;
position: absolute;
opacity: 0.5;
top: 1rem;
right: 1rem;
cursor: pointer;
&:active {
opacity: 0.8;
}
}
}
#voa-mask {

@ -9,8 +9,9 @@ import './assets/css/oaer.scss'
export class OAer {
domid: string
id: string
code: string
domid: string
dom: {
mask: HTMLDivElement
frame: HTMLDivElement
@ -18,8 +19,9 @@ export class OAer {
b1?: HTMLDivElement
login_box?: HTMLDivElement
}
constructor(id: string, domid?: string) {
constructor(id: string, code: string, domid?: string) {
this.id = id
this.code = code
this.domid = domid || 'oaer'
this.dom = {
frame: document.querySelector(`#${this.domid}`)!,

@ -1,6 +1,6 @@
import './style.css'
import { OAer } from '../lib/main'
let t = new OAer('123')
let t = new OAer('123', '')
console.log(t.domid)

@ -1,30 +0,0 @@
/*
* access.ts
* Copyright (C) 2023 veypi <i@veypi.com>
* 2023-10-12 19:32
* Distributed under terms of the MIT license.
*/
import ajax from './axios'
export default (app_id: string) => {
return {
local: `./app/${app_id}/access/`,
create(name: string, props?: { name?: string, level?: number, role_id?: string, user_id?: string, rid?: string }) {
return ajax.post(this.local, Object.assign({ name, level: 0 }, props))
},
get(id: string) {
return ajax.get(this.local + id)
},
list(props?: { name?: string, role_id?: string, user_id?: string }) {
return ajax.get(this.local, props)
},
update(uuid: string, props: any) {
return ajax.patch(this.local + uuid, props)
},
del(id: string) {
return ajax.delete(this.local + id)
},
}
}

@ -1,48 +1,12 @@
/*
* app.ts
* Copyright (C) 2023 veypi <i@veypi.com>
* 2023-09-30 17:31
* Distributed under terms of the MIT license.
*/
import webapi from "./gocliRequest"
import * as components from "./mainComponents"
export * from "./mainComponents"
import ajax from './axios'
export default {
local: './app/',
self() {
return ajax.get(this.local, { option: 'oa' })
},
getKey(uuid: string) {
return ajax.get(this.local + uuid + '/key')
},
create(name: string, icon: string) {
return ajax.post(this.local, { name, icon })
},
get(uuid: string) {
return ajax.get(this.local + uuid)
},
list() {
return ajax.get(this.local)
},
update(uuid: string, props: any) {
return ajax.patch(this.local + uuid, props)
},
user(uuid: string) {
if (uuid === '') {
uuid = '-'
}
return {
local: this.local + uuid + '/user/',
list(id: string, data?: any) {
return ajax.get(this.local + id, data)
},
add(uid: string) {
return ajax.post(this.local + uid)
},
update(uid: string, status: number) {
return ajax.patch(this.local + uid, { status })
},
}
},
/**
* @description
* @param params
* @param req
*/
export function login(pa: string, req: components.AppReq, params: components.AppReqParams) {
return webapi.get<components.AppResp>(`/api/app/login/${pa}`, {"params":params, "req":req})
}

@ -1,86 +0,0 @@
/*
* axios.ts
* Copyright (C) 2023 veypi <i@veypi.com>
* 2023-09-22 20:22
* Distributed under terms of the MIT license.
*/
import axios, { AxiosError, AxiosResponse } from 'axios';
// Be careful when using SSR for cross-request state pollution
// due to creating a Singleton instance here;
// If any client changes this (global) instance, it might be a
// good idea to move this instance creation inside of the
// "export default () => {}" function below (which runs individually
// for each client)
const proxy = axios.create({
baseURL: '/api/',
withCredentials: true,
headers: {
'content-type': 'application/json;charset=UTF-8',
},
});
// 请求拦截
const beforeRequest = (config: any) => {
// 设置 token
const token = util.getToken()
// // NOTE 添加自定义头部
token && (config.headers.auth_token = token)
// config.headers['auth_token'] = ''
return config
}
proxy.interceptors.request.use(beforeRequest)
// 响应拦截器
const responseSuccess = (response: AxiosResponse) => {
// eslint-disable-next-line yoda
// 这里没有必要进行判断axios 内部已经判断
// const isOk = 200 <= response.status && response.status < 300
let data = response.data
if (response.config.method === 'head') {
data = JSON.parse(JSON.stringify(response.headers))
}
return Promise.resolve(data)
}
const responseFailed = (error: AxiosError) => {
const { response } = error
if (!window.navigator.onLine) {
alert('没有网络')
return Promise.reject(new Error('请检查网络连接'))
}
console.warn(response)
return Promise.reject(response?.data || response?.headers.error)
}
proxy.interceptors.response.use(responseSuccess, responseFailed)
const ajax = {
get(url: string, data = {}, header?: any) {
return proxy.get<any, any>(url, { params: data, headers: header })
},
head(url: string, data = {}, header?: any) {
return proxy.head<any, any>(url, { params: data, headers: header })
},
delete(url: string, data = {}, header?: any) {
return proxy.delete<any, any>(url, { params: data, headers: header })
},
post(url: string, data = {}, header?: any) {
return proxy.post<any, any>(url, data, { headers: header })
},
put(url: string, data = {}, header?: any) {
return proxy.put<any, any>(url, data, { headers: header })
},
patch(url: string, data = {}, header?: any) {
return proxy.patch<any, any>(url, data, { headers: header })
},
}
export default ajax

@ -0,0 +1,119 @@
/*
* gocliRequest.ts
* Copyright (C) 2024 veypi <i@veypi.com>
* 2024-08-02 17:14
* Distributed under terms of the MIT license.
*/
import axios, { AxiosError, type AxiosResponse } from 'axios';
// Be careful when using SSR for cross-request state pollution
// due to creating a Singleton instance here;
// If any client changes this (global) instance, it might be a
// good idea to move this instance creation inside of the
// "export default () => {}" function below (which runs individually
// for each client)
axios.defaults.withCredentials = true
const proxy = axios.create({
withCredentials: true,
headers: {
'content-type': 'application/json;charset=UTF-8',
},
});
// 请求拦截
const beforeRequest = (config: any) => {
// 设置 token
const token = util.getToken()
config.retryTimes = 3
// NOTE 添加自定义头部
token && (config.headers.Authorization = `Bearer ${token}`)
// config.headers['auth_token'] = ''
return config
}
proxy.interceptors.request.use(beforeRequest)
// 响应拦截器
const responseSuccess = (response: AxiosResponse) => {
// eslint-disable-next-line yoda
// 这里没有必要进行判断axios 内部已经判断
// const isOk = 200 <= response.status && response.status < 300
let data = response.data
return Promise.resolve(data)
}
const responseFailed = (error: AxiosError) => {
const { response, config } = error
if (!window.navigator.onLine) {
alert('没有网络')
return Promise.reject(new Error('请检查网络连接'))
}
console.log(response)
let needRetry = false
if (!needRetry) {
return Promise.reject(response?.data || response?.headers.error)
};
// @ts-ignore
const { __retryCount = 0, retryDelay = 1000, retryTimes } = config;
// 在请求对象上设置重试次数
// @ts-ignore
config.__retryCount = __retryCount;
// 判断是否超过了重试次数
if (__retryCount >= retryTimes) {
return Promise.reject(response?.data || response?.headers.error)
}
// 增加重试次数
// @ts-ignore
config.__retryCount++;
// 延时处理
const delay = new Promise<void>((resolve) => {
setTimeout(() => {
resolve();
}, retryDelay);
});
// 重新发起请求
return delay.then(function() {
return proxy.request(config as any);
});
}
proxy.interceptors.response.use(responseSuccess, responseFailed)
interface data {
params?: any
req?: any
headers?: any
}
export const webapi = {
get<T>(url: string, req: data): Promise<T> {
return proxy.request<T, any>({ method: 'get', url: url, headers: req.headers, data: req.req, params: req.params })
},
head<T>(url: string, req: data): Promise<T> {
return proxy.request<T, any>({ method: 'head', url: url, headers: req.headers, data: req.req, params: req.params })
},
delete<T>(url: string, req: data): Promise<T> {
return proxy.request<T, any>({ method: 'delete', url: url, headers: req.headers, data: req.req, params: req.params })
},
post<T>(url: string, req: data): Promise<T> {
return proxy.request<T, any>({ method: 'post', url: url, headers: req.headers, data: req.req, params: req.params })
},
put<T>(url: string, req: data): Promise<T> {
return proxy.request<T, any>({ method: 'put', url: url, headers: req.headers, data: req.req, params: req.params })
},
patch<T>(url: string, req: data): Promise<T> {
return proxy.request<T, any>({ method: 'patch', url: url, headers: req.headers, data: req.req, params: req.params })
},
}
export default webapi

@ -1,32 +1,14 @@
/*
* index.ts
* Copyright (C) 2023 veypi <i@veypi.com>
* 2023-09-22 20:17
* Copyright (C) 2024 veypi <i@veypi.com>
* 2024-08-02 17:40
* Distributed under terms of the MIT license.
*/
import app from "./app";
import role from "./role";
import token from "./token";
import user from "./user";
import resource from "./resource";
import access from './access';
import nats from './nats'
import tsdb from './tsdb'
import * as user from './user'
import * as app from './app'
const api = {
export default {
user,
app,
token,
role,
resource,
access,
tsdb,
nats
app
}
export default api;

@ -0,0 +1,47 @@
// Code generated by goctl. DO NOT EDIT.
export interface AppReq {
password: string
}
export interface AppReqParams {
username: string
}
export interface AppResp {
id: number
name: string
token: string
expireAt: string
}
export interface LoginReqParams {
pwd: string
client: string
typ: string
}
export interface RegReq {
username: string
pwd: string
}
export interface AuthHeaders {
authorization: string
}
export interface UserResp {
id: string
created: number
updated: number
username: string
nickname: string
email: string
phone: string
icon: string
status: number // 状态0ok1disabled
used: number
space: number
}

@ -1,19 +0,0 @@
/*
* nats.ts
* Copyright (C) 2023 veypi <i@veypi.com>
* 2023-10-19 21:36
* Distributed under terms of the MIT license.
*/
import ajax from './axios'
export default {
local: './nats/',
general() {
return ajax.get(this.local + 'varz')
},
conns() {
return ajax.get(this.local + 'connz', { subs: true })
},
}

@ -1,30 +0,0 @@
/*
* resource.ts
* Copyright (C) 2023 veypi <i@veypi.com>
* 2023-10-12 18:03
* Distributed under terms of the MIT license.
*/
import ajax from './axios'
export default (app_id: string) => {
return {
local: `./app/${app_id}/resource/`,
create(name: string, props?: { des?: string }) {
return ajax.post(this.local, Object.assign({ name }, props))
},
get(id: string) {
return ajax.get(this.local + id)
},
list() {
return ajax.get(this.local)
},
update(uuid: string, props: any) {
return ajax.patch(this.local + uuid, props)
},
del(id: string) {
return ajax.delete(this.local + id)
},
}
}

@ -1,36 +0,0 @@
/*
* role.ts
* Copyright (C) 2023 veypi <i@veypi.com>
* 2023-10-12 15:40
* Distributed under terms of the MIT license.
*/
import ajax from './axios'
export default (app_id: string) => {
return {
local: `./app/${app_id}/role/`,
create(name: string, props?: { des?: string }) {
return ajax.post(this.local, Object.assign({ name }, props))
},
get(id: string) {
return ajax.get(this.local + id)
},
list() {
return ajax.get(this.local)
},
update(uuid: string, props: any) {
return ajax.patch(this.local + uuid, props)
},
del(id: string) {
return ajax.delete(this.local + id)
},
add(id: string, uid: string) {
return ajax.get(this.local + `${id}/user/${uid}`)
},
drop(id: string, uid: string) {
return ajax.delete(this.local + `${id}/user/${uid}`)
}
}
}

@ -1,18 +0,0 @@
/*
* token.ts
* Copyright (C) 2023 veypi <i@veypi.com>
* 2023-09-30 17:37
* Distributed under terms of the MIT license.
*/
import ajax from './axios'
export default (uuid: string) => {
return {
local: './app/' + uuid + '/token/',
get(data: { app_id?: string, token: string }) {
return ajax.post(this.local, data)
},
}
}

@ -1,25 +0,0 @@
/*
* tsdb.ts
* Copyright (C) 2023 veypi <i@veypi.com>
* 2023-10-20 23:21
* Distributed under terms of the MIT license.
*/
import ajax from './axios'
export default {
local: './ts/',
range(query: string, props?: { start?: string, end?: string, step?: string, query?: string }) {
if (query !== undefined) {
// @ts-ignore
props.query = query
} else {
props = { query: query }
}
return ajax.get(this.local + 'query_range', props)
},
query(query: string) {
return ajax.get(this.local + 'query', { query: query })
}
}

@ -1,48 +1,33 @@
import webapi from "./gocliRequest"
import * as components from "./mainComponents"
export * from "./mainComponents"
/*
* user.ts
* Copyright (C) 2023 veypi <i@veypi.com>
* 2023-09-22 20:18
* Distributed under terms of the MIT license.
/**
* @description
* @param req
*/
export function reg(req: components.RegReq) {
return webapi.post<null>(`/api/user`, {"req":req})
}
/**
* @description
* @param params
*/
export function login(id: string, params: components.LoginReqParams) {
return webapi.head<null>(`/api/user/${id}`, {"params":params})
}
import { Base64 } from 'js-base64'
import ajax from './axios'
export default {
local: './user/',
register(username: string, password: string, prop?: any) {
const data = Object.assign({
username: username,
password: Base64.encode(password),
}, prop)
return ajax.post(this.local, data)
},
reset(id: string, p?: string) {
if (p) {
p = Base64.encode(p)
}
return ajax.get(this.local + id + '/reset', { p: p })
},
login(username: string, password: string) {
return ajax.head(this.local + username, {
typ: 'username',
password: Base64.encode(password),
})
},
search(q: string) {
return ajax.get(this.local, { username: q })
},
get(id: number) {
return ajax.get(this.local + id)
},
list(props?: { name?: string, role_id?: string, app_id?: string }) {
return ajax.get(this.local, props)
},
update(id: string, props: any) {
props.test = { a: 1 }
return ajax.patch(this.local + id, props)
},
/**
* @description
*/
export function list() {
return webapi.get<Array<components.UserResp>>(`/api/user`, {})
}
/**
* @description
*/
export function get(id: number) {
return webapi.get<components.UserResp>(`/api/user/${id}`, {})
}

@ -11,17 +11,17 @@ export default defineNuxtConfig({
nitro: {
devProxy: {
"/api": {
target: "http://127.0.0.1:4001/api",
target: "http://127.0.0.1:4000/api",
changeOrigin: true,
ws: true,
},
'/fs': {
target: 'http://127.0.0.1:4001/fs',
target: 'http://127.0.0.1:4000/fs',
changeOrigin: true,
ws: true,
},
'/media': {
target: 'http://127.0.0.1:4001/media',
target: 'http://127.0.0.1:4000/media',
changeOrigin: true,
ws: true,
},
@ -47,4 +47,4 @@ export default defineNuxtConfig({
},
},
modules: ["@pinia/nuxt", "@nuxt/ui"]
})
})

@ -88,6 +88,7 @@
<script lang="ts" setup>
import msg from '@veypi/msg';
import { Base64 } from 'js-base64';
definePageMeta({
@ -126,7 +127,7 @@ const login = () => {
return
}
api.user.login(data.value.username,
data.value.password).then((data: any) => {
{ client: 'vvv', typ: 'sss', pwd: Base64.encodeURL(data.value.password) }).then((data: any) => {
util.setToken(data.auth_token)
// msg.Info('')
// user.fetchUserData()
@ -143,13 +144,16 @@ const register = () => {
if (!checks.value.u || !checks.value.p || !checks.value.p2) {
return
}
api.user.register(data.value.username, data.value.password).then(u => {
api.user.reg({
username: data.value.username, pwd:
Base64.encodeURL(data.value.password)
}).then(u => {
console.log(u)
msg.Info('注册成功')
aOpt.value = ''
}).catch(e => {
console.log(e)
msg.Warn('注册失败:' + e.data)
msg.Warn('注册失败:' + e)
})
}
const reset = () => {

Loading…
Cancel
Save