命名规则统一改为驼峰式

master
veypi 3 years ago
parent aae799c7fa
commit 4afa6f345e

@ -5,6 +5,7 @@ import (
"encoding/hex"
"fmt"
"github.com/veypi/OneAuth/api/app"
"github.com/veypi/OneAuth/api/resource"
"github.com/veypi/OneAuth/api/role"
"github.com/veypi/OneAuth/api/token"
"github.com/veypi/OneAuth/api/user"
@ -36,10 +37,13 @@ func Router(r OneBD.Router) {
})
user.Router(r.SubRouter("/user"))
wx.Router(r.SubRouter("wx"))
appRouter := r.SubRouter("app")
app.Router(appRouter)
role.Router(appRouter.SubRouter("/:uuid/"))
token.Router(appRouter.SubRouter("/:uuid/token"))
app.Router(r.SubRouter("app"))
appRouter := r.SubRouter("/app/:uuid")
role.Router(appRouter.SubRouter("role"))
resource.Router(appRouter.SubRouter("resource"))
token.Router(appRouter.SubRouter("token"))
r.Set("upload", handleUpload, rfc.MethodPost)
//message.Router(r.SubRouter("/message"))

@ -9,11 +9,14 @@ import (
"github.com/veypi/OneBD"
"github.com/veypi/OneBD/rfc"
"github.com/veypi/utils"
"github.com/veypi/utils/log"
"gorm.io/gorm"
"reflect"
)
var appHandlerP = OneBD.NewHandlerPool(func() OneBD.Handler {
h := &appHandler{}
h.Ignore(rfc.MethodGet, rfc.MethodPost)
h.Ignore(rfc.MethodGet)
return h
})
@ -36,6 +39,20 @@ func (h *appHandler) Get() (interface{}, error) {
if err != nil {
return nil, err
}
if option == "key" {
if uuid == "" {
return nil, oerr.ApiArgsError
}
if !h.GetAuth(auth.APP, uuid).CanDoAny() {
return nil, oerr.NoAuth
}
key := utils.RandSeq(32)
err = cfg.DB().Model(&models.App{}).Where("UUID = ?", uuid).Update("Key", key).Error
if err != nil {
return nil, err
}
return key, nil
}
if !h.GetAuth(auth.APP, uuid).CanRead() {
return nil, oerr.NoAuth
}
@ -63,9 +80,12 @@ func (h *appHandler) Get() (interface{}, error) {
}
func (h *appHandler) Post() (interface{}, error) {
if !h.Payload.GetAuth(auth.APP, "").CanCreate() {
return nil, oerr.NoAuth
}
data := &struct {
Name string `json:"name"`
UUID string `json:"uuid"`
Name string
Icon string
}{}
err := h.Meta().ReadJson(data)
if err != nil {
@ -74,51 +94,70 @@ func (h *appHandler) Post() (interface{}, error) {
if data.Name == "" {
return nil, oerr.ApiArgsMissing.AttachStr("name")
}
_ = h.ParsePayload(h.Meta())
a := &models.App{
UUID: data.UUID,
UUID: utils.RandSeq(16),
Name: data.Name,
Key: utils.RandSeq(32),
Icon: data.Icon,
Creator: h.Payload.ID,
EnableRegister: false,
}
a.Key = utils.RandSeq(32)
if data.UUID != "" {
err = cfg.DB().Where("uuid = ?", data.UUID).FirstOrCreate(a).Error
} else {
data.UUID = utils.RandSeq(16)
err = cfg.DB().Create(a).Error
a.UUID = utils.RandSeq(16)
err = cfg.DB().Transaction(func(tx *gorm.DB) error {
e := tx.Create(a).Error
if e != nil {
return e
}
au := &models.AppUser{
AppUUID: a.UUID,
UserID: h.Payload.ID,
Status: models.AUOK,
}
return tx.Create(au).Error
})
if err != nil {
return nil, err
}
return a, nil
}
func Struct2Map(obj interface{}) (data map[string]interface{}) {
data = make(map[string]interface{})
objT := reflect.TypeOf(obj)
objV := reflect.ValueOf(obj)
var item reflect.Value
var k reflect.StructField
for i := 0; i < objT.NumField(); i++ {
k = objT.Field(i)
item = objV.Field(i)
if !item.IsNil() {
data[k.Name] = item.Interface()
}
}
return
}
func (h *appHandler) Patch() (interface{}, error) {
uid := h.Meta().Params("uuid")
if uid == "" || !h.Payload.GetAuth(auth.APP, uid).CanUpdate() {
return nil, oerr.NoAuth
}
opts := struct {
Icon string `json:"icon"`
Name string `json:"name"`
Icon *string
Name *string
EnableRegister *bool
Des *string
Host *string
UserRefreshUrl *string
}{}
if err := h.Meta().ReadJson(&opts); err != nil {
return nil, err
}
target := models.App{
UUID: uid,
}
if err := cfg.DB().Where(&target).First(&target).Error; err != nil {
return nil, err
}
if !h.Payload.GetAuth(auth.APP, target.UUID).CanUpdate() {
return nil, oerr.NoAuth
}
if opts.Name != "" {
target.Name = opts.Name
}
if opts.Icon != "" {
target.Icon = opts.Icon
query := Struct2Map(opts)
log.Warn().Msgf("%#v", query)
if len(query) == 0 {
return nil, nil
}
if err := cfg.DB().Updates(&target).Error; err != nil {
if err := cfg.DB().Table("Apps").Where("UUID = ?", uid).Updates(query).Error; err != nil {
return nil, err
}
return nil, nil

@ -52,24 +52,24 @@ func (h *appUserHandler) Post() (interface{}, error) {
}
status := models.AUOK
target := &models.App{}
err := cfg.DB().Where("uuid = ?", h.uuid).First(target).Error
err := cfg.DB().Where("UUID = ?", h.uuid).First(target).Error
if err != nil {
return nil, err
}
if target.EnableRegister {
if !target.EnableRegister {
status = models.AUApply
}
au, err := app.AddUser(cfg.DB(), h.uuid, uint(id), target.InitRoleID, status)
return au, err
}
func (h *appUserHandler) Update() (interface{}, error) {
func (h *appUserHandler) Patch() (interface{}, error) {
id := h.Meta().ParamsInt("id")
if h.uuid == "" || id <= 0 {
return nil, oerr.ApiArgsMissing
}
props := struct {
Status string `json:"status"`
Status string
}{}
err := h.Meta().ReadJson(&props)
if err != nil {
@ -82,7 +82,7 @@ func (h *appUserHandler) Update() (interface{}, error) {
UserID: uint(id),
AppUUID: h.uuid,
}
err = cfg.DB().Where(au).Update("status", props.Status).Error
err = cfg.DB().Model(au).Where(au).Update("Status", props.Status).Error
return nil, err
}

@ -0,0 +1,76 @@
package resource
import (
"errors"
"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"
)
/**
* @name: resource
* @author: veypi <i@veypi.com>
* @date: 2021-11-18 15:25
* @descriptionresource
**/
var resP = OneBD.NewHandlerPool(func() OneBD.Handler {
return &resourceHandler{}
})
type resourceHandler struct {
base.AppHandler
}
func (h *resourceHandler) Get() (interface{}, error) {
if !h.GetAuth(auth.Res, h.UUID).CanRead() {
return nil, oerr.NoAuth
}
list := make([]*models.Resource, 0, 10)
err := cfg.DB().Where("AppUUID = ?", h.UUID).Find(&list).Error
return list, err
}
func (h *resourceHandler) Post() (interface{}, error) {
if !h.GetAuth(auth.Res, h.UUID).CanCreate() {
return nil, oerr.NoAuth
}
props := &struct {
Name string
Des string
}{}
err := h.Meta().ReadJson(props)
if err != nil {
return nil, err
}
res := &models.Resource{
AppUUID: h.UUID,
Name: props.Name,
Des: props.Des,
}
err = cfg.DB().Create(res).Error
return res, err
}
func (h *resourceHandler) Delete() (interface{}, error) {
if !h.GetAuth(auth.Res, h.UUID).CanDelete() {
return nil, oerr.NoAuth
}
id := uint(h.Meta().ParamsInt("id"))
if id <= 0 {
return nil, oerr.ApiArgsError
}
list := make([]*models.Auth, 0, 10)
err := cfg.DB().Where("ResourceID = ?", id).Find(&list).Error
if err != nil {
return nil, err
}
if len(list) > 0 {
return nil, errors.New("关联权限未删除")
}
err = cfg.DB().Delete(&models.Resource{}, id).Error
return nil, err
}

@ -0,0 +1,17 @@
package resource
/**
* @name: router
* @author: veypi <i@veypi.com>
* @date: 2021-11-18 15:24
* @descriptionrouter
**/
import (
"github.com/veypi/OneBD"
"github.com/veypi/OneBD/rfc"
)
func Router(r OneBD.Router) {
r.Set("/", resP, rfc.MethodGet, rfc.MethodPost)
r.Set("/:id", resP, rfc.MethodDelete)
}

@ -28,7 +28,7 @@ func (h *authHandler) Get() (interface{}, error) {
query := &models.Auth{}
var err error
if aid > 0 {
err = cfg.DB().Where("id = ?", aid).First(query).Error
err = cfg.DB().Where("ID = ?", aid).First(query).Error
return query, err
}
id, _ := strconv.Atoi(h.Meta().Query("id"))
@ -37,12 +37,12 @@ func (h *authHandler) Get() (interface{}, error) {
return nil, oerr.ApiArgsMissing
}
target := &models.App{}
err = cfg.DB().Where("uuid = ?", uuid).First(target).Error
err = cfg.DB().Where("UUID = ?", uuid).First(target).Error
if err != nil {
return nil, err
}
u := &models.User{}
err = cfg.DB().Preload("Roles.Auths").Preload("Auths").Where("id = ?", id).First(u).Error
err = cfg.DB().Preload("Roles.Auths").Preload("Auths").Where("ID = ?", id).First(u).Error
if err != nil {
return nil, err
}

@ -14,28 +14,18 @@ var roleP = OneBD.NewHandlerPool(func() OneBD.Handler {
return &roleHandler{}
})
type baseAppHandler struct {
base.ApiHandler
uuid string
}
func (h *baseAppHandler) Init(m OneBD.Meta) error {
h.uuid = m.Params("uuid")
return h.ApiHandler.Init(m)
}
type roleHandler struct {
baseAppHandler
base.AppHandler
}
func (h *roleHandler) Get() (interface{}, error) {
id := h.Meta().ParamsInt("id")
if !h.GetAuth(auth.Role, h.uuid).CanRead() {
if !h.GetAuth(auth.Role, h.UUID).CanRead() {
return nil, oerr.NoAuth
}
if id > 0 {
role := &models.Role{}
role.AppUUID = h.uuid
role.AppUUID = h.UUID
role.ID = uint(id)
err := cfg.DB().Preload("Auths").Preload("Users").First(role).Error
if err != nil {
@ -44,7 +34,7 @@ func (h *roleHandler) Get() (interface{}, error) {
return role, nil
}
roles := make([]*models.Role, 0, 10)
err := cfg.DB().Where("app_uuid = ?", h.uuid).Find(&roles).Error
err := cfg.DB().Where("AppUUID = ?", h.UUID).Find(&roles).Error
return roles, err
}
@ -69,9 +59,9 @@ func (h *roleHandler) Patch() (interface{}, error) {
return nil, oerr.NoAuth
}
query := &struct {
Name *string `json:"name"`
Name *string
// 角色标签
Tag *string `json:"tag" gorm:"default:''"`
Tag *string `gorm:"default:''"`
}{}
err := h.Meta().ReadJson(query)
if err != nil {
@ -90,13 +80,13 @@ func (h *roleHandler) Patch() (interface{}, error) {
return nil, cfg.DB().Transaction(func(tx *gorm.DB) error {
var err error
if query.Tag != nil && *query.Tag != role.Tag {
err = tx.Model(role).Update("tag", *query.Tag).Error
err = tx.Model(role).Update("Tag", *query.Tag).Error
if err != nil {
return err
}
}
if query.Name != nil && *query.Name != role.Name {
err = tx.Model(role).Update("name", *query.Name).Error
err = tx.Model(role).Update("Name", *query.Name).Error
if err != nil {
return err
}

@ -20,10 +20,9 @@ var rap = OneBD.NewHandlerPool(func() OneBD.Handler {
})
type roleAuthHandler struct {
base.ApiHandler
base.AppHandler
id uint
aid uint
uuid string
}
func (h *roleAuthHandler) Init(m OneBD.Meta) error {
@ -36,7 +35,7 @@ func (h *roleAuthHandler) Init(m OneBD.Meta) error {
}
func (h *roleAuthHandler) Post() (interface{}, error) {
if !h.Payload.GetAuth(auth.Auth, h.uuid).CanCreate() {
if !h.Payload.GetAuth(auth.Auth, h.UUID).CanCreate() {
return nil, oerr.NoAuth
}
return nil, nil

@ -6,9 +6,9 @@ import (
)
func Router(r OneBD.Router) {
r.Set("/role/", roleP, rfc.MethodGet, rfc.MethodPost)
r.Set("/role/:id", roleP, rfc.MethodGet, rfc.MethodDelete, rfc.MethodPatch)
r.Set("/role/:id/auth/:aid", roleP, rfc.MethodGet)
r.Set("/", roleP, rfc.MethodGet, rfc.MethodPost)
r.Set("/:id", roleP, rfc.MethodGet, rfc.MethodDelete, rfc.MethodPatch)
r.Set("/:id/auth/:aid", roleP, rfc.MethodGet)
r.Set("/auth/", authP, rfc.MethodGet)
r.Set("/auth/:id", authP, rfc.MethodGet)
}

@ -31,7 +31,7 @@ func (h *tokenHandler) Get() (interface{}, error) {
}
a := &models.App{}
a.UUID = uuid
err := cfg.DB().Where("uuid = ?", uuid).First(a).Error
err := cfg.DB().Where("UUID = ?", uuid).First(a).Error
if err != nil {
return nil, err
}
@ -61,7 +61,7 @@ func (h *tokenHandler) Get() (interface{}, error) {
return nil, oerr.NoAuth.AttachStr(string(au.Status))
}
u := &models.User{}
err = cfg.DB().Preload("Auths").Preload("Roles.Auths").Where("id = ?", h.Payload.ID).First(u).Error
err = cfg.DB().Preload("Auths").Preload("Roles.Auths").Where("ID = ?", h.Payload.ID).First(u).Error
if err != nil {
return nil, err
}

@ -80,18 +80,19 @@ func (h *handler) Post() (interface{}, error) {
if err != nil {
return nil, oerr.DBErr.Attach(err)
}
log.Warn().Msgf("%v %v", self.EnableRegister, h.GetAuth(auth.User, "").CanCreate())
if !self.EnableRegister && !h.Payload.GetAuth(auth.User, "").CanCreate() {
return nil, oerr.NoAuth.AttachStr("register disabled")
return nil, errors.New("register disabled")
}
var userdata = struct {
Username string `json:"username"`
Password string `json:"password"`
Nickname string `json:"nickname"`
Phone string `json:"phone"`
Email string `json:"email"`
Domain string `json:"domain"`
Title string `json:"title"`
Position string `json:"position"`
Username string
Password string
Nickname string
Phone string
Email string
Domain string
Title string
Position string
}{}
if err := h.Meta().ReadJson(&userdata); err != nil {
return nil, err
@ -137,13 +138,13 @@ func (h *handler) Post() (interface{}, error) {
func (h *handler) Patch() (interface{}, error) {
uid := h.Meta().Params("user_id")
opts := struct {
Password string `json:"password"`
Icon string `json:"icon"`
Nickname string `json:"nickname"`
Phone string `json:"phone" gorm:"type:varchar(20);unique;default:null" json:",omitempty"`
Email string `json:"email" gorm:"type:varchar(50);unique;default:null" json:",omitempty"`
Status string `json:"status"`
Position string `json:"position"`
Password string
Icon string
Nickname string
Phone string `gorm:"type:varchar(20);unique;default:null" json:",omitempty"`
Email string `gorm:"type:varchar(50);unique;default:null" json:",omitempty"`
Status string
Position string
}{}
if err := h.Meta().ReadJson(&opts); err != nil {
return nil, err
@ -208,7 +209,7 @@ func (h *handler) Head() (interface{}, error) {
return nil, oerr.ApiArgsError
}
h.User = new(models.User)
uidType := h.Meta().Query("uid_type")
uidType := h.Meta().Query("UidType")
switch uidType {
case "username":
h.User.Username = uid

@ -100,9 +100,9 @@ func requestCorpToken(corpid, corpsecret string) (string, error) {
"corpsecret": corpsecret,
}
res := &struct {
Errmsg string `json:"errmsg"`
Errcode *uint `json:"errcode"`
AccessToken string `json:"access_token"`
Errmsg string
Errcode *uint
AccessToken string
}{}
err := tools.Query(addr, query, res)
if err != nil {
@ -120,10 +120,10 @@ func requestCorpToken(corpid, corpsecret string) (string, error) {
func getUserID(token, code string) (string, error) {
addr := "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo"
res := &struct {
Errmsg string `json:"errmsg"`
Errcode *uint `json:"errcode"`
UserId string `json:"UserId"`
DeviceId string `json:"device_id"`
Errmsg string
Errcode *uint
UserId string
DeviceId string
}{}
query := map[string]string{
"access_token": token,

@ -6,6 +6,7 @@ import (
"gorm.io/driver/mysql"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/schema"
)
var Path = cmd.GetCfgPath("oa", "settings")
@ -63,14 +64,22 @@ func DB() *gorm.DB {
}
return db
}
var gormCfg = &gorm.Config{
NamingStrategy: schema.NamingStrategy{
SingularTable: false, // 使用单数表名,启用该选项后,`User` 表将是`user`
NoLowerCase: true,
},
}
func ConnectDB() *gorm.DB {
var err error
conn := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8&parseTime=True&loc=Local", CFG.DB.User, CFG.DB.Pass, CFG.DB.Addr, CFG.DB.DB)
if CFG.DB.Type == "sqlite" {
conn = CFG.DB.Addr
db, err = gorm.Open(sqlite.Open(conn), &gorm.Config{})
db, err = gorm.Open(sqlite.Open(conn), gormCfg)
} else {
db, err = gorm.Open(mysql.Open(conn), &gorm.Config{})
db, err = gorm.Open(mysql.Open(conn), gormCfg)
}
if err != nil {

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

@ -32,7 +32,7 @@ func AddUser(tx *gorm.DB, uuid string, userID uint, roleID uint, status models.A
return nil, err
}
}
err = tx.Model(&models.App{}).Where("uuid = ?", uuid).Update("user_count", gorm.Expr("user_count + ?", 1)).Error
err = tx.Model(&models.App{}).Where("UUID = ?", uuid).Update("UserCount", gorm.Expr("UserCount + ?", 1)).Error
return au, err
}
return nil, err
@ -50,7 +50,7 @@ func EnableUser(tx *gorm.DB, uuid string, userID uint) error {
return err
}
if au.Status != models.AUOK {
return tx.Where(au).Update("status", models.AUOK).Error
return tx.Where(au).Update("Status", models.AUOK).Error
}
return nil
}
@ -63,5 +63,5 @@ func DisableUser(tx *gorm.DB, uuid string, userID uint) error {
AppUUID: uuid,
}
au.UserID = userID
return tx.Where(au).Update("status", models.AUDisable).Error
return tx.Where(au).Update("Status", models.AUDisable).Error
}

@ -32,8 +32,8 @@ func BindUserRole(tx *gorm.DB, userID uint, roleID uint) error {
ur.UserID = userID
err = utils.MultiErr(
tx.Where(ur).FirstOrCreate(ur).Error,
tx.Model(&models.Role{}).Where("id = ?", roleID).
Update("user_count", gorm.Expr("user_count + ?", 1)).Error,
tx.Model(&models.Role{}).Where("ID = ?", roleID).
Update("UserCount", gorm.Expr("UserCount + ?", 1)).Error,
)
return err
}

@ -0,0 +1,20 @@
package base
import "github.com/veypi/OneBD"
/**
* @name: app_handler
* @author: veypi <i@veypi.com>
* @date: 2021-11-18 15:27
* @descriptionapp_handler
**/
type AppHandler struct {
ApiHandler
UUID string
}
func (h *AppHandler) Init(m OneBD.Meta) error {
h.UUID = m.Params("uuid")
return h.ApiHandler.Init(m)
}

@ -3,46 +3,46 @@ package models
var AppKeys = map[string]string{}
type App struct {
UUID string `json:"uuid" gorm:"primaryKey;size:32"`
CreatedAt JSONTime `json:"created_at"`
UpdatedAt JSONTime `json:"updated_at"`
DeletedAt *JSONTime `json:"deleted_at" sql:"index"`
Name string `json:"name"`
Icon string `json:"icon"`
Des string `json:"des"`
Creator uint `json:"creator"`
UserCount uint `json:"user_count"`
Users []*User `json:"users" gorm:"many2many:app_users;"`
UUID string `gorm:"primaryKey;size:32"`
CreatedAt JSONTime
UpdatedAt JSONTime
DeletedAt *JSONTime `sql:"index"`
Name string
Icon string
Des string
Creator uint
UserCount uint
Users []*User `gorm:"many2many:AppUsers;"`
// 初始用户角色
InitRoleID uint `json:"init_role_id"`
InitRole *Role `json:"init_role"`
InitRoleID uint
InitRole *Role
// 是否在首页隐藏
Hide bool `json:"hide"`
// PubKey string `json:"pub_key"`
// PrivateKey string `json:"private_key"`
Hide bool
// PubKey string
// PrivateKey string
// 认证成功跳转链接
Host string `json:"host"`
Host string
// 加解密用户token (key+key2)
// 两个key都是请求获取时刷新
// key oa发放给app 双方保存 针对app生成 每个应用有一个
// key2 app发放给oa app保存 oa使用一次销毁 针对当个用户生成 每个用户有一个
// 获取app用户加密秘钥key2
// TODO
UserRefreshUrl string `json:"user_refresh_url"`
UserRefreshUrl string
// app 校验用户token时使用
Key string `json:"-"`
// 是否允许用户自动加入应用
EnableRegister bool `json:"enable_register"`
EnableRegister bool
//
EnableUserKey bool `json:"enable_user_key"`
UserKeyUrl string `json:"user_key_url"`
EnableUserKey bool
UserKeyUrl string
// 允许登录方式
EnableUser bool `json:"enable_user"`
EnableWx bool `json:"enable_wx"`
EnablePhone bool `json:"enable_phone"`
EnableEmail bool `json:"enable_email"`
EnableUser bool
EnableWx bool
EnablePhone bool
EnableEmail bool
Wx *Wechat `json:"wx" gorm:"-"`
Wx *Wechat `gorm:"-"`
}
type AUStatus string
@ -56,23 +56,23 @@ const (
type AppUser struct {
BaseModel
AppUUID string `json:"app_uuid" gorm:"size:32"`
App *App `json:"app" gorm:"association_foreignkey:UUID"`
UserID uint `json:"user_id"`
User *User `json:"user"`
Status AUStatus `json:"status"`
AppUUID string `gorm:"size:32"`
App *App `gorm:"association_foreignkey:UUID"`
UserID uint
User *User
Status AUStatus
}
type Wechat struct {
BaseModel
AppUUID string `json:"app_uuid" gorm:"size:32"`
App *App `json:"app" gorm:"association_foreignkey:UUID"`
AppUUID string `gorm:"size:32"`
App *App `gorm:"association_foreignkey:UUID"`
// 网页授权登录用
WxID string `json:"wx_id"`
AgentID string `json:"agent_id"`
Url string `json:"url"`
WxID string
AgentID string
Url string
// 获取access_token用
CorpID string `json:"corp_id"`
CorpSecret string `json:"corp_secret"`
CorpID string
CorpSecret string
}

@ -95,8 +95,8 @@ func (jt *JSONTime) SetTime(t time.Time) {
}
type BaseModel struct {
ID uint `json:"id" gorm:"primaryKey"`
CreatedAt JSONTime `json:"created_at"`
UpdatedAt JSONTime `json:"updated_at"`
DeletedAt *JSONTime `json:"deleted_at" sql:"index"`
ID uint `gorm:"primaryKey"`
CreatedAt JSONTime ``
UpdatedAt JSONTime ``
DeletedAt *JSONTime `sql:"index"`
}

@ -2,10 +2,10 @@ package models
type Message struct {
BaseModel
UserID uint `json:"user_id"`
User *User `json:"user"`
Title string `json:"title"`
Redirect string `json:"redirect"`
Content string `json:"content"`
From string `json:"from"`
UserID uint
User *User
Title string
Redirect string
Content string
From string
}

@ -4,50 +4,48 @@ import "github.com/veypi/OneAuth/oalib"
type UserRole struct {
BaseModel
UserID uint `json:"user_id"`
RoleID uint `json:"role_id"`
UserID uint
RoleID uint
}
type Role struct {
BaseModel
AppUUID string `json:"app_uuid" gorm:"size:32"`
App *App `json:"app" gorm:"association_foreignkey:UUID"`
Name string `json:"name"`
AppUUID string `gorm:"size:32"`
App *App `gorm:"association_foreignkey:UUID"`
Name string
// 角色标签
Tag string `json:"tag" gorm:"default:''"`
Users []*User `json:"users" gorm:"many2many:user_roles;"`
Tag string `gorm:"default:''"`
Users []*User `gorm:"many2many:UserRoles;"`
// 具体权限
Auths []*Auth `json:"auths" gorm:"foreignkey:RoleID;references:ID"`
UserCount uint `json:"user_count"`
Auths []*Auth `gorm:"foreignkey:RoleID;references:ID"`
UserCount uint
}
// Auth 资源权限
type Auth struct {
BaseModel
// 该权限作用的应用
AppUUID string `json:"app_uuid" gorm:"size:32"`
App *App `json:"app" gorm:"association_foreignkey:UUID"`
AppUUID string `gorm:"size:32"`
App *App `gorm:"association_foreignkey:UUID"`
// 权限绑定只能绑定一个
RoleID *uint `json:"role_id" gorm:""`
Role *Role `json:"role"`
UserID *uint `json:"user_id"`
User *User `json:"user"`
RoleID *uint `gorm:""`
Role *Role
UserID *uint
User *User
// 资源id
ResourceID uint `json:"resource_id" gorm:"not null"`
Resource *Resource `json:"resource"`
ResourceID uint `gorm:"not null"`
Resource *Resource
// resource_name 用于其他系统方便区分权限的名字
RID string `json:"rid" gorm:""`
RID string `gorm:""`
// 具体某个资源的id
RUID string `json:"ruid"`
Level oalib.AuthLevel `json:"level"`
RUID string
Level oalib.AuthLevel
}
type Resource struct {
BaseModel
AppUUID string `json:"app_uuid" gorm:"size:32"`
App *App `json:"app" gorm:"association_foreignkey:UUID"`
Name string `json:"name"`
// 权限标签
Tag string `json:"tag"`
Des string `json:"des"`
AppUUID string ` gorm:"size:32"`
App *App `gorm:"association_foreignkey:UUID"`
Name string
Des string
}

@ -9,20 +9,20 @@ import (
// User db user model
type User struct {
BaseModel
Username string `json:"username" gorm:"type:varchar(100);unique;not null"`
Nickname string `json:"nickname" gorm:"type:varchar(100)" json:",omitempty"`
Phone string `json:"phone" gorm:"type:varchar(20);unique;default:null" json:",omitempty"`
Email string `json:"email" gorm:"type:varchar(50);unique;default:null" json:",omitempty"`
Username string `gorm:"type:varchar(100);unique;not null"`
Nickname string `gorm:"type:varchar(100)"`
Phone string `gorm:"type:varchar(20);unique;default:null"`
Email string `gorm:"type:varchar(50);unique;default:null"`
CheckCode string `gorm:"type:varchar(64);not null" json:"-"`
RealCode string `gorm:"type:varchar(32);not null" json:"-"`
Position string `json:"position"`
Position string
// disabled 禁用
Status string `json:"status"`
Status string
Icon string `json:"icon"`
Roles []*Role `json:"roles" gorm:"many2many:user_roles;"`
Apps []*App `json:"apps" gorm:"many2many:app_users;"`
Auths []*Auth `json:"auths" gorm:"foreignkey:UserID;references:ID"`
Icon string
Roles []*Role `gorm:"many2many:UserRoles;"`
Apps []*App `gorm:"many2many:AppUsers;"`
Auths []*Auth `gorm:"foreignkey:UserID;references:ID"`
}
func (u *User) String() string {
@ -30,7 +30,7 @@ func (u *User) String() string {
}
func (u *User) LoadAuths(tx *gorm.DB) error {
return tx.Where("id = ?", u.ID).Preload("Auths").Preload("Roles.Auths").First(u).Error
return tx.Where("ID = ?", u.ID).Preload("Auths").Preload("Roles.Auths").First(u).Error
}
func (u *User) GetAuths() []*Auth {

@ -4,7 +4,7 @@
<meta charset="UTF-8"/>
<link rel="icon" href="/favicon.ico"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Vite App</title>
<title>OA</title>
</head>
<body>
<div id="app"></div>

@ -4,7 +4,8 @@
<transition mode="out-in" enter-active-class="animate__fadeInLeft"
leave-active-class="animate__fadeOutRight">
<component class="animate__animated animate__400ms" :is="Component"
style="margin: 10px; min-height: calc(100vh - 108px)"
:style="{'min-height': store.state.height}"
style="margin: 10px"
></component>
</transition>
</router-view>
@ -14,7 +15,6 @@
// This starter template is using Vue 3 <script setup> SFCs
import BaseFrame from './components/frame.vue'
import {onBeforeMount, ref} from 'vue'
import util from './libs/util'
import {useStore} from "./store";
let store = useStore()
@ -24,7 +24,6 @@ onBeforeMount(() => {
if (loader && loader.parentElement) {
loader.parentElement.removeChild(loader)
}
util.title("统一认证")
store.dispatch('fetchSelf')
store.dispatch('user/fetchUserData')
})

@ -14,8 +14,11 @@ export default {
self() {
return new Interface(ajax.get, this.local, {option: 'oa'})
},
create(name: string) {
return new Interface(ajax.post, this.local, {name: name})
getKey(uuid: string) {
return new Interface(ajax.get, this.local + uuid, {option: 'key'})
},
create(name: string, icon: string) {
return new Interface(ajax.post, this.local, {name, icon})
},
get(uuid: string) {
return new Interface(ajax.get, this.local + uuid)
@ -34,7 +37,13 @@ export default {
local: this.local + uuid + '/user/',
list(uid: number) {
return new Interface(ajax.get, this.local + uid)
},
add(uid: number) {
return new Interface(ajax.post, this.local + uid)
},
update(uid: number, status: string) {
return new Interface(ajax.patch, this.local + uid, {status})
},
}
}
}
},
}

@ -9,13 +9,15 @@ import role from "./role";
import app from './app'
import user from './user'
import auth from './auth'
import resource from './resource'
const api = {
user: user,
app: app,
auth: auth,
role: role
role: role,
resource: resource
}
const Api = {

@ -19,7 +19,7 @@ export class Interface {
this.data = data
}
Start(success: SuccessFunction<any>, fail?: FailedFunction<any>) {
Start(success?: SuccessFunction<any>, fail?: FailedFunction<any>) {
const newFail = function (data: any) {
if (data) {
if (data.code === 40001) {
@ -38,6 +38,9 @@ export class Interface {
}
if (fail) {
fail(data.err)
} else {
// @ts-ignore
window.$msg.warning(data.err)
}
}
@ -45,6 +48,9 @@ export class Interface {
if (Number(data.status) === 1) {
if (success) {
success(data.content)
} else {
// @ts-ignore
window.$msg.warning('ok')
}
} else {
newFail(data)

@ -0,0 +1,25 @@
/*
* @name: resource
* @author: veypi <i@veypi.com>
* @date: 2021-11-18 15:52
* @descriptionresource
*/
import {Interface} from './interface'
import ajax from './ajax'
import {BaseUrl} from './setting'
export default (uuid: string) => {
return {
local: BaseUrl + 'app/' + uuid + '/resource/',
list() {
return new Interface(ajax.get, this.local)
},
create(Name: string, Des: string) {
return new Interface(ajax.post, this.local, {Name, Des})
},
delete(id: number) {
return new Interface(ajax.delete, this.local + id)
},
}
}

@ -12,10 +12,9 @@ export default {
}, prop)
return new Interface(ajax.post, this.local, data)
},
login(username: string, password: string, uuid: string) {
login(username: string, password: string) {
return new Interface(ajax.head, this.local + username, {
uid_type: 'username',
uuid: uuid,
UidType: 'username',
password: Base64.encode(password)
})
},

File diff suppressed because one or more lines are too long

@ -1,3 +1,5 @@
import {modelsSimpleAuth} from '@/models'
export const R = {
// 应用管理配置权限
App: 'app',
@ -22,12 +24,6 @@ const level = {
All: 6
}
export interface Auth {
rid: string
ruid: string
level: number
}
class authLevel {
level = level.None
constructor(level: number) {
@ -54,17 +50,17 @@ class authLevel {
}
export class Auths {
private readonly list: [Auth]
private readonly list: modelsSimpleAuth[]
constructor(auths: [Auth]) {
constructor(auths: modelsSimpleAuth[]) {
this.list = auths
}
Get(rid: string, ruid: string): authLevel {
let l = level.None
for (let i of this.list) {
if (i.rid == rid && (i.ruid === '' || i.ruid === ruid) && i.level > l) {
l = i.level
if (i.RID == rid && (i.RUID === '' || i.RUID === ruid) && i.Level > l) {
l = i.Level
}
}
return new authLevel(l)
@ -72,6 +68,6 @@ export class Auths {
}
export function NewAuths(a: any) {
export function NewAuths(a: modelsSimpleAuth[]) {
return new Auths(a)
}

@ -2,29 +2,66 @@
<div class="core rounded-2xl p-3">
<div class="grid gap-4 grid-cols-5">
<div class="col-span-2">
<n-avatar @click="$router.push({name: 'app.main', params: {uuid: core.uuid}})" round :size="80" :src="core.icon">
<n-avatar @click="Go" round :size="80" :src="core.Icon">
</n-avatar>
</div>
<div class="col-span-3 grid grid-cols-1 items-center text-left">
<div class="h-10 flex items-center text-2xl italic font-bold">{{ core.name }}</div>
<div class="select-all">{{ core.uuid }}</div>
<div class="h-10 flex items-center text-2xl italic font-bold">{{ core.Name }}</div>
<span class="truncate">{{ core.Des }}</span>
</div>
</div>
<textarea disabled style="background: none;border: none" class="focus:outline-none w-full">{{core.des}}</textarea>
</div>
</template>
<script setup lang='ts'>
import {defineProps, withDefaults} from "vue";
interface App {
uuid: ''
[key: string]: any
}
import {defineProps, withDefaults} from 'vue'
import {useRouter} from 'vue-router'
import {useMessage, useLoadingBar} from 'naive-ui'
import api from '@/api'
import {useStore} from '@/store'
import {modelsApp} from '@/models'
let router = useRouter()
let store = useStore()
let msg = useMessage()
let bar = useLoadingBar()
let props = withDefaults(defineProps<{
core?: App
core?: modelsApp
}>(), {
// @ts-ignore
core: {}
core: {},
})
function Go() {
switch (props.core.UserStatus) {
case 'ok':
router.push({name: 'app.main', params: {uuid: props.core.UUID}})
return
case 'apply':
msg.info('请等待管理员审批进入')
return
case 'deny':
msg.warning('进入申请未通过')
return
case 'disabled':
msg.warning('已被禁止使用')
return
}
bar.start()
api.app.user(props.core.UUID).add(store.state.user.id).Start(e => {
bar.finish()
if (e.Status === 'ok') {
router.push({name: 'app.main', params: {uuid: props.core.UUID}})
return
}
props.core.UserStatus = e.Status
msg.info('已发起加入申请')
}, (e) => {
msg.warning('加入失败: ' + e)
bar.error()
})
return
}
</script>
<style scoped>
.core {

@ -1,7 +1,7 @@
<template>
<base-frame style="line-height:40px" v-model="shown" :isDark="IsDark">
<div class="flex">
<n-avatar :src="$store.state.user.icon" round></n-avatar>
<n-avatar :src="$store.state.user.local.Icon" round></n-avatar>
</div>
<template v-slot:main>
<div style="height: 100%">
@ -14,12 +14,12 @@
</div>
<div class="grid grid-cols-4 gap-4 h-20">
<div class="flex items-center justify-center">
<n-avatar size="50" :src="$store.state.user.icon" round></n-avatar>
<n-avatar size="50" :src="$store.state.user.local.Icon" round></n-avatar>
</div>
<div class="col-span-2 text-xs grid grid-cols-1 items-center" style="">
<span>昵称: &ensp;&ensp; {{ $store.state.user.nickname }}</span>
<span>账户: &ensp;&ensp; {{ $store.state.user.username }}</span>
<span>邮箱: &ensp;&ensp; {{ $store.state.user.email }}</span>
<span>昵称: &ensp;&ensp; {{ $store.state.user.local.Nickname }}</span>
<span>账户: &ensp;&ensp; {{ $store.state.user.local.Username }}</span>
<span>邮箱: &ensp;&ensp; {{ $store.state.user.local.Email }}</span>
</div>
<div class="">123</div>
</div>
@ -46,7 +46,7 @@
<script lang="ts" setup>
import BaseFrame from './frame.vue'
import {IsDark} from '@/theme'
import {ref} from "vue";
import {ref} from 'vue'
import {useMessage} from 'naive-ui'
// @ts-ignore
window.$msg = useMessage()

@ -2,35 +2,55 @@
<n-config-provider :theme-overrides="Theme.overrides" :locale="zhCN" :date-locale="dateZhCN"
:theme="Theme">
<n-message-provider>
<n-layout class="font-sans select-none">
<n-layout>
<n-layout-header class="pr-5" bordered style="height: 64px;line-height: 64px;">
<div class="inline-block float-left h-full">
<n-layout class="font-sans select-none" style="height: 100vh">
<transition enter-active-class="animate__slideInDown" leave-active-class="animate__slideOutUp">
<one-icon class="header-down animate__animated" @click="$store.commit('hideHeader', false)"
v-if="$store.state.hideHeader">down
</one-icon>
</transition>
<n-layout style="height: calc(100vh - 24px)">
<transition enter-active-class="animate__slideInDown" leave-active-class="animate__slideOutUp">
<n-layout-header class="animate__animated" v-if="!$store.state.hideHeader" bordered
style="height: 64px;line-height: 64px;">
<div class="flex h-full">
<div class="h-full">
<one-icon color="#000" class="inline-block" @click="$router.push('/')"
style="font-size: 48px;margin:8px;color:aqua">
glassdoor
</one-icon>
</div>
<div class="inline-block float-left h-full" style="margin-left: 10px">
<div class="h-full" style="margin-left: 10px">
<n-h6 prefix="bar" align-text>
<n-text type="primary">统一认证系统</n-text>
</n-h6>
</div>
<div v-if="$store.state.user.ready"
class="inline-block h-full float-right flex justify-center items-center">
<avatar></avatar>
<div class="flex-grow flex justify-center">
<span class="text-2xl" style="line-height: 64px">{{ $store.state.title }}</span>
</div>
<div class="inline-block float-right h-full px-3">
<div class="h-full px-3">
<fullscreen v-model="isFullScreen" class="header-icon">fullscreen</fullscreen>
<div class="header-icon">
<one-icon @click="ChangeTheme">
{{ IsDark ? 'Daytimemode' : 'nightmode-fill' }}
</one-icon>
</div>
<div class="header-icon" @click="$store.commit('hideHeader', true)">
<one-icon>up</one-icon>
</div>
</div>
<div v-if="$store.state.user.ready"
class="h-full flex justify-center items-center mr-5">
<avatar></avatar>
</div>
</div>
</n-layout-header>
<n-layout style="height: calc(100vh - 88px)" :native-scrollbar="false">
</transition>
<n-layout :native-scrollbar="false">
<n-loading-bar-provider>
<n-dialog-provider>
<slot></slot>
</n-dialog-provider>
</n-loading-bar-provider>
<n-back-top>
</n-back-top>
</n-layout>
@ -39,7 +59,7 @@
class="flex justify-around px-3 text-gray-500 text-xs">
<span class="hover:text-black cursor-pointer" @click="$router.push({name: 'about'})">关于OA</span>
<span class="hover:text-black cursor-pointer">使用须知</span>
<span class="hover:text-black cursor-pointer" @click="goto('https://veypi.com')">
<span class="hover:text-black cursor-pointer" @click="util.goto('https://veypi.com')">
©2021 veypi
</span>
</n-layout-footer>
@ -49,22 +69,32 @@
</template>
<script lang="ts" setup>
import {Theme, IsDark, ChangeTheme} from "@/theme";
import {Theme, IsDark, ChangeTheme} from '@/theme'
import {zhCN, dateZhCN} from 'naive-ui'
import fullscreen from './fullscreen'
import avatar from './avatar'
import {onMounted, ref} from "vue";
import {onMounted, ref} from 'vue'
import {useStore} from '@/store'
import {useRouter} from 'vue-router'
import util from '@/libs/util'
let goto = (url: any) => {
window.open(url, "_blank")
}
let store = useStore()
let router = useRouter()
let isFullScreen = ref(false)
onMounted(() => {
})
onMounted(() => {
})
</script>
<style scoped>
.header-down {
font-size: 24px;
position: fixed;
right: 76px;
top: 5px;
z-index: 100;
}
</style>

@ -7,14 +7,43 @@
show-trigger="bar"
content-style="padding: 4px;"
bordered
default-collapsed
:default-collapsed="true"
:native-scrollbar="false"
style="height: calc(100vh - 108px)"
:style="{'height': $store.state.height}"
>
<slot name="sider"></slot>
</n-layout-sider>
<n-layout style="height: calc(100vh - 108px);padding-left: 20px" :native-scrollbar="false">
<n-layout :style="{'height': $store.state.height}" :native-scrollbar="false">
<n-page-header @back="back" class="mx-5">
<template #title>
<slot name="title"></slot>
</template>
<template #subtitle>
<slot name="subtitle"></slot>
</template>
<template #header>
<n-breadcrumb>
<n-breadcrumb-item @click="go(item)"
:key="key"
v-for="(item, key) in breads">
<one-icon class="inline-block" v-if="item.Type==='icon'">{{ item.Name }}</one-icon>
<span v-else>{{ item.Name }}</span>
</n-breadcrumb-item>
</n-breadcrumb>
</template>
<template #avatar>
<slot name="avatar"></slot>
</template>
<template #extra>
<slot name="extra"></slot>
</template>
<template #footer>
<slot name="footer"></slot>
</template>
</n-page-header>
<div class="mx-5">
<slot></slot>
</div>
<n-back-top>
</n-back-top>
</n-layout>
@ -22,6 +51,40 @@
</template>
<script lang="ts" setup>
import {modelsBread} from '@/models'
import {useRoute, useRouter} from 'vue-router'
import {computed} from 'vue'
import {useStore} from '@/store'
let router = useRouter()
let route = useRoute()
let store = useStore()
let breads = computed(() => {
let list: modelsBread[] = []
for (let b of store.state.breads) {
list.push(b)
if (b.RName === route.name) {
break
}
}
return list
})
function go(b: modelsBread) {
router.push({
name: b.RName,
params: b.RParams,
query: b.RQuery,
})
}
function back() {
if (breads.value.length > 1) {
let b = breads.value[breads.value.length - 2]
router.push({name: b.RName, query: b.RQuery, params: b.RParams})
}
}
</script>
<style scoped>

@ -0,0 +1,10 @@
/*
* @name: index
* @author: veypi <i@veypi.com>
* @date: 2021-11-17 22:22
* @descriptionindex
* @update: 2021-11-17 22:22
*/
import u from './util'
export const util = u

@ -3,6 +3,20 @@ function padLeftZero(str: string): string {
}
const util = {
goto(url: string) {
window.open(url, '_blank')
},
uploadUrl: '/api/upload',
upload: (src, des, key) => {
if (src.event.target.response) {
let d = JSON.parse(src.event.target.response)
if (d.status === 1) {
des[key] = d.content
return true
}
}
return false
},
title: function (title: string) {
window.document.title = title ? title + ' - oa' : 'veypi project'
},
@ -39,7 +53,7 @@ const util = {
if (/(y+)/.test(fmt)) {
fmt = fmt.replace(
RegExp.$1,
(date.getFullYear() + '').substr(4 - RegExp.$1.length)
(date.getFullYear() + '').substr(4 - RegExp.$1.length),
)
}
const o = {
@ -47,7 +61,7 @@ const util = {
'd+': date.getDate(),
'h+': date.getHours(),
'm+': date.getMinutes(),
's+': date.getSeconds()
's+': date.getSeconds(),
}
for (const k in o) {
if (new RegExp(`(${k})`).test(fmt)) {
@ -56,12 +70,12 @@ const util = {
const str = o[k] + ''
fmt = fmt.replace(
RegExp.$1,
RegExp.$1.length === 1 ? str : padLeftZero(str)
RegExp.$1.length === 1 ? str : padLeftZero(str),
)
}
}
return fmt
}
},
}
export default util

@ -0,0 +1,91 @@
/*
* @name: index
* @author: veypi <i@veypi.com>
* @date: 2021-11-18 17:36
* @descriptionindex
*/
export interface modelsBread {
Index: number
Name: string
Type?: string
RName: string
RParams?: any
RQuery?: any
}
export interface modelsApp {
CreatedAt: string
UpdatedAt: string
DeletedAt: null
Creator: number
Des: string
EnableEmail: boolean
EnablePhone: boolean
EnableRegister: true
EnableUser: boolean
EnableUserKey: boolean
EnableWx: boolean
Hide: boolean
Host: string
Icon: string
InitRole: null
InitRoleID: number
Name: string
UUID: string
UserCount: number
UserKeyUrl: string
UserRefreshUrl: string
UserStatus: string
Users: null
}
export interface modelsUser {
// Index 前端缓存
Index?: number
Apps: modelsApp[]
Auths: null
CreatedAt: string
DeletedAt: null
ID: number
Icon: string
Position: string
Roles: null
Status: string
UpdatedAt: string
Username: string
Email: string
Nickname: string
Phone: string
}
export interface modelsSimpleAuth {
Level: number
RID: string
RUID: string
}
export interface modelsRole {
App?: modelsApp
AppUUID: string
Auths: null
CreatedAt: string
DeletedAt: null
ID: number
Name: string
Tag: string
UpdatedAt: string
UserCount: number
}
export interface modelsResource {
App?: modelsApp
AppUUID: string
CreatedAt: string
DeletedAt: null
Des: string
ID: number
Name: string
UpdatedAt: string
}

@ -1,7 +1,8 @@
import {createRouter, createWebHistory} from 'vue-router'
import {createRouter, createWebHistory, RouteLocationNormalized} from 'vue-router'
import util from '@/libs/util'
import {Auths, R} from '@/auth'
import {store} from "@/store";
import {store} from '@/store'
declare module 'vue-router' {
interface RouteMeta {
@ -10,7 +11,7 @@ declare module 'vue-router' {
title?: string
// 每个路由都必须声明
requiresAuth: boolean
checkAuth?: (a: Auths) => boolean
checkAuth?: (a: Auths, r?: RouteLocationNormalized) => boolean
}
}
@ -23,7 +24,7 @@ const router = createRouter({
meta: {
requiresAuth: true,
},
component: () => import('@/views/home.vue')
component: () => import('@/views/home.vue'),
},
{
path: '/app/:uuid?',
@ -37,7 +38,7 @@ const router = createRouter({
title: '首页',
requiresAuth: true,
},
component: () => import('@/views/app/main.vue')
component: () => import('@/views/app/main.vue'),
},
{
path: 'users',
@ -45,11 +46,11 @@ const router = createRouter({
meta: {
title: '用户',
requiresAuth: true,
checkAuth: a => {
return a.Get(R.User, '').CanRead()
}
checkAuth: (a, r) => {
return a.Get(R.User, r.params.uuid as string).CanRead()
},
component: () => import('@/views/app/users.vue')
},
component: () => import('@/views/app/users.vue'),
},
{
path: 'roles',
@ -57,11 +58,11 @@ const router = createRouter({
meta: {
title: '权限',
requiresAuth: true,
checkAuth: a => {
return a.Get(R.Role, '').CanRead()
}
checkAuth: (a, r) => {
return a.Get(R.Role, r.params.uuid as string).CanRead()
},
component: () => import('@/views/app/roles.vue')
},
component: () => import('@/views/app/roles.vue'),
},
{
path: 'setting',
@ -69,52 +70,55 @@ const router = createRouter({
meta: {
title: '应用设置',
requiresAuth: true,
checkAuth: a => {
return a.Get(R.App, '').CanRead()
}
checkAuth: (a, r) => {
return a.Get(R.App, r.params.uuid as string).CanUpdate()
},
component: () => import('@/views/app/setting.vue')
}
]
},
component: () => import('@/views/app/setting.vue'),
},
],
},
{
path: '/user/setting',
name: 'user_setting',
meta: {
requiresAuth: true
requiresAuth: true,
},
component: () => import('@/views/user_setting.vue')
component: () => import('@/views/user_setting.vue'),
},
{
path: '/about',
name: 'about',
component: () => import('@/views/about.vue')
component: () => import('@/views/about.vue'),
},
{
path: '/wx',
name: 'wx',
component: () => import('@/views/wx.vue')
component: () => import('@/views/wx.vue'),
},
{
path: '/login/:uuid?',
name: 'login',
component: () => import('@/views/login.vue')
component: () => import('@/views/login.vue'),
},
{
path: '/register/:uuid?',
name: 'register',
component: () => import('@/views/register.vue')
component: () => import('@/views/register.vue'),
},
{
path: '/:path(.*)',
name: '404',
component: () => import('@/views/404.vue')
}
component: () => import('@/views/404.vue'),
},
],
})
router.beforeEach((to, from) => {
// to.matched.some(record => record.meta.requiresAuth)
if (to.query.noh === '1') {
store.commit('hideHeader', true)
}
if (to.meta.requiresAuth && !util.checkLogin()) {
// 此路由需要授权,请检查是否已登录
// 如果没有,则重定向到登录页面
@ -125,7 +129,7 @@ router.beforeEach((to, from) => {
}
}
if (to.meta.checkAuth) {
if (!to.meta.checkAuth(store.state.user.auth)) {
if (!to.meta.checkAuth(store.state.user.auth, to)) {
// @ts-ignore
if (window.$msg) {

@ -1,34 +1,76 @@
import {InjectionKey} from 'vue'
import {createStore, useStore as baseUseStore, Store} from 'vuex'
import api from "@/api";
import api from '@/api'
import {User, UserState} from './user'
import {modelsBread} from '@/models'
type Map = { [key: string]: string }
export interface State extends Object {
oauuid: string
title: string
user: UserState
// 页面主页高度
height: string
hideHeader: boolean
breads: modelsBread[]
apps: []
translateCache: Map
}
export const key: InjectionKey<Store<State>> = Symbol()
export const store = createStore<State>({
modules: {
user: User
user: User,
},
// @ts-ignore
state: {
oauuid: '',
apps: []
title: '',
height: 'calc(100vh - 108px)',
hideHeader: false,
apps: [],
translateCache: {},
breads: [{Index: 0, Name: 'home', Type: 'icon', RName: 'home'}],
},
getters: {
cache: (state: State) => (key: string) => {
return state.translateCache[key] || key
},
},
getters: {},
mutations: {
setOA(state: any, data: any) {
state.oauuid = data.uuid
},
setApps(state: State, data: any) {
state.apps = data
},
addCache(state: State, data: Map) {
Object.assign(state.translateCache, data)
},
hideHeader(state: State, i: boolean) {
state.hideHeader = i
if (i) {
state.height = 'calc(100vh - 44px)'
} else {
state.height = 'calc(100vh - 108px)'
}
},
setHeight(state: State, h: string) {
state.height = h
},
setTitle(state: State, to: string) {
state.title = to
},
setBreads(state: State, b: modelsBread) {
let l = state.breads.length
for (let i = l; i < b.Index; i++) {
state.breads.push({} as modelsBread)
}
state.breads[b.Index] = b
},
},
actions: {
fetchSelf({commit}) {
api.app.self().Start(d => {
@ -39,8 +81,8 @@ export const store = createStore<State>({
api.app.list().Start(e => {
commit('setApps', e)
})
}
}
},
},
})
// 定义自己的 `useStore` 组合式函数

@ -5,43 +5,30 @@ import {Base64} from 'js-base64'
import {State} from './index'
import router from "@/router";
import {Auths, NewAuths} from '@/auth'
import {modelsSimpleAuth, modelsUser} from '@/models'
export interface UserState {
id: number
username: string
nickname: string
phone: string
icon: string
email: string
local: modelsUser
ready: boolean
auth: Auths
[key: string]: any
}
export const User: Module<UserState, State> = {
namespaced: true,
state: {
id: 0,
username: '',
nickname: '',
phone: '',
icon: '',
email: '',
local: {} as modelsUser,
auth: NewAuths([]),
ready: false
},
mutations: {
setBase(state: UserState, data: any) {
state.id = data.id
state.icon = data.icon
state.username = data.username
state.nickname = data.nickname
state.phone = data.phone
state.email = data.email
setBase(state: UserState, data: modelsUser) {
state.id = data.ID
state.local = data
state.ready = true
},
setAuth(state: UserState, data: any) {
setAuth(state: UserState, data: modelsSimpleAuth[]) {
state.auth = NewAuths(data)
},
logout(state: UserState) {
@ -57,10 +44,12 @@ export const User: Module<UserState, State> = {
return false
}
let data = JSON.parse(Base64.decode(token[1]))
if (data.id > 0) {
context.commit('setAuth', data.auth)
api.user.get(data.id).Start(e => {
if (data.ID > 0) {
context.commit('setAuth', data.Auth)
api.user.get(data.ID).Start(e => {
context.commit('setBase', e)
},e=> {
context.commit('logout')
})
}
}

@ -1,5 +1,10 @@
<template>
<siderframe>
<template v-slot:avatar>
<n-avatar @click="util.goto(app.Host)" :src="app.Icon" round size="large"></n-avatar>
</template>
<template #title>{{ app.Name }}</template>
<template #subtitle>{{ app.Des }}</template>
<template v-slot:sider>
<div class="grid grid-cols-1">
<div class="cursor-pointer" v-for="(item, key) in navRouter" :key="key">
@ -29,13 +34,15 @@
</template>
<script lang="ts" setup>
import {elementScrollIntoView} from "seamless-scroll-polyfill";
import {useRoute, useRouter} from "vue-router";
import {computed, onMounted, ref, provide} from "vue";
import api from "@/api";
import Siderframe from "@/components/siderframe.vue";
import {useMessage} from "naive-ui";
import {useStore} from "@/store";
import {elementScrollIntoView} from 'seamless-scroll-polyfill'
import {useRoute, useRouter, RouteRecord, RouteLocation, RouteParams} from 'vue-router'
import {computed, onMounted, ref, provide, onBeforeUnmount, Ref} from 'vue'
import api from '@/api'
import Siderframe from '@/components/siderframe.vue'
import {useMessage} from 'naive-ui'
import {useStore} from '@/store'
import {modelsApp, modelsBread} from '@/models'
import util from '@/libs/util'
let store = useStore()
@ -43,7 +50,7 @@ let mgs = useMessage()
let route = useRoute()
let router = useRouter()
let uuid = computed(() => route.params.uuid)
let app = ref({})
let app = ref<modelsApp>({} as modelsApp)
provide('app', app)
provide('uuid', uuid)
let main = ref(null)
@ -51,12 +58,12 @@ let main = ref(null)
let nav = computed(() => main.value ? main.value.nav : [])
let navRouter = ref(buildRouter())
function buildRouter() {
let navs = []
function buildRouter(): RouteRecord[] {
let navs: RouteRecord[] = []
for (let n of router.getRoutes()) {
if (n.name && (n.name as string).startsWith('app')) {
if (n.meta.checkAuth) {
if (n.meta.checkAuth(store.state.user.auth)) {
if (n.meta.checkAuth(store.state.user.auth, route)) {
navs.push(n)
}
} else {
@ -76,23 +83,28 @@ onMounted(() => {
router.push({name: '404', params: {path: route.path}})
return
}
api.app.get(uuid.value as string).Start(e => {
api.app.get(uuid.value as string).Start((e: modelsApp) => {
app.value = e
store.commit('setBreads', {Index: 1, Name: e.Name, RName: 'app.main', RParams: {uuid: e.UUID}} as modelsBread)
store.commit('setTitle', e.Name)
}, e => {
mgs.error('获取应用数据失败: ' + (e.err || e))
router.push({name: '404', params: {path: route.path}})
})
})
onBeforeUnmount(() => {
store.commit('setTitle', '')
})
function goAnchor(element: any) {
// safari not support
// element.scrollIntoView({
// behavior: "smooth"
// })
elementScrollIntoView(element, {behavior: "smooth"});
elementScrollIntoView(element, {behavior: 'smooth'})
}
</script>
<style scoped>

@ -1,8 +1,6 @@
<template>
<div>
<h1 class="page-h1">首页</h1>
{{uuid}}
{{ app }}
<div :ref="el => nav[6]=el" class="my-80">123123</div>
<div :ref="el => nav[k-1]=el" class="mb-64 text-center" v-for="(k) in [1,2,3,4,5,6]" :key="k">
{{ k }}

@ -6,38 +6,58 @@
:columns="columns"
:data="roles"
/>
<div class="flex justify-between">
<h1 class="page-h1">资源管理</h1>
<div class="my-5 mr-10">
<n-button @click="tmp_res={};tr_flag=true"></n-button>
</div>
</div>
<n-data-table class="mb-96" :bordered="false" :data="resources" :columns="resCols">
</n-data-table>
<n-modal v-model:show="tr_flag">
<n-card class="w-4/5 md:w-96 rounded-2xl" :title="tmp_res.index >= 0 ? tmp_res.Name:' '" :bordered="false"
size="huge">
<template #header-extra>{{ tmp_res.index >= 0 ? '编辑' : '创建' }}</template>
<div class="grid grid-cols-5 gap-1 gap-y-8" style="line-height: 34px">
<div>资源名</div>
<div class="col-span-4">
<n-input v-model:value="tmp_res.Name"></n-input>
</div>
<div>资源描述</div>
<div class="col-span-4">
<n-input type="textarea" v-model:value="tmp_res.Des"></n-input>
</div>
</div>
<template #footer>
<div class="flex justify-end">
<n-button class="mx-3" @click="tr_flag=false"></n-button>
<n-button @click="add_res"></n-button>
</div>
</template>
</n-card>
</n-modal>
</div>
</template>
<script lang="ts" setup>
import {h, inject, onMounted, Ref, ref} from 'vue'
import api from '@/api'
import {NButton} from 'naive-ui'
import {NButton, useMessage} from 'naive-ui'
import {modelsBread, modelsResource, modelsRole} from '@/models'
import {useStore} from '@/store'
import {useRoute} from 'vue-router'
let roles = ref([])
let uuid = inject<Ref>('uuid')
let store = useStore()
let route = useRoute()
let msg = useMessage()
let roles = ref<modelsRole[]>([])
let uuid = inject<Ref<string>>('uuid')
const columns = [
{
title: 'ID',
key: 'id',
width: 50,
},
{
title: '角色名',
key: 'name',
width: 100,
fixed: 'left',
},
{
title: '创建时间',
key: 'created_at',
fixed: 'left',
},
{
title: '绑定用户数',
key: 'user_count',
fixed: 'left',
},
{title: 'ID', key: 'ID', width: 50},
{title: '角色名', key: 'Name', width: 100, fixed: 'left'},
{title: '标签', key: 'Tag', width: 100},
{title: '创建时间', key: 'CreatedAt', fixed: 'left'},
{title: '绑定用户数', key: 'UserCount', fixed: 'left'},
{
title: '操作',
key: '',
@ -61,11 +81,63 @@ const columns = [
},
]
onMounted(() => {
store.commit('setBreads', {
Index: 2,
Name: '权限',
RName: route.name,
RParams: route.params,
RQuery: route.query,
} as modelsBread)
api.role(uuid.value).list().Start(e => {
roles.value = e
})
api.resource(uuid.value).list().Start(e => {
resources.value = e
})
})
let resources = ref<modelsResource[]>([])
const resCols = [
{title: 'ID', key: 'ID', width: 50},
{title: 'Name', key: 'Name', width: 200, fixed: 'left'},
{title: '描述', key: 'Des'},
{
title: '操作',
key: '',
width: 100,
fixed: 'right',
render(row, i) {
return h(NButton, {
class: 'mr-1',
size: 'small',
onClick: () => {
api.resource(uuid.value).delete(row.ID).Start(e => {
resources.value.splice(i, 1)
msg.success('删除成功')
})
},
}, {default: () => '删除'},
)
},
},
]
let tmp_res = ref({
index: -1,
Name: '',
Des: '',
})
let tr_flag = ref(false)
function add_res() {
if (tmp_res.value.index >= 0) {
return
}
api.resource(uuid.value).create(tmp_res.value.Name, tmp_res.value.Des).Start(e => {
resources.value.push(e)
tr_flag.value = false
})
}
</script>
<style scoped>

@ -3,55 +3,89 @@
<div style="line-height: 48px" class="inline-block mt-16 grid grid-cols-5 w-1/3 text-center gap-4">
<div>应用名</div>
<div class="col-span-4">
<n-input v-model:value="data.name" @blur="update('name')"></n-input>
<n-input v-model:value="data.Name" @blur="update('Name')"></n-input>
</div>
<div>UUID</div>
<div class="col-span-4 select-all">
{{ data.UUID }}
</div>
<div>Key</div>
<div class="col-span-4">
<n-popconfirm
@positive-click="getKey"
>
<template #trigger>
<n-button>获取</n-button>
</template>
获取key将导致之前的key失效 是否获取?
</n-popconfirm>
</div>
<div>logo</div>
<div class="col-span-4">
<n-upload
action="/api/upload"
:action="util.uploadUrl"
@finish="handleFinish"
:show-file-list="false"
>
<n-avatar size="large" round :src="data.icon">
<n-avatar size="large" round :src="data.Icon">
</n-avatar>
</n-upload>
</div>
<div>自主注册</div>
<div class='col-span-4'>
<n-switch @update:value="update('EnableRegister', $event)" v-model:value='data.EnableRegister'>
<template #checked> 允许</template>
<template #unchecked> 禁止</template>
</n-switch>
</div>
<div>应用简介</div>
<div class='col-span-4 text-left'>
<n-input :autosize="{minRows: 3}" type="textarea" @blur="update('Des')" maxlength="256"
v-model:value="data.Des"></n-input>
</div>
<div>项目首页</div>
<div class="col-span-4 text-left">
<n-input v-model:value="data.Host" @blur="update('Host')"></n-input>
</div>
<div>跳转地址</div>
<div class="col-span-4 text-left">
<n-input v-model:value="data.UserRefreshUrl" @blur="update('UserRefreshUrl')"></n-input>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import {inject, watch, ref, onMounted} from "vue";
import api from "@/api";
import {useMessage} from "naive-ui";
import {inject, watch, ref, onMounted} from 'vue'
import api from '@/api'
import {useDialog, useMessage} from 'naive-ui'
import util from '@/libs/util'
import {modelsApp} from '@/models'
let msg = useMessage()
let app: any = inject('app')
let data = ref({
name: '',
icon: ''
})
let dialog = useDialog()
let app = inject<{ value: modelsApp }>('app')
let data = ref<modelsApp>({} as modelsApp)
function handleFinish(e: any) {
if (e.event.target.response) {
let d = JSON.parse(e.event.target.response)
if (d.status === 1) {
data.value.icon = d.content
update('icon')
return
}
}
if (util.upload(e, data.value, 'Icon')) {
update('Icon')
} else {
msg.error('上传失败')
data.value.icon = app.value.icon
data.value.Icon = app.value.Icon
}
}
function update(key: string) {
function update(key: string, v?: any) {
// @ts-ignore
let v = data.value[key]
if (v === undefined) {
v = data.value[key]
}
console.log([key, v])
if (v === app.value[key]) {
return
}
api.app.update(app.value.uuid, {[key]: v}).Start(e => {
api.app.update(app.value.UUID, {[key]: v}).Start(e => {
msg.success('更新成功')
app.value[key] = v
}, e => {
@ -59,14 +93,22 @@ function update(key: string) {
})
}
function sync() {
data.value.name = app.value.name
data.value.icon = app.value.icon
Object.assign(data.value, app.value)
}
watch(app, sync)
onMounted(sync)
function getKey() {
api.app.getKey(data.value.UUID).Start(e => {
dialog.success({
title: '请保存好秘钥',
content: e
})
})
}
</script>
<style scoped>

@ -1,67 +1,123 @@
<template>
<div>
<div class="flex justify-between">
<h1 class="page-h1">用户名单</h1>
<div class="my-5 mr-10">
<n-button @click="temp_user = {};tu_flag=true">添加用户</n-button>
</div>
</div>
<n-data-table
:bordered="false"
:columns="columns"
:scroll-x="980"
:data="users"
/>
<n-modal v-model:show="tu_flag">
<n-card class="w-4/5 md:w-96 rounded-2xl" :title="temp_user.Index >= 0 ? temp_user.Username:' '" :bordered="false"
size="huge">
<template #header-extra>{{ temp_user.Index >= 0 ? '编辑' : '创建' }}</template>
<div class="grid grid-cols-5 gap-1 gap-y-8" style="line-height: 34px">
<div>用户名</div>
<div class="col-span-4">
<n-input v-model:value="temp_user.Username"></n-input>
</div>
</div>
<template #footer>
<div class="flex justify-end">
<n-button class="mx-3" @click="tu_flag=false"></n-button>
<n-button>更新</n-button>
</div>
</template>
</n-card>
</n-modal>
</div>
</template>
<script lang="ts" setup>
import {inject, onMounted, ref, h} from "vue";
import api from "@/api";
import {NTag} from 'naive-ui'
import {inject, onMounted, ref, h, computed, Ref} from 'vue'
import api from '@/api'
import {NTag as ntag, NButton as nbtn, useDialog} from 'naive-ui'
import {useStore} from '@/store'
import {R} from '@/auth'
import {modelsBread, modelsUser} from '@/models'
import {useRoute} from 'vue-router'
let uuid: any = inject('uuid')
let users = ref([])
let store = useStore()
let route = useRoute()
let dialog = useDialog()
let uuid = inject<Ref<string>>('uuid')
let users = ref<modelsUser[]>([])
let isOA = computed(() => uuid.value === store.state.oauuid)
onMounted(() => {
if (isOA) {
columns.value.push({
title: '操作',
key: '',
width: 200,
render(row, index) {
return [
h(nbtn, {
onClick: () => {
temp_user.value = Object.assign({Index: index}, row.User)
tu_flag.value = true
},
}, {
default: () => '编辑',
}),
]
},
},
)
}
api.app.user(uuid.value as string).list(0).Start(e => {
users.value = e
console.log(e)
})
})
const columns = [
let columns = ref([
{
title: 'ID',
key: 'user_id',
width: 50
key: 'UserID',
width: 100,
},
{
title: '用户',
key: 'user.username',
width: 200,
fixed: 'left'
key: 'User.Username',
width: 100,
fixed: 'left',
},
{
title: '加入时间',
key: 'user.created_at',
fixed: 'left'
key: 'User.CreatedAt',
},
{
title: 'Status',
key: 'status',
key: 'Status',
width: 100,
render(row) {
let t = statusTag(row.status)
return h(NTag,{
'type': t[1]
let t = statusTag(row.Status)
// @ts-ignore
return h(ntag, {
'type': t[1],
onClick: () => {
changeStatus(row)
},
},
{
default: () => t[0]
}
default: () => t[0],
},
)
}
}
]
},
},
])
function statusTag(s: string) {
switch (s) {
case 'ok':
return ['正常', 'success']
case "apply":
case 'apply':
return ['申请中', 'info']
case 'deny':
return ['拒绝', '']
@ -71,6 +127,52 @@ function statusTag(s: string) {
return ['未知', '']
}
function changeStatus(u) {
if (store.state.user.auth.Get(R.User, uuid.value).CanUpdate()) {
dialog.warning({
title: '请选择切换状态',
content: () => {
let tags = []
for (let s of ['ok', 'apply', 'deny', 'disabled']) {
let t = statusTag(s)
if (u.Status !== s) {
// @ts-ignore
tags.push(h(ntag, {
'type': t[1],
onClick: () => {
api.app.user(uuid.value).update(u.UserID, s).Start(e => {
u.Status = s
dialog.destroyAll()
})
},
}, {
default: () => t[0],
}))
}
}
return h('div', {
class: 'flex justify-between mx-16 mt-10',
}, {
default: () => tags,
})
},
})
}
}
let temp_user = ref<modelsUser>({} as modelsUser)
let tu_flag = ref(false)
function add_user() {
}
store.commit('setBreads', {
Index: 2,
Name: '用户',
RName: route.name,
RParams: route.params,
RQuery: route.query,
} as modelsBread)
</script>
<style scoped>

@ -1,7 +1,12 @@
<template>
<div>
<div v-if="ofApps.length > 0">
<h1 class="page-h1">已绑定应用</h1>
<div class="flex justify-between">
<h1 class="page-h1">我的应用</h1>
<div class="my-5 mr-10">
<n-button @click="new_flag=true" v-if="store.state.user.auth.Get(R.App, '').CanCreate()"></n-button>
</div>
</div>
<div class="grid gap-4 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 text-center">
<div v-for="(item, k) in ofApps" class="flex items-center justify-center" :key="k">
<AppCard :core="item"></AppCard>
@ -12,7 +17,7 @@
</div>
</div>
</div>
<div v-if="apps.length > 0">
<div class="mt-20" v-if="apps.length > 0">
<h1 class="page-h1">应用中心</h1>
<div class="grid gap-4 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-5 text-center">
<div v-for="(item, k) in apps" class="flex items-center justify-center" :key="k">
@ -24,41 +29,111 @@
</div>
</div>
</div>
<n-modal v-model:show="new_flag">
<n-card class="w-4/5 md:w-96 rounded-2xl" title="创建应用" :bordered="false"
size="huge">
<n-form label-width="70px" label-align="left" :model="temp_app" ref="form_ref" label-placement="left"
:rules="rules">
<n-form-item required label="应用名" path="name">
<n-input v-model:value="temp_app.name"></n-input>
</n-form-item>
<n-form-item required label="icon" path="icon">
<n-upload
:action="util.uploadUrl"
@finish="(e) => {util.upload(e, temp_app, 'icon')}"
:show-file-list="false"
>
<n-avatar size="large" round :src="temp_app.icon">
</n-avatar>
</n-upload>
</n-form-item>
</n-form>
<template #footer>
<div class="flex justify-end">
<n-button class="mx-3" @click="new_flag=false"></n-button>
<n-button @click="create_new"></n-button>
</div>
</template>
</n-card>
</n-modal>
</div>
</template>
<script lang="ts" setup>
import {onMounted, ref} from "vue";
import api from "@/api";
import {onMounted, ref} from 'vue'
import api from '@/api'
import AppCard from '@/components/app.vue'
import {useStore} from "@/store";
import {useStore} from '@/store'
import {R} from '@/auth'
import util from '@/libs/util'
import {useMessage, useLoadingBar} from 'naive-ui'
import {modelsApp} from '@/models'
let msg = useMessage()
let bar = useLoadingBar()
let store = useStore()
let apps = ref([])
let ofApps = ref([])
let apps = ref<modelsApp[]>([])
let ofApps = ref<modelsApp[]>([])
function getApps() {
bar.start()
api.app.list().Start(e => {
apps.value = e
api.app.user('').list(store.state.user.id).Start(e => {
console.log(e)
bar.finish()
ofApps.value = []
for (let i in e) {
let ai = apps.value.findIndex(a => a.id === e[i].app_id)
let ai = apps.value.findIndex(a => a.UUID === e[i].AppUUID)
if (ai >= 0) {
apps.value[ai].user_status = e[i].status
apps.value[ai].UserStatus = e[i].Status
if (e[i].Status === 'ok') {
ofApps.value.push(apps.value[ai])
apps.value.splice(ai, 1)
}
}
}
}, () => {
bar.error()
})
})
}, () => bar.error())
}
onMounted(() => {
getApps()
})
let new_flag = ref(false)
let temp_app = ref({
name: '',
icon: '',
})
let form_ref = ref(null)
let rules = {
name: [{
required: true,
validator(r: any, v: any) {
return (v && v.length >= 2 && v.length <= 16) || new Error('长度要求2~16')
},
trigger: ['input', 'blur'],
}],
}
function create_new() {
// @ts-ignore
form_ref.value.validate((e: any) => {
if (!e) {
api.app.create(temp_app.value.name, temp_app.value.icon).Start(e => {
e.Status = 'ok'
ofApps.value.push(e)
msg.success('创建成功')
new_flag.value = false
}, e => {
msg.warning('创建失败: ' + e)
})
}
})
}
</script>
<style scoped>

@ -64,7 +64,7 @@ function login() {
// @ts-ignore
form_ref.value.validate((e:any) => {
if (!e) {
api.user.login(data.value.username, data.value.password, uuid.value as string).Start((url: string) => {
api.user.login(data.value.username, data.value.password).Start((url: string) => {
msg.success('登录成功')
store.dispatch('user/fetchUserData')
let target = url

@ -16,7 +16,7 @@
<div v-if="ifInfo" class="animate__animated animate__faster">
<n-form label-placement="left" label-width="80px" label-align="left">
<n-form-item label="昵称">
<n-input v-model:value="user.nickname" @blur="update('nickname')"></n-input>
<n-input v-model:value="user.Nickname" @blur="update('Nickname')"></n-input>
</n-form-item>
<n-form-item label="头像">
<n-upload
@ -24,7 +24,7 @@
@finish="handleFinish"
:show-file-list="false"
>
<n-avatar size="large" round :src="user.icon">
<n-avatar size="large" round :src="user.Icon">
</n-avatar>
</n-upload>
</n-form-item>
@ -32,15 +32,27 @@
</div>
<div v-else class="animate__animated animate__faster">
<n-form label-align="left" label-width="80px" label-placement="left">
<n-form-item label="username">
<n-input disabled v-model:value="user.username"></n-input>
<n-form-item label="Username">
<n-input disabled v-model:value="user.Username"></n-input>
</n-form-item>
<n-form-item label="phone">
<n-input v-model:value="user.phone" @blur="update('phone')"></n-input>
<n-input v-model:value="user.Phone" @blur="update('Phone')"></n-input>
</n-form-item>
<n-form-item label="email">
<n-auto-complete :options="emailOptions" v-model:value="user.email"
@blur="update('email')"></n-auto-complete>
<n-auto-complete :options="emailOptions" v-model:value="user.Email"
@blur="update('Email')"></n-auto-complete>
</n-form-item>
<n-form-item label="邮件通知">
<n-switch>
<template #checked>启用</template>
<template #unchecked>关闭</template>
</n-switch>
</n-form-item>
<n-form-item label="短信通知">
<n-switch>
<template #checked>启用</template>
<template #unchecked>关闭</template>
</n-switch>
</n-form-item>
</n-form>
</div>
@ -50,29 +62,30 @@
</template>
<script lang="ts" setup>
import {ref, computed} from "vue";
import {IsDark} from "@/theme";
import {useStore} from "@/store";
import api from "@/api";
import {useMessage} from "naive-ui";
import {ref, computed} from 'vue'
import {IsDark} from '@/theme'
import {useStore} from '@/store'
import api from '@/api'
import {useMessage} from 'naive-ui'
import {modelsUser} from '@/models'
let msg = useMessage()
let store = useStore()
let ifInfo = ref(true)
let user = ref({
username: store.state.user.username,
nickname: store.state.user.nickname,
icon: store.state.user.icon,
email: store.state.user.email,
phone: store.state.user.phone,
})
let user = ref<modelsUser>({
Username: store.state.user.local.Username,
Nickname: store.state.user.local.Nickname,
Icon: store.state.user.local.Icon,
Email: store.state.user.local.Email,
Phone: store.state.user.local.Phone,
} as modelsUser)
let emailOptions = computed(() => {
return ['@gmail.com', '@163.com', '@qq.com'].map((suffix) => {
const prefix = user.value.email.split('@')[0]
return ['@qq.com', '@163.com', '@gmail.com', '@outlook.com', '@icloud.com', '@169.com'].map((suffix) => {
const prefix = user.value.Email.split('@')[0]
return {
label: prefix + suffix,
value: prefix + suffix
value: prefix + suffix,
}
})
})
@ -81,26 +94,26 @@ function handleFinish(e: any) {
if (e.event.target.response) {
let data = JSON.parse(e.event.target.response)
if (data.status === 1) {
user.value.icon = data.content
user.value.Icon = data.content
update('icon')
return
}
}
msg.error('上传失败')
user.value.icon = store.state.user.icon
user.value.Icon = store.state.user.local.Icon
}
function update(key: string) {
// @ts-ignore
let v = user.value[key]
if (v === store.state.user[key]) {
if (v === store.state.user.local[key]) {
return
}
api.user.update(store.state.user.id, {[key]: v}).Start(e => {
msg.success('更新成功')
store.state.user[key] = v
store.state.user.local[key] = v
}, e => {
msg.error('更新失败: ' + e.err)
msg.error('更新失败: ' + e)
})
}
</script>

@ -12,17 +12,17 @@ import (
)
type SimpleAuth struct {
RID string `json:"rid"`
RID string
// 具体某个资源的id
RUID string `json:"ruid"`
Level AuthLevel `json:"level"`
RUID string
Level AuthLevel
}
// PayLoad TODO:: roles 是否会造成token过大 ?
type PayLoad struct {
jwt.Payload
ID uint `json:"id"`
Auth []*SimpleAuth `json:"auth"`
ID uint
Auth []*SimpleAuth
}
// GetAuth resource_uuid 缺省或仅第一个有效 权限会被更高权限覆盖

@ -35,7 +35,7 @@ func runAppList(c *cli.Context) error {
return err
}
for _, a := range list {
log.Info().Msgf("%d: %s", a.UUID, a.Name)
log.Info().Msgf("%-32s: %s", a.UUID, a.Name)
}
return nil
}

@ -63,7 +63,7 @@ func selfApp() (*models.App, error) {
EnablePhone: false,
EnableEmail: false,
}
return self, cfg.DB().Where("uuid = ?", self.UUID).FirstOrCreate(self).Error
return self, cfg.DB().Where("UUID = ?", self.UUID).FirstOrCreate(self).Error
}
func role(reset_init_role bool) error {
@ -88,7 +88,6 @@ func role(reset_init_role bool) error {
a := &models.Resource{
AppUUID: cfg.CFG.APPUUID,
Name: na,
Tag: "",
Des: "",
}
err = cfg.DB().Where(a).FirstOrCreate(a).Error
@ -114,7 +113,7 @@ func role(reset_init_role bool) error {
return err
}
if reset_init_role {
return cfg.DB().Model(&models.App{}).Where("uuid = ?", cfg.CFG.APPUUID).Update("init_role_id", adminRole.ID).Error
return cfg.DB().Model(&models.App{}).Where("UUID = ?", cfg.CFG.APPUUID).Update("InitRoleID", adminRole.ID).Error
}
return nil
}

@ -100,7 +100,7 @@ func runResourceList(c *cli.Context) error {
return nil
}
for _, r := range l {
log.Info().Msgf("%d: %s@%d", r.ID, r.Name, r.AppUUID)
log.Info().Msgf("%d: %s@%s", r.ID, r.Name, r.AppUUID)
}
return nil
}

Loading…
Cancel
Save