用户权限编辑 登录跳转

master
veypi 3 years ago
parent f90d4c9371
commit e081e4ecb6

@ -1,12 +1,46 @@
package app package app
import ( import (
"errors"
"github.com/veypi/OneAuth/cfg"
"github.com/veypi/OneAuth/models"
"github.com/veypi/OneAuth/oalib"
"github.com/veypi/OneBD" "github.com/veypi/OneBD"
"github.com/veypi/OneBD/rfc" "github.com/veypi/OneBD/rfc"
"github.com/veypi/utils/jwt"
) )
func Router(r OneBD.Router) { func Router(r OneBD.Router) {
r.Set("/", appHandlerP, rfc.MethodPost, rfc.MethodGet) r.Set("/", appHandlerP, rfc.MethodPost, rfc.MethodGet)
r.Set("/:uuid", appHandlerP, rfc.MethodGet, rfc.MethodPatch) r.Set("/:uuid", appHandlerP, rfc.MethodGet, rfc.MethodPatch)
r.Set("/:uuid/user/:id", auHandlerP, rfc.MethodAll) r.Set("/:uuid/user/:id", auHandlerP, rfc.MethodAll)
r.Set("/:uuid/ping", ping, rfc.MethodGet)
}
func ping(m OneBD.Meta) {
var err error
defer func() {
if err != nil {
m.WriteHeader(rfc.StatusBadRequest)
m.Write([]byte(err.Error()))
} else {
m.WriteHeader(rfc.StatusOK)
m.Write([]byte("ok"))
}
}()
t := m.GetHeader("auth_token")
uuid := m.Params("uuid")
a := &models.App{}
err = cfg.DB().Where("UUID = ?", uuid).First(a).Error
if err != nil {
return
}
p := &oalib.PayLoad{}
ok, err := jwt.ParseToken(t, p, []byte(a.Key))
if err != nil {
return
}
if !ok {
err = errors.New("invalid key")
}
} }

@ -1,12 +1,14 @@
package role package role
import ( import (
"errors"
"github.com/veypi/OneAuth/cfg" "github.com/veypi/OneAuth/cfg"
"github.com/veypi/OneAuth/libs/auth" "github.com/veypi/OneAuth/libs/auth"
"github.com/veypi/OneAuth/libs/base" "github.com/veypi/OneAuth/libs/base"
"github.com/veypi/OneAuth/libs/oerr" "github.com/veypi/OneAuth/libs/oerr"
"github.com/veypi/OneAuth/models" "github.com/veypi/OneAuth/models"
"github.com/veypi/OneBD" "github.com/veypi/OneBD"
"github.com/veypi/utils/log"
"gorm.io/gorm" "gorm.io/gorm"
) )
@ -98,18 +100,25 @@ func (h *roleHandler) Patch() (interface{}, error) {
} }
func (h *roleHandler) Delete() (interface{}, error) { func (h *roleHandler) Delete() (interface{}, error) {
if !h.GetAuth(auth.Role).CanDelete() { log.Warn().Msgf("%s %d", h.UUID, h.GetAuth(auth.Role, h.UUID))
if !h.GetAuth(auth.Role, h.UUID).CanDelete() {
return nil, oerr.NoAuth return nil, oerr.NoAuth
} }
rid := h.Meta().ParamsInt("id") rid := h.Meta().ParamsInt("id")
if rid <= 2 { if rid <= 0 {
return nil, oerr.NoAuth return nil, oerr.ApiArgsError
} }
role := &models.Role{} role := &models.Role{}
role.ID = uint(rid) role.ID = uint(rid)
err := cfg.DB().Where(role).First(role).Error err := cfg.DB().Preload("Users").Where(role).First(role).Error
if err != nil { if err != nil {
return nil, err return nil, err
} }
if role.AppUUID != h.UUID {
return nil, oerr.NoAuth
}
if len(role.Users) != 0 {
return nil, errors.New("关联用户未删除")
}
return nil, cfg.DB().Delete(role).Error return nil, cfg.DB().Delete(role).Error
} }

@ -8,5 +8,6 @@ import (
func Router(r OneBD.Router) { func Router(r OneBD.Router) {
r.Set("/", roleP, rfc.MethodGet, rfc.MethodPost) r.Set("/", roleP, rfc.MethodGet, rfc.MethodPost)
r.Set("/:id", roleP, rfc.MethodGet, rfc.MethodDelete, rfc.MethodPatch) r.Set("/:id", roleP, rfc.MethodGet, rfc.MethodDelete, rfc.MethodPatch)
r.Set("/:id/auth/:aid", roleP, rfc.MethodGet) r.Set("/:id/user/", ruP, rfc.MethodGet)
r.Set("/:id/user/:uid", ruP, rfc.MethodPost, rfc.MethodDelete)
} }

@ -0,0 +1,86 @@
package role
import (
"github.com/veypi/OneAuth/cfg"
"github.com/veypi/OneAuth/libs/auth"
"github.com/veypi/OneAuth/libs/base"
"github.com/veypi/OneAuth/libs/oerr"
"github.com/veypi/OneAuth/models"
"github.com/veypi/OneBD"
"github.com/veypi/OneBD/core"
)
/**
* @name: user
* @author: veypi <i@veypi.com>
* @date: 2021-11-23 10:17
* @descriptionuser
**/
var ruP = OneBD.NewHandlerPool(func() core.Handler {
return &roleUserHandler{}
})
type roleUserHandler struct {
base.AppHandler
}
func (h *roleUserHandler) Get() (interface{}, error) {
if !h.GetAuth(auth.Role, h.UUID).CanRead() {
return nil, oerr.NoAuth
}
id := h.Meta().ParamsInt("id")
if id <= 0 {
return nil, oerr.ApiArgsMissing
}
r := &models.Role{}
err := cfg.DB().Preload("Users").Where("ID = ?", id).First(r).Error
if err != nil {
return nil, err
}
if r.AppUUID != h.UUID {
return nil, oerr.NoAuth
}
return r.Users, nil
}
func (h *roleUserHandler) Post() (interface{}, error) {
if !h.GetAuth(auth.Role, h.UUID).CanCreate() {
return nil, oerr.NoAuth
}
id := h.Meta().ParamsInt("id")
uid := h.Meta().ParamsInt("uid")
if id <= 0 || uid <= 0 {
return nil, oerr.ApiArgsMissing
}
r := &models.Role{}
err := cfg.DB().Where("ID = ?", id).First(r).Error
if err != nil {
return nil, err
}
if r.AppUUID != h.UUID {
return nil, oerr.NoAuth
}
err = auth.BindUserRole(cfg.DB(), uint(uid), uint(id))
return nil, err
}
func (h *roleUserHandler) Delete() (interface{}, error) {
if !h.GetAuth(auth.Role, h.UUID).CanCreate() {
return nil, oerr.NoAuth
}
id := h.Meta().ParamsInt("id")
uid := h.Meta().ParamsInt("uid")
if id <= 0 || uid <= 0 {
return nil, oerr.ApiArgsMissing
}
r := &models.Role{}
err := cfg.DB().Where("ID = ?", id).First(r).Error
if err != nil {
return nil, err
}
if r.AppUUID != h.UUID {
return nil, oerr.NoAuth
}
err = auth.UnBindUserRole(cfg.DB(), uint(uid), uint(id))
return nil, err
}

@ -17,7 +17,7 @@ func Router(r OneBD.Router) {
p := OneBD.NewHandlerPool(func() OneBD.Handler { p := OneBD.NewHandlerPool(func() OneBD.Handler {
return &tokenHandler{} return &tokenHandler{}
}) })
r.Set("/:uuid", p, rfc.MethodGet) r.Set("/", p, rfc.MethodGet)
} }
type tokenHandler struct { type tokenHandler struct {

@ -42,15 +42,15 @@ type handler struct {
// Get get user data // Get get user data
func (h *handler) Get() (interface{}, error) { func (h *handler) Get() (interface{}, error) {
userID := h.Meta().ParamsInt("user_id") userID := uint(h.Meta().ParamsInt("user_id"))
if userID != h.Payload.ID && !h.Payload.GetAuth(auth.User, "").CanRead() {
return nil, oerr.NoAuth.AttachStr("to read user data")
}
if userID != 0 { if userID != 0 {
user := &models.User{} user := &models.User{}
user.ID = uint(userID) user.ID = userID
return user, cfg.DB().Where(user).First(user).Error return user, cfg.DB().Where(user).First(user).Error
} else { } else {
if !h.Payload.GetAuth(auth.User, "").CanRead() {
return nil, oerr.NoAuth.AttachStr("to read user list")
}
username := h.Meta().Query("username") username := h.Meta().Query("username")
if username != "" { if username != "" {
users := make([]*models.User, 0, 10) users := make([]*models.User, 0, 10)

@ -12,4 +12,4 @@ require (
gorm.io/gorm v1.21.3 gorm.io/gorm v1.21.3
) )
//replace github.com/veypi/OneBD v0.4.3 => ../OceanCurrent/OneBD replace github.com/veypi/OneBD v0.4.3 => ../OceanCurrent/OneBD

@ -1,9 +1,9 @@
package auth package auth
import ( import (
"errors"
"github.com/veypi/OneAuth/models" "github.com/veypi/OneAuth/models"
"github.com/veypi/OneAuth/oalib" "github.com/veypi/OneAuth/oalib"
"github.com/veypi/utils"
"gorm.io/gorm" "gorm.io/gorm"
) )
@ -21,20 +21,28 @@ const (
) )
func BindUserRole(tx *gorm.DB, userID uint, roleID uint) error { func BindUserRole(tx *gorm.DB, userID uint, roleID uint) error {
r := &models.Role{} ur := &models.UserRole{}
r.ID = roleID ur.RoleID = roleID
err := tx.Where(r).First(r).Error ur.UserID = userID
if err != nil { err := tx.Where(ur).First(ur).Error
return err if errors.Is(err, gorm.ErrRecordNotFound) {
err = tx.Create(ur).Error
if err == nil {
tx.Model(&models.Role{}).Where("ID = ?", roleID).
Update("UserCount", gorm.Expr("UserCount + ?", 1))
}
} }
return err
}
func UnBindUserRole(tx *gorm.DB, userID uint, roleID uint) error {
ur := &models.UserRole{} ur := &models.UserRole{}
ur.RoleID = roleID ur.RoleID = roleID
ur.UserID = userID ur.UserID = userID
err = utils.MultiErr( err := tx.Unscoped().Where(ur).Delete(ur).Error
tx.Where(ur).FirstOrCreate(ur).Error, if err == nil {
tx.Model(&models.Role{}).Where("ID = ?", roleID). tx.Model(&models.Role{}).Where("ID = ?", roleID).
Update("UserCount", gorm.Expr("UserCount + ?", 1)).Error, Update("UserCount", gorm.Expr("UserCount - ?", 1))
) }
return err return err
} }

@ -14,7 +14,7 @@ const Version = "v0.1.0"
func main() { func main() {
cmd.LoadCfg(cfg.Path, cfg.CFG) cmd.LoadCfg(cfg.Path, cfg.CFG)
app := cli.NewApp() app := cli.NewApp()
app.Name = "github.com/veypi/OneAuth" app.Name = "oneauth"
app.Usage = "one auth" app.Usage = "one auth"
app.Version = Version app.Version = Version
app.Flags = []cli.Flag{ app.Flags = []cli.Flag{
@ -59,7 +59,6 @@ func main() {
if cfg.CFG.Debug { if cfg.CFG.Debug {
cfg.CFG.LoggerLevel = "debug" cfg.CFG.LoggerLevel = "debug"
} }
cfg.ConnectDB()
return nil return nil
} }
_ = app.Run(os.Args) _ = app.Run(os.Args)

@ -10,10 +10,12 @@ import app from './app'
import user from './user' import user from './user'
import auth from './auth' import auth from './auth'
import resource from './resource' import resource from './resource'
import token from './token'
const api = { const api = {
user: user, user: user,
token: token,
app: app, app: app,
auth: auth, auth: auth,
role: role, role: role,

@ -54,10 +54,6 @@ export class Interface {
} }
} else { } else {
newFail(data) newFail(data)
if (data.code === 41001) {
store.commit('user/logout')
// bus.$emit('log_out')
}
} }
} }
this.method(this.api, this.data, newSuccess, newFail) this.method(this.api, this.data, newSuccess, newFail)

@ -16,10 +16,25 @@ export default (uuid: string) => {
}, },
create(Name: string, Tag: string) { create(Name: string, Tag: string) {
return new Interface(ajax.post, this.local, { return new Interface(ajax.post, this.local, {
Name,Tag Name, Tag,
}) })
}, },
bind(id: number, aid: number) { delete(id: number) {
return new Interface(ajax.delete, this.local + id)
},
user(id: number) {
return {
local: this.local + id + '/user/',
list() {
return new Interface(ajax.get, this.local)
},
create(uid: number) {
return new Interface(ajax.post, this.local + uid)
},
delete(uid: number) {
return new Interface(ajax.delete, this.local + uid)
},
}
}, },
} }
} }

@ -0,0 +1,18 @@
/*
* @name: token
* @author: veypi <i@veypi.com>
* @date: 2021-11-26 19:22
* @descriptiontoken
*/
import {Interface} from '@/api/interface'
import ajax from './ajax'
export default (uuid: string) => {
return {
local: '/api/app/' + uuid + '/token/',
get() {
return new Interface(ajax.get, this.local)
},
}
}

@ -1,4 +1,4 @@
import {Base64} from "js-base64"; import {Base64} from 'js-base64'
import {Interface} from './interface' import {Interface} from './interface'
import ajax from './ajax' import ajax from './ajax'
import {BaseUrl} from './setting' import {BaseUrl} from './setting'
@ -8,16 +8,19 @@ export default {
register(username: string, password: string, prop?: any) { register(username: string, password: string, prop?: any) {
const data = Object.assign({ const data = Object.assign({
username: username, username: username,
password: Base64.encode(password) password: Base64.encode(password),
}, prop) }, prop)
return new Interface(ajax.post, this.local, data) return new Interface(ajax.post, this.local, data)
}, },
login(username: string, password: string) { login(username: string, password: string) {
return new Interface(ajax.head, this.local + username, { return new Interface(ajax.head, this.local + username, {
UidType: 'username', UidType: 'username',
password: Base64.encode(password) password: Base64.encode(password),
}) })
}, },
search(q: string) {
return new Interface(ajax.get, this.local, {username: q})
},
get(id: number) { get(id: number) {
return new Interface(ajax.get, this.local + id) return new Interface(ajax.get, this.local + id)
}, },
@ -26,5 +29,5 @@ export default {
}, },
update(id: number, props: any) { update(id: number, props: any) {
return new Interface(ajax.patch, this.local + id, props) return new Interface(ajax.patch, this.local + id, props)
} },
} }

@ -2,7 +2,7 @@
<div class="core rounded-2xl p-3"> <div class="core rounded-2xl p-3">
<div class="grid gap-4 grid-cols-5"> <div class="grid gap-4 grid-cols-5">
<div class="col-span-2"> <div class="col-span-2">
<n-avatar @click="Go" round :size="80" :src="core.Icon"> <n-avatar style="--color: none;" @click="Go" round :size="80" :src="core.Icon">
</n-avatar> </n-avatar>
</div> </div>
<div class="col-span-3 grid grid-cols-1 items-center text-left"> <div class="col-span-3 grid grid-cols-1 items-center text-left">
@ -13,7 +13,7 @@
</div> </div>
</template> </template>
<script setup lang='ts'> <script setup lang='ts'>
import {defineProps, withDefaults} from 'vue' import {withDefaults} from 'vue'
import {useRouter} from 'vue-router' import {useRouter} from 'vue-router'
import {useMessage, useLoadingBar} from 'naive-ui' import {useMessage, useLoadingBar} from 'naive-ui'
import api from '@/api' import api from '@/api'

@ -1,7 +1,7 @@
<template> <template>
<base-frame style="line-height:40px" v-model="shown" :isDark="IsDark"> <base-frame style="line-height:40px" v-model="shown" :isDark="IsDark">
<div class="flex"> <div class="flex">
<n-avatar :src="$store.state.user.local.Icon" round></n-avatar> <n-avatar style="--color: none" :src="$store.state.user.local.Icon" round></n-avatar>
</div> </div>
<template v-slot:main> <template v-slot:main>
<div style="height: 100%"> <div style="height: 100%">

@ -71,6 +71,9 @@ let msg = useMessage()
let id = computed(() => { let id = computed(() => {
return props.role.ID || 0 return props.role.ID || 0
}) })
let value = computed(() => {
return props.modelValue
})
let auths = ref<modelsAuth[]>([]) let auths = ref<modelsAuth[]>([])
let RIDOptions = computed(() => { let RIDOptions = computed(() => {
let l = [] let l = []
@ -94,8 +97,8 @@ let levelOptions = () => {
return l return l
} }
watch(id, () => { watch(value, () => {
if (id.value > 0) { if (id.value > 0 && props.modelValue) {
api.auth(props.uuid).listOfRole(id.value).Start(e => { api.auth(props.uuid).listOfRole(id.value).Start(e => {
auths.value = e auths.value = e
}) })

@ -1,8 +1,93 @@
<template> <template>
<div></div> <div>
<slot>
</slot>
<n-modal @after-leave="emit('update:modelValue', false)" v-model:show="modelValue">
<n-card class="w-4/5 md:w-1/2 rounded-2xl" :title="role.Name" :bordered="false"
size="huge">
<template #header-extra>
<UserSelect @selected="tmp=$event"></UserSelect>
<n-button @click="add"></n-button>
</template>
<div class="grid grid-cols-4 gap-1 gap-y-8" style="line-height: 34px">
<div>ID</div>
<div>昵称</div>
<div>用户名</div>
<div></div>
<template :key="key" v-for="(item, key) in users">
<div>{{ item.ID }}</div>
<div>{{ item.Nickname }}</div>
<div>{{ item.Username }}</div>
<div>
<n-button @click="del(item.ID, key)">删除</n-button>
</div>
</template>
</div>
<template #footer>
</template>
</n-card>
</n-modal>
</div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import {modelsRole, modelsUser} from '@/models'
import {computed, ref, watch} from 'vue'
import api from '@/api'
import {useMessage} from 'naive-ui'
import UserSelect from '@/components/userSelect.vue'
let msg = useMessage()
let props = withDefaults(defineProps<{
uuid: string
role: modelsRole
modelValue: boolean
}>(), {})
let emit = defineEmits<{
(e: 'update:modelValue', v: boolean): void
}>()
let id = computed(() => {
return props.role.ID || 0
})
let value = computed(() => {
return props.modelValue
})
let users = ref<modelsUser[]>([])
function del(uid: number, index: number) {
props.role.UserCount --
api.role(props.uuid).user(id.value).delete(uid).Start(e => {
users.value.splice(index, 1)
msg.success('删除成功')
})
}
let tmp = ref<modelsUser>(null)
function add() {
if (tmp.value && tmp.value.ID > 0) {
api.role(props.uuid).user(id.value).create(tmp.value.ID).Start(e => {
let added = false
for (let u of users.value) {
if (u.ID === tmp.value.ID) {
added = true
}
}
if (!added) {
users.value.push(tmp.value)
props.role.UserCount++
}
})
}
}
watch(value, () => {
if (id.value > 0 && props.modelValue) {
api.role(props.uuid).user(id.value).list().Start(e => {
users.value = e
})
}
})
</script> </script>
<style scoped> <style scoped>

@ -6,7 +6,7 @@
<script lang="ts" setup> <script lang="ts" setup>
// @ts-nocheck // @ts-nocheck
import {onMounted} from "vue"; import {onMounted} from 'vue'
let emit = defineEmits<{ let emit = defineEmits<{
(e: 'update:modelValue', v: boolean): void (e: 'update:modelValue', v: boolean): void
@ -41,7 +41,7 @@ function handleFullscreen() {
} }
} }
onMounted(() => { function sync() {
let isFullscreen = let isFullscreen =
document.fullscreenElement || document.fullscreenElement ||
document.mozFullScreenElement || document.mozFullScreenElement ||
@ -50,19 +50,15 @@ onMounted(() => {
document.mozFullScreen || document.mozFullScreen ||
document.webkitIsFullScreen document.webkitIsFullScreen
isFullscreen = !!isFullscreen isFullscreen = !!isFullscreen
document.addEventListener('fullscreenchange', () => {
emit('update:modelValue', !props.modelValue)
})
document.addEventListener('mozfullscreenchange', () => {
emit('update:modelValue', !props.modelValue)
})
document.addEventListener('webkitfullscreenchange', () => {
emit('update:modelValue', !props.modelValue)
})
document.addEventListener('msfullscreenchange', () => {
emit('update:modelValue', !props.modelValue)
})
emit('update:modelValue', isFullscreen) emit('update:modelValue', isFullscreen)
}
onMounted(() => {
document.addEventListener('fullscreenchange', sync)
document.addEventListener('mozfullscreenchange', sync)
document.addEventListener('webkitfullscreenchange', sync)
document.addEventListener('msfullscreenchange', sync)
sync()
}) })
</script> </script>

@ -14,34 +14,34 @@
<slot name="sider"></slot> <slot name="sider"></slot>
</n-layout-sider> </n-layout-sider>
<n-layout :style="{'height': $store.state.height}" :native-scrollbar="false"> <n-layout :style="{'height': $store.state.height}" :native-scrollbar="false">
<n-page-header @back="back" class="mx-5"> <div class="mx-5" :style="{'min-height': $store.state.height}">
<template #title> <n-page-header @back="back">
<slot name="title"></slot> <template #title>
</template> <slot name="title"></slot>
<template #subtitle> </template>
<slot name="subtitle"></slot> <template #subtitle>
</template> <slot name="subtitle"></slot>
<template #header> </template>
<n-breadcrumb> <template #header>
<n-breadcrumb-item @click="go(item)" <n-breadcrumb>
:key="key" <n-breadcrumb-item @click="go(item)"
v-for="(item, key) in breads"> :key="key"
<one-icon class="inline-block" v-if="item.Type==='icon'">{{ item.Name }}</one-icon> v-for="(item, key) in breads">
<span v-else>{{ item.Name }}</span> <one-icon class="inline-block" v-if="item.Type==='icon'">{{ item.Name }}</one-icon>
</n-breadcrumb-item> <span v-else>{{ item.Name }}</span>
</n-breadcrumb> </n-breadcrumb-item>
</template> </n-breadcrumb>
<template #avatar> </template>
<slot name="avatar"></slot> <template #avatar>
</template> <slot name="avatar"></slot>
<template #extra> </template>
<slot name="extra"></slot> <template #extra>
</template> <slot name="extra"></slot>
<template #footer> </template>
<slot name="footer"></slot> <template #footer>
</template> <slot name="footer"></slot>
</n-page-header> </template>
<div class="mx-5"> </n-page-header>
<slot></slot> <slot></slot>
</div> </div>
<n-back-top> <n-back-top>

@ -0,0 +1,54 @@
<template>
<div>
<n-select
filterable
placeholder="搜索用户"
:options="options"
:loading="loading"
clearable
remote
@search="handleSearch"
@update-value="select"
/>
</div>
</template>
<script lang="ts" setup>
import {ref} from 'vue'
import api from '@/api'
import {modelsUser} from '@/models'
let emits = defineEmits<{
(e: 'selected', v:modelsUser): void
}>()
let options = ref([])
let loading = ref(false)
function select(v, o) {
emits('selected', o.user)
}
function handleSearch(query: string) {
if (!query.length) {
options.value = []
return
}
loading.value = true
api.user.search(query).Start((e: modelsUser[]) => {
let l = []
for (let u of e) {
l.push({
label: u.Username,
value: u.ID,
user: u,
})
}
options.value = l
loading.value = false
})
}
</script>
<style scoped>
</style>

@ -26,7 +26,7 @@ export const store = createStore<State>({
}, },
// @ts-ignore // @ts-ignore
state: { state: {
oauuid: '', oauuid: 'jU5Jo5hM',
title: '', title: '',
height: 'calc(100vh - 108px)', height: 'calc(100vh - 108px)',
hideHeader: false, hideHeader: false,

@ -18,27 +18,27 @@ let intputNone = {
color: 'url(0) no-repeat', color: 'url(0) no-repeat',
colorFocus: 'url(0) no-repeat', colorFocus: 'url(0) no-repeat',
colorFocusWarning: 'url(0) no-repeat', colorFocusWarning: 'url(0) no-repeat',
colorFocusError: 'url(0) no-repeat' colorFocusError: 'url(0) no-repeat',
} }
light.overrides = { light.overrides = {
Input: Object.assign({}, intputNone) Input: Object.assign({}, intputNone),
} }
dark.overrides = { dark.overrides = {
Input: Object.assign({ Input: Object.assign({
border: '1px solid #aaa' border: '1px solid #aaa',
}, intputNone) }, intputNone),
} }
light.common.cardColor = '#f4f4f4' light.common.cardColor = '#f4f4f4'
light.common.bodyColor = '#eee' light.common.bodyColor = '#eee'
dark.common.bodyColor = '#2e2e2e' dark.common.bodyColor = '#2e2e2e'
light.me = { light.me = {
lightBox: '#f4f4f4', lightBox: '#f4f4f4',
lightBoxShadow: '18px 18px 36px #c6c6c6, -18px -18px 36px #fff' lightBoxShadow: '18px 18px 36px #c6c6c6, -18px -18px 36px #fff',
} }
dark.me = { dark.me = {
lightBox: '#2e2e2e', lightBox: '#2e2e2e',
lightBoxShadow: '21px 21px 42px #272727, -21px -21px 42px #353535' lightBoxShadow: '21px 21px 42px #272727, -21px -21px 42px #353535',
} }
export const OsThemeRef = useOsTheme() export const OsThemeRef = useOsTheme()

@ -1,7 +1,7 @@
<template> <template>
<siderframe> <siderframe>
<template v-slot:avatar> <template v-slot:avatar>
<n-avatar @click="util.goto(app.Host)" :src="app.Icon" round size="large"></n-avatar> <n-avatar style="--color:none" @click="util.goto(app.Host)" :src="app.Icon" round size="large"></n-avatar>
</template> </template>
<template #title>{{ app.Name }}</template> <template #title>{{ app.Name }}</template>
<template #subtitle>{{ app.Des }}</template> <template #subtitle>{{ app.Des }}</template>

@ -9,6 +9,7 @@
</div> </div>
</div> </div>
<RoleAuths :res="resources" v-model="raFlag" :uuid="uuid" :role="tmp"></RoleAuths> <RoleAuths :res="resources" v-model="raFlag" :uuid="uuid" :role="tmp"></RoleAuths>
<RoleUsers v-model="ruFlag" :uuid="uuid" :role="tmp"></RoleUsers>
<n-data-table <n-data-table
:bordered="false" :bordered="false"
:columns="columns" :columns="columns"
@ -37,6 +38,7 @@ import {useRoute} from 'vue-router'
import EditorRes from '@/components/editor/resource.vue' import EditorRes from '@/components/editor/resource.vue'
import EditorRole from '@/components/editor/role.vue' import EditorRole from '@/components/editor/role.vue'
import RoleAuths from '@/components/connectors/roleauths.vue' import RoleAuths from '@/components/connectors/roleauths.vue'
import RoleUsers from '@/components/connectors/roleusers.vue'
let store = useStore() let store = useStore()
let route = useRoute() let route = useRoute()
@ -52,7 +54,7 @@ const columns = [
{ {
title: '操作', title: '操作',
key: '', key: '',
render(row) { render(row: modelsRole, index: number) {
return [ return [
h(NButton, { h(NButton, {
class: 'mr-1', class: 'mr-1',
@ -66,10 +68,24 @@ const columns = [
h(NButton, { h(NButton, {
class: 'mr-1', class: 'mr-1',
size: 'small', size: 'small',
onClick: () => console.log(row), onClick: () => {
ruFlag.value = true
tmp.value = row
},
}, },
{default: () => '用户'}, {default: () => '用户'},
), ),
h(NButton, {
class: 'mr-1',
size: 'small',
onClick: () => {
api.role(uuid.value).delete(row.ID).Start(e => {
roles.value.splice(index, 1)
})
},
},
{default: () => '删除'},
),
] ]
}, },
}, },
@ -128,6 +144,7 @@ let tmp = ref({})
let trFlag = ref(false) let trFlag = ref(false)
let roleFlag = ref(false) let roleFlag = ref(false)
let raFlag = ref(false) let raFlag = ref(false)
let ruFlag = ref(false)
</script> </script>

@ -11,10 +11,6 @@
<div v-for="(item, k) in ofApps" class="flex items-center justify-center" :key="k"> <div v-for="(item, k) in ofApps" class="flex items-center justify-center" :key="k">
<AppCard :core="item"></AppCard> <AppCard :core="item"></AppCard>
</div> </div>
<div class="flex items-center justify-center" v-for="(item) in '123456789'.split('')"
:key="item">
<AppCard :core2="{}"></AppCard>
</div>
</div> </div>
</div> </div>
<div class="mt-20" v-if="apps.length > 0"> <div class="mt-20" v-if="apps.length > 0">
@ -23,10 +19,6 @@
<div v-for="(item, k) in apps" class="flex items-center justify-center" :key="k"> <div v-for="(item, k) in apps" class="flex items-center justify-center" :key="k">
<AppCard :core="item"></AppCard> <AppCard :core="item"></AppCard>
</div> </div>
<div class="flex items-center justify-center" v-for="(item) in '123456'.split('')"
:key="item">
<AppCard :core2="{}"></AppCard>
</div>
</div> </div>
</div> </div>
<n-modal v-model:show="new_flag"> <n-modal v-model:show="new_flag">

@ -21,12 +21,13 @@
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import {computed, onMounted, ref} from "vue"; import {computed, onMounted, ref, watch} from 'vue'
import {Theme} from "@/theme"; import {Theme} from '@/theme'
import {useMessage} from 'naive-ui' import {useMessage} from 'naive-ui'
import api from "@/api" import api from '@/api'
import {useRoute, useRouter} from "vue-router"; import {useRoute, useRouter} from 'vue-router'
import {store} from "@/store"; import {store} from '@/store'
import {modelsApp} from '@/models'
let msg = useMessage() let msg = useMessage()
const route = useRoute() const route = useRoute()
@ -36,7 +37,7 @@ const divs = ref([])
let form_ref = ref(null) let form_ref = ref(null)
let data = ref({ let data = ref({
username: '', username: '',
password: '' password: '',
}) })
let rules = { let rules = {
username: [ username: [
@ -45,24 +46,25 @@ let rules = {
validator(r: any, v: any) { validator(r: any, v: any) {
return (v && v.length >= 3 && v.length <= 16) || new Error('长度要求3~16') return (v && v.length >= 3 && v.length <= 16) || new Error('长度要求3~16')
}, },
trigger: ['input', 'blur'] trigger: ['input', 'blur'],
} },
], ],
password: [{ password: [{
required: true, required: true,
validator(r: any, v: any) { validator(r: any, v: any) {
return (v && v.length >= 6 && v.length <= 16) || new Error('长度要求6~16') return (v && v.length >= 6 && v.length <= 16) || new Error('长度要求6~16')
} },
}] }],
} }
let uuid = computed(() => { let uuid = computed(() => {
return route.params.uuid || store.state.oauuid return route.query.uuid
}) })
function login() { function login() {
redirect()
// @ts-ignore // @ts-ignore
form_ref.value.validate((e:any) => { form_ref.value.validate((e: any) => {
if (!e) { if (!e) {
api.user.login(data.value.username, data.value.password).Start((url: string) => { api.user.login(data.value.username, data.value.password).Start((url: string) => {
msg.success('登录成功') msg.success('登录成功')
@ -86,7 +88,22 @@ function login() {
}) })
} }
function redirect() {
console.log(uuid.value)
if (uuid.value !== store.state.oauuid) {
api.app.get(uuid.value as string).Start((app: modelsApp) => {
console.log(app.UserRefreshUrl)
api.token(uuid.value as string).get().Start(e => {
let url = app.UserRefreshUrl.replaceAll('$token', e)
console.log(url)
window.location.href = url
})
})
}
}
onMounted(() => { onMounted(() => {
redirect()
if (divs.value[0]) { if (divs.value[0]) {
// @ts-ignore // @ts-ignore
divs.value[0].focus() divs.value[0].focus()

@ -7,14 +7,6 @@ export default defineConfig({
resolve: { resolve: {
alias: { alias: {
"@": path.resolve(__dirname, "src"), "@": path.resolve(__dirname, "src"),
"components": path.resolve(__dirname, "src/components"),
"styles": path.resolve(__dirname, "src/styles"),
"plugins": path.resolve(__dirname, "src/plugins"),
"views": path.resolve(__dirname, "src/views"),
"layouts": path.resolve(__dirname, "src/layouts"),
"utils": path.resolve(__dirname, "src/utils"),
"apis": path.resolve(__dirname, "src/apis"),
"dirs": path.resolve(__dirname, "src/directives"),
}, },
}, },
plugins: [vue()], plugins: [vue()],

@ -11,11 +11,11 @@ package oalib
type Config struct { type Config struct {
Host string Host string
UUID string UUID string
Key string Key []byte
} }
func (c *Config) Valid() bool { func (c *Config) Valid() bool {
if c != nil && c.Host != "" && c.UUID != "" && c.Key != "" { if c != nil && c.Host != "" && c.UUID != "" && c.Key != nil {
return true return true
} }
return false return false

@ -1,8 +1,11 @@
package oalib package oalib
import ( import (
"errors"
"fmt" "fmt"
"github.com/veypi/utils/jwt" "github.com/veypi/utils/jwt"
"io/ioutil"
"net/http"
) )
/** /**
@ -16,21 +19,46 @@ func New(c *Config) *OA {
if !c.Valid() { if !c.Valid() {
panic("invalid oa config") panic("invalid oa config")
} }
return &OA{cfg: c, Key: []byte(c.Key)} return &OA{cfg: c}
} }
type OA struct { type OA struct {
cfg *Config cfg *Config
Key []byte
} }
func (oa *OA) Ping() { func (oa *OA) Ping() error {
url := fmt.Sprintf("%s/api/app/%s/ping", oa.cfg.Host, oa.cfg.UUID)
client := &http.Client{}
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return err
}
p := &PayLoad{}
t, err := jwt.GetToken(p, oa.cfg.Key)
if err != nil {
return err
}
req.Header.Set("auth_token", t)
resp, err := client.Do(req)
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
if string(body) != "ok" {
return errors.New(string(body))
}
return nil
} }
func (oa *OA) LoginUrl() string { func (oa *OA) LoginUrl() string {
return fmt.Sprintf("%s/login?uuid=%s", oa.cfg.Host, oa.cfg.Key) return fmt.Sprintf("%s/login?uuid=%s", oa.cfg.Host, oa.cfg.UUID)
} }
func (oa *OA) Parse(token string, payload jwt.PayloadInterface) (bool, error) { func (oa *OA) Parse(token string, payload jwt.PayloadInterface) (bool, error) {
return jwt.ParseToken(token, payload, oa.Key) return jwt.ParseToken(token, payload, oa.cfg.Key)
} }

@ -4,11 +4,11 @@
<meta charset="UTF-8"/> <meta charset="UTF-8"/>
<link rel="icon" href="/favicon.ico"/> <link rel="icon" href="/favicon.ico"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Vite App</title> <title>OA</title>
<script type="module" crossorigin src="/static/index.8f7b8116.js"></script> <script type="module" crossorigin src="/static/index.ed0f0dcd.js"></script>
<link rel="modulepreload" href="/static/vendor.29407274.js"> <link rel="modulepreload" href="/static/vendor.04a04dc4.js">
<link rel="stylesheet" href="/static/vendor.7d59d594.css"> <link rel="stylesheet" href="/static/vendor.7d59d594.css">
<link rel="stylesheet" href="/static/index.22e5e80b.css"> <link rel="stylesheet" href="/static/index.430287f7.css">
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>

@ -1,6 +1,7 @@
package sub package sub
import ( import (
"embed"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"github.com/veypi/OneAuth/api" "github.com/veypi/OneAuth/api"
"github.com/veypi/OneAuth/cfg" "github.com/veypi/OneAuth/cfg"
@ -8,14 +9,14 @@ import (
"github.com/veypi/utils/log" "github.com/veypi/utils/log"
) )
// go:embed static/static //go:embed static/static
//var staticFiles embed.FS var staticFiles embed.FS
// go:embed static/favicon.ico //go:embed static/favicon.ico
//var icon []byte var icon []byte
// go:embed static/index.html //go:embed static/index.html
//var indexFile []byte var indexFile []byte
var Web = &cli.Command{ var Web = &cli.Command{
Name: "web", Name: "web",
@ -40,9 +41,9 @@ func RunWeb(c *cli.Context) error {
// TODO media 文件需要检验权限 // TODO media 文件需要检验权限
app.Router().SubRouter("/media/").Static("/", cfg.CFG.MediaDir) app.Router().SubRouter("/media/").Static("/", cfg.CFG.MediaDir)
//app.Router().EmbedDir("/static", staticFiles, "static/static/") app.Router().EmbedDir("/static", staticFiles, "static/static/")
//app.Router().EmbedFile("/favicon.ico", icon) app.Router().EmbedFile("/favicon.ico", icon)
//app.Router().EmbedFile("/*", indexFile) app.Router().EmbedFile("/*", indexFile)
log.Info().Msg("\nRouting Table\n" + app.Router().String()) log.Info().Msg("\nRouting Table\n" + app.Router().String())
return app.Run() return app.Run()

Loading…
Cancel
Save