Compare commits
No commits in common. "bc3f5e0b0c7c20ed470acebc9600280ddce2d7ac" and "82b64a4bb290f9fd9b5d467d2121dbe9fa6c21b1" have entirely different histories.
bc3f5e0b0c
...
82b64a4bb2
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
统一验证服务
|
统一验证服务
|
||||||
|
|
||||||
|
|
||||||
## 用户验证思路
|
## 用户验证思路
|
||||||
|
|
||||||

|

|
||||||
|
|||||||
@ -3,25 +3,22 @@ package api
|
|||||||
import (
|
import (
|
||||||
"OneAuth/api/app"
|
"OneAuth/api/app"
|
||||||
"OneAuth/api/role"
|
"OneAuth/api/role"
|
||||||
"OneAuth/api/token"
|
|
||||||
"OneAuth/api/user"
|
"OneAuth/api/user"
|
||||||
"OneAuth/api/wx"
|
"OneAuth/api/wx"
|
||||||
"OneAuth/libs/base"
|
|
||||||
"github.com/veypi/OneBD"
|
"github.com/veypi/OneBD"
|
||||||
"github.com/veypi/OneBD/core"
|
"github.com/veypi/OneBD/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Router(r OneBD.Router) {
|
func Router(r OneBD.Router) {
|
||||||
r.SetNotFoundFunc(func(m core.Meta) {
|
r.SetNotFoundFunc(func(m core.Meta) {
|
||||||
base.JSONResponse(m, nil, nil)
|
m.Write([]byte("{\"status\": 0}"))
|
||||||
})
|
})
|
||||||
r.SetInternalErrorFunc(func(m core.Meta) {
|
r.SetInternalErrorFunc(func(m core.Meta) {
|
||||||
base.JSONResponse(m, nil, nil)
|
m.Write([]byte("{\"status\": 0}"))
|
||||||
})
|
})
|
||||||
user.Router(r.SubRouter("/user"))
|
user.Router(r.SubRouter("/user"))
|
||||||
wx.Router(r.SubRouter("wx"))
|
wx.Router(r.SubRouter("wx"))
|
||||||
app.Router(r.SubRouter("app"))
|
app.Router(r.SubRouter("app"))
|
||||||
token.Router(r.SubRouter("token"))
|
|
||||||
role.Router(r)
|
role.Router(r)
|
||||||
|
|
||||||
//message.Router(r.SubRouter("/message"))
|
//message.Router(r.SubRouter("/message"))
|
||||||
|
|||||||
@ -8,17 +8,15 @@ import (
|
|||||||
"OneAuth/models"
|
"OneAuth/models"
|
||||||
"github.com/veypi/OneBD"
|
"github.com/veypi/OneBD"
|
||||||
"github.com/veypi/OneBD/rfc"
|
"github.com/veypi/OneBD/rfc"
|
||||||
"github.com/veypi/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Router(r OneBD.Router) {
|
func Router(r OneBD.Router) {
|
||||||
r.Set("/", appHandlerP, rfc.MethodPost, rfc.MethodGet)
|
|
||||||
r.Set("/:id", appHandlerP, rfc.MethodGet)
|
r.Set("/:id", appHandlerP, rfc.MethodGet)
|
||||||
}
|
}
|
||||||
|
|
||||||
var appHandlerP = OneBD.NewHandlerPool(func() OneBD.Handler {
|
var appHandlerP = OneBD.NewHandlerPool(func() OneBD.Handler {
|
||||||
h := &appHandler{}
|
h := &appHandler{}
|
||||||
h.Ignore(rfc.MethodGet, rfc.MethodPost)
|
h.Ignore(rfc.MethodGet)
|
||||||
return h
|
return h
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -33,7 +31,7 @@ func (h *appHandler) Get() (interface{}, error) {
|
|||||||
isSelf := h.Meta().Query("is_self")
|
isSelf := h.Meta().Query("is_self")
|
||||||
if isSelf != "" {
|
if isSelf != "" {
|
||||||
// 无权限可以获取本系统基本信息
|
// 无权限可以获取本系统基本信息
|
||||||
h.query.UUID = cfg.CFG.APPUUID
|
h.query.ID = cfg.CFG.APPID
|
||||||
err := cfg.DB().Where(h.query).First(h.query).Error
|
err := cfg.DB().Where(h.query).First(h.query).Error
|
||||||
return h.query, err
|
return h.query, err
|
||||||
}
|
}
|
||||||
@ -66,35 +64,3 @@ func (h *appHandler) Get() (interface{}, error) {
|
|||||||
err = cfg.DB().Find(&list).Error
|
err = cfg.DB().Find(&list).Error
|
||||||
return list, err
|
return list, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *appHandler) Post() (interface{}, error) {
|
|
||||||
data := &struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
UUID string `json:"uuid"`
|
|
||||||
}{}
|
|
||||||
err := h.Meta().ReadJson(data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if data.Name == "" {
|
|
||||||
return nil, oerr.ApiArgsMissing.AttachStr("name")
|
|
||||||
}
|
|
||||||
_ = h.ParsePayload(h.Meta())
|
|
||||||
a := &models.App{
|
|
||||||
UUID: data.UUID,
|
|
||||||
Name: data.Name,
|
|
||||||
Key: utils.RandSeq(32),
|
|
||||||
Creator: h.Payload.ID,
|
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return a, nil
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,69 +0,0 @@
|
|||||||
package token
|
|
||||||
|
|
||||||
import (
|
|
||||||
"OneAuth/cfg"
|
|
||||||
"OneAuth/libs/app"
|
|
||||||
"OneAuth/libs/base"
|
|
||||||
"OneAuth/libs/oerr"
|
|
||||||
"OneAuth/libs/token"
|
|
||||||
"OneAuth/models"
|
|
||||||
"errors"
|
|
||||||
"github.com/veypi/OneBD"
|
|
||||||
"github.com/veypi/OneBD/rfc"
|
|
||||||
"gorm.io/gorm"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Router(r OneBD.Router) {
|
|
||||||
p := OneBD.NewHandlerPool(func() OneBD.Handler {
|
|
||||||
return &tokenHandler{}
|
|
||||||
})
|
|
||||||
r.Set("/:uuid", p, rfc.MethodGet)
|
|
||||||
}
|
|
||||||
|
|
||||||
type tokenHandler struct {
|
|
||||||
base.ApiHandler
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *tokenHandler) Get() (interface{}, error) {
|
|
||||||
uuid := h.Meta().Params("uuid")
|
|
||||||
if uuid == "" {
|
|
||||||
return nil, oerr.ApiArgsMissing.AttachStr("uuid")
|
|
||||||
}
|
|
||||||
a := &models.App{}
|
|
||||||
a.UUID = uuid
|
|
||||||
err := cfg.DB().Where("uuid = ?", uuid).First(a).Error
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
au := &models.AppUser{
|
|
||||||
UserID: h.Payload.ID,
|
|
||||||
AppID: a.ID,
|
|
||||||
}
|
|
||||||
err = cfg.DB().Where(au).First(au).Error
|
|
||||||
if err != nil {
|
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
||||||
if a.EnableRegister {
|
|
||||||
err = cfg.DB().Transaction(func(tx *gorm.DB) error {
|
|
||||||
return app.AddUser(cfg.DB(), au.AppID, au.UserID, a.InitRoleID, models.AUOK)
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
au.Status = models.AUOK
|
|
||||||
} else {
|
|
||||||
return nil, oerr.AppNotJoin.AttachStr(a.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, oerr.DBErr.Attach(err)
|
|
||||||
}
|
|
||||||
if au.Status != models.AUOK {
|
|
||||||
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
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
t, err := token.GetToken(u, a.ID, a.Key)
|
|
||||||
return t, err
|
|
||||||
}
|
|
||||||
@ -3,11 +3,13 @@ package user
|
|||||||
import (
|
import (
|
||||||
"OneAuth/cfg"
|
"OneAuth/cfg"
|
||||||
"OneAuth/libs/app"
|
"OneAuth/libs/app"
|
||||||
"OneAuth/libs/auth"
|
|
||||||
"OneAuth/libs/base"
|
"OneAuth/libs/base"
|
||||||
"OneAuth/libs/oerr"
|
"OneAuth/libs/oerr"
|
||||||
"OneAuth/libs/token"
|
"OneAuth/libs/token"
|
||||||
"OneAuth/models"
|
"OneAuth/models"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
//"OneAuth/ws"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/veypi/OneBD"
|
"github.com/veypi/OneBD"
|
||||||
@ -41,13 +43,10 @@ type handler struct {
|
|||||||
|
|
||||||
// Get get user data
|
// Get get user data
|
||||||
func (h *handler) Get() (interface{}, error) {
|
func (h *handler) Get() (interface{}, error) {
|
||||||
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)
|
||||||
err := cfg.DB().Where("username LIKE ? OR nickname LIKE ?", "%"+username+"%", "%"+username+"%").Find(&users).Error
|
err := cfg.DB().Preload("Scores").Preload("Roles.Auths").Where("username LIKE ? OR nickname LIKE ?", "%"+username+"%", "%"+username+"%").Find(&users).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -57,14 +56,14 @@ func (h *handler) Get() (interface{}, error) {
|
|||||||
if userID != 0 {
|
if userID != 0 {
|
||||||
user := &models.User{}
|
user := &models.User{}
|
||||||
user.ID = uint(userID)
|
user.ID = uint(userID)
|
||||||
return user, cfg.DB().Where(user).First(user).Error
|
return user, cfg.DB().Where(user).Preload("Scores").Preload("Roles.Auths").Preload("Favorites").First(user).Error
|
||||||
} else {
|
} else {
|
||||||
users := make([]models.User, 10)
|
users := make([]models.User, 10)
|
||||||
skip, err := strconv.Atoi(h.Meta().Query("skip"))
|
skip, err := strconv.Atoi(h.Meta().Query("skip"))
|
||||||
if err != nil || skip < 0 {
|
if err != nil || skip < 0 {
|
||||||
skip = 0
|
skip = 0
|
||||||
}
|
}
|
||||||
if err := cfg.DB().Offset(skip).Find(&users).Error; err != nil {
|
if err := cfg.DB().Preload("Scores").Preload("Roles.Auths").Offset(skip).Find(&users).Error; err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return users, nil
|
return users, nil
|
||||||
@ -74,15 +73,16 @@ func (h *handler) Get() (interface{}, error) {
|
|||||||
// Post register user
|
// Post register user
|
||||||
func (h *handler) Post() (interface{}, error) {
|
func (h *handler) Post() (interface{}, error) {
|
||||||
self := &models.App{}
|
self := &models.App{}
|
||||||
self.UUID = cfg.CFG.APPUUID
|
self.ID = cfg.CFG.APPID
|
||||||
err := cfg.DB().Where(self).First(self).Error
|
err := cfg.DB().Where(self).First(self).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, oerr.DBErr.Attach(err)
|
return nil, oerr.DBErr.Attach(err)
|
||||||
}
|
}
|
||||||
if !self.EnableRegister && !h.Payload.GetAuth(auth.User, "").CanCreate() {
|
if !self.EnableRegister {
|
||||||
return nil, oerr.NoAuth.AttachStr("register disabled")
|
return nil, oerr.NoAuth.AttachStr("register disabled")
|
||||||
}
|
}
|
||||||
var userdata = struct {
|
var userdata = struct {
|
||||||
|
UUID string `json:"uuid"`
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
Nickname string `json:"nickname"`
|
Nickname string `json:"nickname"`
|
||||||
@ -120,10 +120,27 @@ func (h *handler) Post() (interface{}, error) {
|
|||||||
if err := tx.Create(&h.User).Error; err != nil {
|
if err := tx.Create(&h.User).Error; err != nil {
|
||||||
return oerr.ResourceDuplicated
|
return oerr.ResourceDuplicated
|
||||||
}
|
}
|
||||||
err := app.AddUser(tx, self.ID, h.User.ID, self.InitRoleID, models.AUOK)
|
err := app.AddUser(tx, self.ID, h.User.ID, self.InitRoleID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if userdata.UUID != self.UUID && userdata.UUID != "" {
|
||||||
|
target := &models.App{}
|
||||||
|
target.UUID = userdata.UUID
|
||||||
|
err = tx.Where(target).First(target).Error
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if target.EnableRegister {
|
||||||
|
err := app.AddUser(tx, target.ID, h.User.ID, target.InitRoleID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -152,10 +169,11 @@ func (h *handler) Patch() (interface{}, error) {
|
|||||||
} else {
|
} else {
|
||||||
target.ID = uint(tempID)
|
target.ID = uint(tempID)
|
||||||
}
|
}
|
||||||
|
tx := cfg.DB().Begin()
|
||||||
if err := cfg.DB().Where(&target).First(&target).Error; err != nil {
|
if err := cfg.DB().Where(&target).First(&target).Error; err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if target.ID != h.Payload.ID && h.Payload.GetAuth(auth.User, strconv.Itoa(int(target.ID))).CanUpdate() {
|
if target.ID != h.Payload.ID {
|
||||||
return nil, oerr.NoAuth
|
return nil, oerr.NoAuth
|
||||||
}
|
}
|
||||||
if len(opts.Password) >= 6 {
|
if len(opts.Password) >= 6 {
|
||||||
@ -179,9 +197,11 @@ func (h *handler) Patch() (interface{}, error) {
|
|||||||
if opts.Status != "" {
|
if opts.Status != "" {
|
||||||
target.Status = opts.Status
|
target.Status = opts.Status
|
||||||
}
|
}
|
||||||
if err := cfg.DB().Updates(&target).Error; err != nil {
|
if err := tx.Updates(&target).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
tx.Commit()
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,6 +222,10 @@ func (h *handler) Head() (interface{}, error) {
|
|||||||
if len(uid) == 0 || len(password) == 0 {
|
if len(uid) == 0 || len(password) == 0 {
|
||||||
return nil, oerr.ApiArgsError
|
return nil, oerr.ApiArgsError
|
||||||
}
|
}
|
||||||
|
appUUID := h.Meta().Query("uuid")
|
||||||
|
if appUUID == "" {
|
||||||
|
return nil, oerr.ApiArgsMissing.AttachStr("uuid")
|
||||||
|
}
|
||||||
h.User = new(models.User)
|
h.User = new(models.User)
|
||||||
uidType := h.Meta().Query("uid_type")
|
uidType := h.Meta().Query("uid_type")
|
||||||
switch uidType {
|
switch uidType {
|
||||||
@ -215,7 +239,7 @@ func (h *handler) Head() (interface{}, error) {
|
|||||||
h.User.Username = uid
|
h.User.Username = uid
|
||||||
}
|
}
|
||||||
target := &models.App{}
|
target := &models.App{}
|
||||||
target.UUID = cfg.CFG.APPUUID
|
target.UUID = appUUID
|
||||||
err = cfg.DB().Where(target).Find(target).Error
|
err = cfg.DB().Where(target).Find(target).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, oerr.DBErr.Attach(err)
|
return nil, oerr.DBErr.Attach(err)
|
||||||
@ -237,12 +261,17 @@ func (h *handler) Head() (interface{}, error) {
|
|||||||
au.AppID = target.ID
|
au.AppID = target.ID
|
||||||
err = cfg.DB().Where(au).First(au).Error
|
err = cfg.DB().Where(au).First(au).Error
|
||||||
appID := target.ID
|
appID := target.ID
|
||||||
|
h.Meta().SetHeader("content", target.UserRefreshUrl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
} else if au.Status != models.AUOK {
|
return nil, err
|
||||||
|
}
|
||||||
|
appID = cfg.CFG.APPID
|
||||||
|
h.Meta().SetHeader("content", "/app/"+target.UUID)
|
||||||
|
} else if au.Disabled {
|
||||||
return nil, oerr.DisableLogin
|
return nil, oerr.DisableLogin
|
||||||
}
|
}
|
||||||
tokenStr, err := token.GetToken(h.User, appID, cfg.CFG.APPKey)
|
tokenStr, err := token.GetToken(h.User, appID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.HandlerErrs(err)
|
log.HandlerErrs(err)
|
||||||
return nil, oerr.Unknown.Attach(err)
|
return nil, oerr.Unknown.Attach(err)
|
||||||
|
|||||||
@ -15,11 +15,11 @@ var CFG = &struct {
|
|||||||
Host string
|
Host string
|
||||||
LoggerPath string
|
LoggerPath string
|
||||||
LoggerLevel string
|
LoggerLevel string
|
||||||
APPUUID string
|
APPID uint
|
||||||
APPKey string
|
APPKey string
|
||||||
TimeFormat string
|
TimeFormat string
|
||||||
Debug bool
|
Debug bool
|
||||||
MediaDir string
|
EXEDir string
|
||||||
DB struct {
|
DB struct {
|
||||||
Type string
|
Type string
|
||||||
Addr string
|
Addr string
|
||||||
@ -28,15 +28,14 @@ var CFG = &struct {
|
|||||||
DB string
|
DB string
|
||||||
}
|
}
|
||||||
}{
|
}{
|
||||||
APPUUID: "jU5Jo5hM",
|
APPID: 1,
|
||||||
APPKey: "cB43wF94MLTksyBK",
|
|
||||||
AdminUser: "admin",
|
AdminUser: "admin",
|
||||||
Host: "0.0.0.0:4001",
|
Host: "0.0.0.0:4001",
|
||||||
LoggerPath: "",
|
LoggerPath: "",
|
||||||
LoggerLevel: "debug",
|
LoggerLevel: "debug",
|
||||||
TimeFormat: "2006/01/02 15:04:05",
|
TimeFormat: "2006/01/02 15:04:05",
|
||||||
Debug: true,
|
Debug: true,
|
||||||
MediaDir: "/Users/light/test/media/",
|
EXEDir: "./",
|
||||||
DB: struct {
|
DB: struct {
|
||||||
Type string
|
Type string
|
||||||
Addr string
|
Addr string
|
||||||
|
|||||||
3
go.mod
3
go.mod
@ -6,10 +6,9 @@ require (
|
|||||||
github.com/json-iterator/go v1.1.10
|
github.com/json-iterator/go v1.1.10
|
||||||
github.com/urfave/cli/v2 v2.2.0
|
github.com/urfave/cli/v2 v2.2.0
|
||||||
github.com/veypi/OneBD v0.4.1
|
github.com/veypi/OneBD v0.4.1
|
||||||
github.com/veypi/utils v0.3.1
|
github.com/veypi/utils v0.2.2
|
||||||
gorm.io/driver/mysql v1.0.5
|
gorm.io/driver/mysql v1.0.5
|
||||||
gorm.io/driver/sqlite v1.1.4
|
gorm.io/driver/sqlite v1.1.4
|
||||||
gorm.io/gorm v1.21.3
|
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
|
||||||
|
|||||||
8
go.sum
8
go.sum
@ -43,11 +43,11 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0
|
|||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/urfave/cli/v2 v2.2.0 h1:JTTnM6wKzdA0Jqodd966MVj4vWbbquZykeX1sKbe2C4=
|
github.com/urfave/cli/v2 v2.2.0 h1:JTTnM6wKzdA0Jqodd966MVj4vWbbquZykeX1sKbe2C4=
|
||||||
github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
|
github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
|
||||||
|
github.com/veypi/OneBD v0.4.1 h1:IkuV2tqay3fyX+FsM9XrKiKS9N7pymUJnrscGx2T0mc=
|
||||||
|
github.com/veypi/OneBD v0.4.1/go.mod h1:9IoMOBzwIGyv6IZGF7ZnTYwTcHltLKicDgcwha66G0U=
|
||||||
|
github.com/veypi/utils v0.1.5/go.mod h1:oKcwTDfvE1qtuhJuCcDcfvGquv9bHdFaCGA42onVMC4=
|
||||||
|
github.com/veypi/utils v0.2.2 h1:BRxu0mYJJpuubPjmIIrRVr0XEq9NMp//KUCrVTkFums=
|
||||||
github.com/veypi/utils v0.2.2/go.mod h1:rAkC6Fbk5cBa3u+8pyCpsVcnXw74EhEQJGmPND9FvRg=
|
github.com/veypi/utils v0.2.2/go.mod h1:rAkC6Fbk5cBa3u+8pyCpsVcnXw74EhEQJGmPND9FvRg=
|
||||||
github.com/veypi/utils v0.3.0 h1:vCi0jqMsAMBPblFCmneUw3Wet5y1XHZLA5ZP9c/2owI=
|
|
||||||
github.com/veypi/utils v0.3.0/go.mod h1:rAkC6Fbk5cBa3u+8pyCpsVcnXw74EhEQJGmPND9FvRg=
|
|
||||||
github.com/veypi/utils v0.3.1 h1:QL4Q/+iXNFXNVENiUeEttSwNwkeqrorSpTBpCs7fXBI=
|
|
||||||
github.com/veypi/utils v0.3.1/go.mod h1:rAkC6Fbk5cBa3u+8pyCpsVcnXw74EhEQJGmPND9FvRg=
|
|
||||||
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
|||||||
@ -8,10 +8,7 @@ import (
|
|||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
func AddUser(tx *gorm.DB, appID uint, userID uint, roleID uint, status models.AUStatus) error {
|
func AddUser(tx *gorm.DB, appID uint, userID uint, roleID uint) error {
|
||||||
if appID == 0 || userID == 0 {
|
|
||||||
return oerr.FuncArgsError
|
|
||||||
}
|
|
||||||
au := &models.AppUser{}
|
au := &models.AppUser{}
|
||||||
au.AppID = appID
|
au.AppID = appID
|
||||||
au.UserID = userID
|
au.UserID = userID
|
||||||
@ -20,25 +17,19 @@ func AddUser(tx *gorm.DB, appID uint, userID uint, roleID uint, status models.AU
|
|||||||
return oerr.ResourceDuplicated
|
return oerr.ResourceDuplicated
|
||||||
}
|
}
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
au.Status = status
|
|
||||||
err = tx.Create(au).Error
|
err = tx.Create(au).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if roleID > 0 {
|
err = auth.BindUserRole(tx, userID, roleID)
|
||||||
err = auth.BindUserRole(tx, userID, roleID)
|
if err != nil {
|
||||||
if err != nil {
|
return err
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return tx.Model(&models.App{}).Where("id = ?", appID).Update("user_count", gorm.Expr("user_count + ?", 1)).Error
|
return tx.Model(&models.App{}).Where("id = ?", appID).Update("user_count", gorm.Expr("user_count + ?", 1)).Error
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
func EnableUser(tx *gorm.DB, appID uint, userID uint) error {
|
func EnableUser(tx *gorm.DB, appID uint, userID uint) error {
|
||||||
if appID == 0 || userID == 0 {
|
|
||||||
return oerr.FuncArgsError
|
|
||||||
}
|
|
||||||
au := &models.AppUser{}
|
au := &models.AppUser{}
|
||||||
au.AppID = appID
|
au.AppID = appID
|
||||||
au.UserID = userID
|
au.UserID = userID
|
||||||
@ -46,18 +37,12 @@ func EnableUser(tx *gorm.DB, appID uint, userID uint) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if au.Status != models.AUOK {
|
return tx.Where(au).Update("disabled", false).Error
|
||||||
return tx.Where(au).Update("status", models.AUOK).Error
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func DisableUser(tx *gorm.DB, appID uint, userID uint) error {
|
func DisableUser(tx *gorm.DB, appID uint, userID uint) error {
|
||||||
if appID == 0 || userID == 0 {
|
|
||||||
return oerr.FuncArgsError
|
|
||||||
}
|
|
||||||
au := &models.AppUser{}
|
au := &models.AppUser{}
|
||||||
au.AppID = appID
|
au.AppID = appID
|
||||||
au.UserID = userID
|
au.UserID = userID
|
||||||
return tx.Where(au).Update("status", models.AUDisable).Error
|
return tx.Where(au).Update("disabled", true).Error
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,12 +3,10 @@ package base
|
|||||||
import (
|
import (
|
||||||
"OneAuth/libs/oerr"
|
"OneAuth/libs/oerr"
|
||||||
"OneAuth/libs/tools"
|
"OneAuth/libs/tools"
|
||||||
"errors"
|
|
||||||
"github.com/json-iterator/go"
|
"github.com/json-iterator/go"
|
||||||
"github.com/veypi/OneBD"
|
"github.com/veypi/OneBD"
|
||||||
"github.com/veypi/OneBD/rfc"
|
"github.com/veypi/OneBD/rfc"
|
||||||
"github.com/veypi/utils/log"
|
"github.com/veypi/utils/log"
|
||||||
"gorm.io/gorm"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@ -16,41 +14,6 @@ import (
|
|||||||
|
|
||||||
var json = jsoniter.ConfigFastest
|
var json = jsoniter.ConfigFastest
|
||||||
|
|
||||||
func JSONResponse(m OneBD.Meta, data interface{}, err error) {
|
|
||||||
if err != nil {
|
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
||||||
err = oerr.ResourceNotExist
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if m.Method() == rfc.MethodHead {
|
|
||||||
if err != nil {
|
|
||||||
m.SetHeader("status", "0")
|
|
||||||
m.SetHeader("code", strconv.Itoa(int(oerr.OfType(err.Error()))))
|
|
||||||
m.SetHeader("err", err.Error())
|
|
||||||
} else {
|
|
||||||
m.SetHeader("status", "1")
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
res := map[string]interface{}{
|
|
||||||
"status": 1,
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
res["status"] = 0
|
|
||||||
res["code"] = oerr.OfType(err.Error())
|
|
||||||
res["err"] = err.Error()
|
|
||||||
} else {
|
|
||||||
res["status"] = 1
|
|
||||||
res["content"] = data
|
|
||||||
}
|
|
||||||
p, err := json.Marshal(res)
|
|
||||||
if err != nil {
|
|
||||||
log.Warn().Err(err).Msg("encode json data error")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, _ = m.Write(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
type ApiHandler struct {
|
type ApiHandler struct {
|
||||||
OneBD.BaseHandler
|
OneBD.BaseHandler
|
||||||
UserHandler
|
UserHandler
|
||||||
@ -61,12 +24,29 @@ func (h *ApiHandler) Init(m OneBD.Meta) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *ApiHandler) OnResponse(data interface{}) {
|
func (h *ApiHandler) OnResponse(data interface{}) {
|
||||||
JSONResponse(h.Meta(), data, nil)
|
if h.Meta().Method() == rfc.MethodHead {
|
||||||
|
h.Meta().SetHeader("status", "1")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p, err := json.Marshal(map[string]interface{}{"status": 1, "content": data})
|
||||||
|
if err != nil {
|
||||||
|
log.Warn().Err(err).Msg("encode json data error")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
h.Meta().Write(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *ApiHandler) OnError(err error) {
|
func (h *ApiHandler) OnError(err error) {
|
||||||
log.WithNoCaller.Warn().Err(err).Msg(h.Meta().RequestPath())
|
log.WithNoCaller.Warn().Err(err).Msg(h.Meta().RequestPath())
|
||||||
JSONResponse(h.Meta(), nil, err)
|
msg := err.Error()
|
||||||
|
if h.Meta().Method() == rfc.MethodHead {
|
||||||
|
h.Meta().SetHeader("status", "0")
|
||||||
|
h.Meta().SetHeader("code", strconv.Itoa(int(oerr.OfType(msg))))
|
||||||
|
h.Meta().SetHeader("err", msg)
|
||||||
|
} else {
|
||||||
|
p, _ := json.Marshal(map[string]interface{}{"status": 0, "code": oerr.OfType(msg), "err": msg})
|
||||||
|
h.Meta().Write(p)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var ioNumLimit = make(map[string]time.Time)
|
var ioNumLimit = make(map[string]time.Time)
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
package base
|
package base
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"OneAuth/cfg"
|
|
||||||
"OneAuth/libs/oerr"
|
"OneAuth/libs/oerr"
|
||||||
"OneAuth/libs/token"
|
"OneAuth/libs/token"
|
||||||
"OneAuth/models"
|
"OneAuth/models"
|
||||||
@ -27,7 +26,7 @@ func (a *UserHandler) ParsePayload(m OneBD.Meta) error {
|
|||||||
if tokenStr == "" {
|
if tokenStr == "" {
|
||||||
return oerr.NotLogin
|
return oerr.NotLogin
|
||||||
}
|
}
|
||||||
ok, err := token.ParseToken(tokenStr, a.Payload, cfg.CFG.APPKey)
|
ok, err := token.ParseToken(tokenStr, a.Payload)
|
||||||
if ok {
|
if ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package oerr
|
package oerr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
@ -41,13 +42,11 @@ type Code uint
|
|||||||
- 9 : 本不可能发生的错误,例如被人攻击导致数据异常产生的逻辑错误
|
- 9 : 本不可能发生的错误,例如被人攻击导致数据异常产生的逻辑错误
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Unknown error
|
|
||||||
const (
|
const (
|
||||||
Unknown Code = 0
|
Unknown Code = 0
|
||||||
)
|
)
|
||||||
const (
|
const (
|
||||||
// DBErr 2 数据库错误
|
// 2 数据库错误
|
||||||
// -1 系统错误
|
// -1 系统错误
|
||||||
// -2 数据读写错误
|
// -2 数据读写错误
|
||||||
DBErr Code = 20001
|
DBErr Code = 20001
|
||||||
@ -57,13 +56,10 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// LogicErr 3 系统内逻辑错误
|
// 3
|
||||||
LogicErr Code = 30000
|
|
||||||
AppNotJoin Code = 30001
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// NotLogin
|
|
||||||
// 4 权限类型错误
|
// 4 权限类型错误
|
||||||
// 1: 登录权限
|
// 1: 登录权限
|
||||||
// 2: 资源操作权限
|
// 2: 资源操作权限
|
||||||
@ -134,8 +130,6 @@ var codeMap = map[Code]string{
|
|||||||
NoAuth: "no auth to access",
|
NoAuth: "no auth to access",
|
||||||
AccessErr: "access error",
|
AccessErr: "access error",
|
||||||
AccessTooFast: "access too fast",
|
AccessTooFast: "access too fast",
|
||||||
LogicErr: "logic error",
|
|
||||||
AppNotJoin: "not join in app",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Code) Error() string {
|
func (c Code) Error() string {
|
||||||
@ -150,7 +144,7 @@ func (c Code) String() string {
|
|||||||
return codeMap[Unknown]
|
return codeMap[Unknown]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attach 附加错误详细原因
|
// 附加错误详细原因
|
||||||
func (c Code) Attach(errs ...error) (e error) {
|
func (c Code) Attach(errs ...error) (e error) {
|
||||||
e = c
|
e = c
|
||||||
for _, err := range errs {
|
for _, err := range errs {
|
||||||
@ -201,3 +195,16 @@ func (w *wrapErr) Error() string {
|
|||||||
func (w *wrapErr) UnWrap() error {
|
func (w *wrapErr) UnWrap() error {
|
||||||
return w.err
|
return w.err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CheckMultiErr(errs ...error) error {
|
||||||
|
msg := ""
|
||||||
|
for _, e := range errs {
|
||||||
|
if e != nil {
|
||||||
|
msg += e.Error() + "\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if msg != "" {
|
||||||
|
return errors.New(msg)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@ -1,8 +1,20 @@
|
|||||||
package token
|
package token
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"OneAuth/libs/key"
|
||||||
"OneAuth/models"
|
"OneAuth/models"
|
||||||
"github.com/veypi/utils/jwt"
|
"crypto/hmac"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
InvalidToken = errors.New("invalid token")
|
||||||
|
ExpiredToken = errors.New("expired token")
|
||||||
)
|
)
|
||||||
|
|
||||||
type simpleAuth struct {
|
type simpleAuth struct {
|
||||||
@ -14,9 +26,11 @@ type simpleAuth struct {
|
|||||||
|
|
||||||
// TODO:: roles 是否会造成token过大 ?
|
// TODO:: roles 是否会造成token过大 ?
|
||||||
type PayLoad struct {
|
type PayLoad struct {
|
||||||
jwt.Payload
|
ID uint `json:"id"`
|
||||||
ID uint `json:"id"`
|
AppID uint `json:"app_id"`
|
||||||
Auth map[uint]*simpleAuth `json:"auth"`
|
Iat int64 `json:"iat"` //token time
|
||||||
|
Exp int64 `json:"exp"`
|
||||||
|
Auth map[uint]*simpleAuth `json:"auth"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAuth resource_uuid 缺省或仅第一个有效 权限会被更高权限覆盖
|
// GetAuth resource_uuid 缺省或仅第一个有效 权限会被更高权限覆盖
|
||||||
@ -47,10 +61,19 @@ func (p *PayLoad) GetAuth(ResourceID string, ResourceUUID ...string) models.Auth
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetToken(u *models.User, appID uint, key string) (string, error) {
|
func GetToken(u *models.User, appID uint) (string, error) {
|
||||||
payload := &PayLoad{
|
header := map[string]string{
|
||||||
ID: u.ID,
|
"typ": "JWT",
|
||||||
Auth: map[uint]*simpleAuth{},
|
"alg": "HS256",
|
||||||
|
}
|
||||||
|
//header := "{\"typ\": \"JWT\", \"alg\": \"HS256\"}"
|
||||||
|
now := time.Now().Unix()
|
||||||
|
payload := PayLoad{
|
||||||
|
ID: u.ID,
|
||||||
|
AppID: appID,
|
||||||
|
Iat: now,
|
||||||
|
Exp: now + 60*60*24,
|
||||||
|
Auth: map[uint]*simpleAuth{},
|
||||||
}
|
}
|
||||||
for _, a := range u.GetAuths() {
|
for _, a := range u.GetAuths() {
|
||||||
if appID == a.AppID {
|
if appID == a.AppID {
|
||||||
@ -61,9 +84,44 @@ func GetToken(u *models.User, appID uint, key string) (string, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return jwt.GetToken(payload, []byte(key))
|
a, err := json.Marshal(header)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
b, err := json.Marshal(payload)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
A := base64.StdEncoding.EncodeToString(a)
|
||||||
|
B := base64.StdEncoding.EncodeToString(b)
|
||||||
|
hmacCipher := hmac.New(sha256.New, []byte(key.User(payload.ID, payload.AppID)))
|
||||||
|
hmacCipher.Write([]byte(A + "." + B))
|
||||||
|
C := hmacCipher.Sum(nil)
|
||||||
|
return A + "." + B + "." + base64.StdEncoding.EncodeToString(C), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseToken(token string, payload *PayLoad, key string) (bool, error) {
|
func ParseToken(token string, payload *PayLoad) (bool, error) {
|
||||||
return jwt.ParseToken(token, payload, []byte(key))
|
var A, B, C string
|
||||||
|
if seqs := strings.Split(token, "."); len(seqs) == 3 {
|
||||||
|
A, B, C = seqs[0], seqs[1], seqs[2]
|
||||||
|
} else {
|
||||||
|
return false, InvalidToken
|
||||||
|
}
|
||||||
|
tempPayload, err := base64.StdEncoding.DecodeString(B)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(tempPayload, payload); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
hmacCipher := hmac.New(sha256.New, []byte(key.User(payload.ID, payload.AppID)))
|
||||||
|
hmacCipher.Write([]byte(A + "." + B))
|
||||||
|
tempC := hmacCipher.Sum(nil)
|
||||||
|
if !hmac.Equal([]byte(C), []byte(base64.StdEncoding.EncodeToString(tempC))) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
if time.Now().Unix() > payload.Exp {
|
||||||
|
return false, ExpiredToken
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|||||||
21
main.go
21
main.go
@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/veypi/utils/cmd"
|
"github.com/veypi/utils/cmd"
|
||||||
"github.com/veypi/utils/log"
|
"github.com/veypi/utils/log"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
|
||||||
const Version = "v0.1.0"
|
const Version = "v0.1.0"
|
||||||
@ -34,6 +35,21 @@ func main() {
|
|||||||
Value: cfg.CFG.LoggerPath,
|
Value: cfg.CFG.LoggerPath,
|
||||||
Destination: &cfg.CFG.LoggerPath,
|
Destination: &cfg.CFG.LoggerPath,
|
||||||
},
|
},
|
||||||
|
&cli.UintFlag{
|
||||||
|
Name: "id",
|
||||||
|
Value: cfg.CFG.APPID,
|
||||||
|
Destination: &cfg.CFG.APPID,
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "key",
|
||||||
|
Value: cfg.CFG.APPKey,
|
||||||
|
Destination: &cfg.CFG.APPKey,
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "exe_dir",
|
||||||
|
Value: cfg.CFG.EXEDir,
|
||||||
|
Destination: &cfg.CFG.EXEDir,
|
||||||
|
},
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "host",
|
Name: "host",
|
||||||
Value: cfg.CFG.Host,
|
Value: cfg.CFG.Host,
|
||||||
@ -56,6 +72,11 @@ func main() {
|
|||||||
srv.SetStopFunc(func() {
|
srv.SetStopFunc(func() {
|
||||||
})
|
})
|
||||||
app.Before = func(c *cli.Context) error {
|
app.Before = func(c *cli.Context) error {
|
||||||
|
var err error
|
||||||
|
cfg.CFG.EXEDir, err = filepath.Abs(cfg.CFG.EXEDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if cfg.CFG.Debug {
|
if cfg.CFG.Debug {
|
||||||
cfg.CFG.LoggerLevel = "debug"
|
cfg.CFG.LoggerLevel = "debug"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -29,35 +29,22 @@ type App struct {
|
|||||||
UserRefreshUrl string `json:"user_refresh_url"`
|
UserRefreshUrl string `json:"user_refresh_url"`
|
||||||
// app 校验用户token时使用
|
// app 校验用户token时使用
|
||||||
Key string `json:"-"`
|
Key string `json:"-"`
|
||||||
// 是否允许用户自动加入应用
|
// 是否允许用户自主注册
|
||||||
EnableRegister bool `json:"enable_register"`
|
EnableRegister bool `json:"enable_register"`
|
||||||
//
|
EnableUserKey bool `json:"enable_user_key"`
|
||||||
EnableUserKey bool `json:"enable_user_key"`
|
EnableUser bool `json:"enable_user"`
|
||||||
UserKeyUrl string `json:"user_key_url"`
|
EnableWx bool `json:"enable_wx"`
|
||||||
// 允许登录方式
|
EnablePhone bool `json:"enable_phone"`
|
||||||
EnableUser bool `json:"enable_user"`
|
EnableEmail bool `json:"enable_email"`
|
||||||
EnableWx bool `json:"enable_wx"`
|
Wx *Wechat `json:"wx" gorm:"foreignkey:AppID;references:ID"`
|
||||||
EnablePhone bool `json:"enable_phone"`
|
|
||||||
EnableEmail bool `json:"enable_email"`
|
|
||||||
Wx *Wechat `json:"wx" gorm:"foreignkey:AppID;references:ID"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type AUStatus string
|
|
||||||
|
|
||||||
const (
|
|
||||||
AUOK AUStatus = "ok"
|
|
||||||
AUDisable AUStatus = "disabled"
|
|
||||||
AUApply AUStatus = "apply"
|
|
||||||
AUDeny AUStatus = "deny"
|
|
||||||
)
|
|
||||||
|
|
||||||
type AppUser struct {
|
type AppUser struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
AppID uint `json:"app_id"`
|
AppID uint `json:"app_id"`
|
||||||
APP *App `json:"app"`
|
UserID uint `json:"user_id"`
|
||||||
UserID uint `json:"user_id"`
|
Disabled bool `json:"disabled"`
|
||||||
User *User `json:"user"`
|
Status string `json:"status"`
|
||||||
Status AUStatus `json:"status"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Wechat struct {
|
type Wechat struct {
|
||||||
|
|||||||
@ -1,11 +0,0 @@
|
|||||||
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"`
|
|
||||||
}
|
|
||||||
3
oaf/.browserslistrc
Normal file
3
oaf/.browserslistrc
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
> 1%
|
||||||
|
last 2 versions
|
||||||
|
not dead
|
||||||
5
oaf/.editorconfig
Normal file
5
oaf/.editorconfig
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
[*.{js,jsx,ts,tsx,vue}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
1
oaf/.eslintignore
Normal file
1
oaf/.eslintignore
Normal file
@ -0,0 +1 @@
|
|||||||
|
src/libs/wwLogin.js
|
||||||
22
oaf/.eslintrc.js
Normal file
22
oaf/.eslintrc.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
env: {
|
||||||
|
node: true
|
||||||
|
},
|
||||||
|
extends: [
|
||||||
|
'plugin:vue/essential',
|
||||||
|
'@vue/standard',
|
||||||
|
'@vue/typescript/recommended'
|
||||||
|
],
|
||||||
|
parserOptions: {
|
||||||
|
ecmaVersion: 2020
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
'object-curly-spacing': 0,
|
||||||
|
'space-before-function-paren': 0,
|
||||||
|
'@typescript-eslint/camelcase': 0,
|
||||||
|
'@typescript-eslint/no-empty-function': 0,
|
||||||
|
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||||
|
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
|
||||||
|
}
|
||||||
|
}
|
||||||
26
oaf/.gitignore
vendored
26
oaf/.gitignore
vendored
@ -1,5 +1,23 @@
|
|||||||
node_modules
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
dist
|
node_modules
|
||||||
dist-ssr
|
/dist
|
||||||
*.local
|
|
||||||
|
|
||||||
|
# local env files
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
|
||||||
|
# Log files
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
|
|||||||
3
oaf/.vscode/extensions.json
vendored
3
oaf/.vscode/extensions.json
vendored
@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"recommendations": ["johnsoncodehk.volar"]
|
|
||||||
}
|
|
||||||
@ -1,11 +1,24 @@
|
|||||||
# Vue 3 + Typescript + Vite
|
# oaf
|
||||||
|
|
||||||
This template should help get you started developing with Vue 3 and Typescript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
|
## Project setup
|
||||||
|
```
|
||||||
|
yarn install
|
||||||
|
```
|
||||||
|
|
||||||
## Recommended IDE Setup
|
### Compiles and hot-reloads for development
|
||||||
|
```
|
||||||
|
yarn serve
|
||||||
|
```
|
||||||
|
|
||||||
- [VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.volar)
|
### Compiles and minifies for production
|
||||||
|
```
|
||||||
|
yarn build
|
||||||
|
```
|
||||||
|
|
||||||
## Type Support For `.vue` Imports in TS
|
### Lints and fixes files
|
||||||
|
```
|
||||||
|
yarn lint
|
||||||
|
```
|
||||||
|
|
||||||
Since TypeScript cannot handle type information for `.vue` imports, they are shimmed to be a generic Vue component type by default. In most cases this is fine if you don't really care about component prop types outside of templates. However, if you wish to get actual prop types in `.vue` imports (for example to get props validation when using manual `h(...)` calls), you can enable Volar's `.vue` type support plugin by running `Volar: Switch TS Plugin on/off` from VSCode command palette.
|
### Customize configuration
|
||||||
|
See [Configuration Reference](https://cli.vuejs.org/config/).
|
||||||
|
|||||||
5
oaf/babel.config.js
Normal file
5
oaf/babel.config.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
module.exports = {
|
||||||
|
presets: [
|
||||||
|
'@vue/cli-plugin-babel/preset'
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -1,13 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<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>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="app"></div>
|
|
||||||
<script type="module" src="/src/main.ts"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
2793
oaf/package-lock.json
generated
2793
oaf/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,31 +1,49 @@
|
|||||||
{
|
{
|
||||||
"name": "oaf",
|
"name": "oaf",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"serve": "vue-cli-service serve",
|
||||||
"build": "vue-tsc --noEmit && vite build",
|
"build": "vue-cli-service build",
|
||||||
"serve": "vite preview"
|
"lint": "vue-cli-service lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@veypi/one-icon": "2.0.5",
|
"@veypi/one-icon": "^1.0.1",
|
||||||
"animate.css": "^4.1.1",
|
"axios": "^0.21.1",
|
||||||
"axios": "^0.24.0",
|
"core-js": "^3.6.5",
|
||||||
"js-base64": "^3.7.2",
|
"js-base64": "^3.6.0",
|
||||||
"vue": "^3.2.16",
|
"vue": "^2.6.11",
|
||||||
"vue-router": "^4.0.12",
|
"vue-class-component": "^7.2.3",
|
||||||
"vuex": "^4.0.2"
|
"vue-m-message": "^3.1.0",
|
||||||
|
"vue-property-decorator": "^9.1.2",
|
||||||
|
"vue-router": "^3.2.0",
|
||||||
|
"vuetify": "^2.4.0",
|
||||||
|
"vuex": "^3.4.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/postcss7-compat": "^2.1.0",
|
"@typescript-eslint/eslint-plugin": "^2.33.0",
|
||||||
"@vitejs/plugin-vue": "^1.9.3",
|
"@typescript-eslint/parser": "^2.33.0",
|
||||||
"autoprefixer": "^9.8.8",
|
"@vue/cli-plugin-babel": "~4.5.0",
|
||||||
"less": "^4.1.2",
|
"@vue/cli-plugin-eslint": "~4.5.0",
|
||||||
"naive-ui": "^2.19.11",
|
"@vue/cli-plugin-router": "~4.5.0",
|
||||||
"postcss": "^7.0.39",
|
"@vue/cli-plugin-typescript": "~4.5.0",
|
||||||
"tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.2.17",
|
"@vue/cli-plugin-vuex": "~4.5.0",
|
||||||
"typescript": "^4.4.3",
|
"@vue/cli-service": "~4.5.0",
|
||||||
"vfonts": "^0.1.0",
|
"@vue/eslint-config-standard": "^5.1.2",
|
||||||
"vite": "^2.6.4",
|
"@vue/eslint-config-typescript": "^5.0.2",
|
||||||
"vue-tsc": "^0.3.0"
|
"eslint": "^6.7.2",
|
||||||
|
"eslint-plugin-import": "^2.20.2",
|
||||||
|
"eslint-plugin-node": "^11.1.0",
|
||||||
|
"eslint-plugin-promise": "^4.2.1",
|
||||||
|
"eslint-plugin-standard": "^4.0.0",
|
||||||
|
"eslint-plugin-vue": "^6.2.2",
|
||||||
|
"less": "^3.0.4",
|
||||||
|
"less-loader": "^5.0.0",
|
||||||
|
"sass": "^1.32.0",
|
||||||
|
"sass-loader": "^10.0.0",
|
||||||
|
"typescript": "~3.9.3",
|
||||||
|
"vue-cli-plugin-vuetify": "^2.2.2",
|
||||||
|
"vue-template-compiler": "^2.6.11",
|
||||||
|
"vuetify-loader": "^1.7.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
plugins: {
|
|
||||||
tailwindcss: {},
|
|
||||||
autoprefixer: {},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
20
oaf/public/index.html
Normal file
20
oaf/public/index.html
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||||
|
<!-- <link rel="icon" href="<%= BASE_URL %>favicon.ico">-->
|
||||||
|
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mdi/font@latest/css/materialdesignicons.min.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled.
|
||||||
|
Please enable it to continue.</strong>
|
||||||
|
</noscript>
|
||||||
|
<div id="app"></div>
|
||||||
|
<!-- built files will be auto injected -->
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
164
oaf/src/App.vue
164
oaf/src/App.vue
@ -1,137 +1,39 @@
|
|||||||
<template>
|
<template>
|
||||||
<n-config-provider :theme-overrides="Theme.overrides" :locale="zhCN" :date-locale="dateZhCN"
|
<v-app>
|
||||||
:theme="Theme">
|
<v-app-bar
|
||||||
<n-layout class="font-sans select-none">
|
app
|
||||||
<n-layout>
|
color="primary"
|
||||||
<n-layout-header class="pr-5" bordered style="height: 64px;line-height: 64px;">
|
dark
|
||||||
<div class="inline-block float-left h-full">
|
>
|
||||||
<one-icon color="#000" class="inline-block" @click="$router.push('/')" style="font-size: 48px;margin:8px;color:aqua">
|
<div class="d-flex align-center">
|
||||||
glassdoor
|
<one-icon style="color: aqua;font-size: 56px">glassdoor</one-icon>
|
||||||
</one-icon>
|
<span class="font-italic font-weight-bold" style="font-size: 20px">统一认证</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="inline-block float-left h-full" style="margin-left: 10px">
|
<v-spacer></v-spacer>
|
||||||
<n-h6 prefix="bar" align-text><n-text type="primary">统一认证系统</n-text></n-h6>
|
</v-app-bar>
|
||||||
</div>
|
|
||||||
<div v-if="store.state.user.ready" class="inline-block h-full float-right flex justify-center items-center">
|
<v-main>
|
||||||
<avatar></avatar>
|
<router-view></router-view>
|
||||||
</div>
|
</v-main>
|
||||||
<div class="inline-block float-right h-full px-3">
|
</v-app>
|
||||||
<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>
|
|
||||||
</n-layout-header>
|
|
||||||
<n-layout has-sider style="height: calc(100vh - 88px)">
|
|
||||||
<n-layout-sider
|
|
||||||
collapse-mode="transform"
|
|
||||||
:collapsed-width="0"
|
|
||||||
:width="120"
|
|
||||||
show-trigger="bar"
|
|
||||||
content-style="padding: 24px;"
|
|
||||||
bordered
|
|
||||||
default-collapsed
|
|
||||||
:native-scrollbar="false"
|
|
||||||
>
|
|
||||||
-
|
|
||||||
</n-layout-sider>
|
|
||||||
<n-layout class="main" :native-scrollbar="false">
|
|
||||||
<n-message-provider>
|
|
||||||
<router-view v-slot="{ Component }">
|
|
||||||
<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)"
|
|
||||||
></component>
|
|
||||||
</transition>
|
|
||||||
</router-view>
|
|
||||||
</n-message-provider>
|
|
||||||
</n-layout>
|
|
||||||
</n-layout>
|
|
||||||
</n-layout>
|
|
||||||
<n-layout-footer bordered style="height: 24px;line-height: 24px"
|
|
||||||
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')">
|
|
||||||
©2021 veypi
|
|
||||||
</span>
|
|
||||||
</n-layout-footer>
|
|
||||||
</n-layout>
|
|
||||||
</n-config-provider>
|
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
|
||||||
// This starter template is using Vue 3 <script setup> SFCs
|
|
||||||
import {onBeforeMount, ref} from 'vue'
|
|
||||||
import util from './libs/util'
|
|
||||||
import {useStore} from "./store";
|
|
||||||
import {Theme, IsDark, ChangeTheme} from "./theme";
|
|
||||||
import {zhCN, dateZhCN} from 'naive-ui'
|
|
||||||
import avatar from "./components/avatar";
|
|
||||||
import fullscreen from './components/fullscreen'
|
|
||||||
import Fullscreen from "./components/fullscreen/fullscreen.vue";
|
|
||||||
|
|
||||||
let isFullScreen = ref(false)
|
<script lang="ts">
|
||||||
let store = useStore()
|
import Vue from 'vue'
|
||||||
|
import util from '@/libs/util'
|
||||||
|
|
||||||
onBeforeMount(() => {
|
export default Vue.extend({
|
||||||
util.title("统一认证")
|
name: 'App',
|
||||||
store.dispatch('fetchSelf')
|
|
||||||
store.dispatch('user/fetchUserData')
|
components: {},
|
||||||
|
|
||||||
|
data: () => ({
|
||||||
|
//
|
||||||
|
}),
|
||||||
|
|
||||||
|
beforeCreate() {
|
||||||
|
util.title('统一认证')
|
||||||
|
this.$store.dispatch('fetchSelf')
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
let goto = (url: any) => {
|
|
||||||
window.open(url, "_blank")
|
|
||||||
}
|
|
||||||
|
|
||||||
let collapsed = ref(true)
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<style lang="less">
|
|
||||||
.animate__400ms {
|
|
||||||
--animate-duration: 400ms;
|
|
||||||
}
|
|
||||||
|
|
||||||
html,
|
|
||||||
body {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 周围滑动留白 */
|
|
||||||
html {
|
|
||||||
overflow: hidden;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
overflow: auto;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header-icon {
|
|
||||||
display: inline-block;
|
|
||||||
font-size: 24px;
|
|
||||||
margin: 20px 10px 20px 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#app {
|
|
||||||
font-family: Avenir, Helvetica, Arial, sans-serif;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
-moz-osx-font-smoothing: grayscale;
|
|
||||||
color: #2c3e50;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main {
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar {
|
|
||||||
display: none; /* Chrome Safari */
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@ -1,66 +1,65 @@
|
|||||||
// @ts-ignore
|
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import {store} from '../store'
|
import store from '@/store'
|
||||||
|
|
||||||
|
|
||||||
function baseRequests(url: string, method: any = 'GET', query: any, data: any, success: any, fail?: Function) {
|
function baseRequests(url: string, method: any = 'GET', query: any, data: any, success: any, fail?: Function) {
|
||||||
return axios({
|
return axios({
|
||||||
url: url,
|
url: url,
|
||||||
params: query,
|
params: query,
|
||||||
data: data,
|
data: data,
|
||||||
method: method,
|
method: method,
|
||||||
headers: {
|
headers: {
|
||||||
auth_token: localStorage.auth_token
|
auth_token: localStorage.auth_token
|
||||||
}
|
}
|
||||||
}).then((res: any) => {
|
})
|
||||||
if ('auth_token' in res.headers) {
|
.then((res: any) => {
|
||||||
localStorage.auth_token = res.headers.auth_token
|
if ('auth_token' in res.headers) {
|
||||||
}
|
localStorage.auth_token = res.headers.auth_token
|
||||||
if (method === 'HEAD') {
|
}
|
||||||
success(res.headers)
|
if (method === 'HEAD') {
|
||||||
} else {
|
success(res.headers)
|
||||||
success(res.data)
|
} else {
|
||||||
}
|
success(res.data)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((e: any) => {
|
||||||
|
if (e.response && e.response.status === 401) {
|
||||||
|
console.log(e)
|
||||||
|
store.dispatch('handleLogOut')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
console.log(e)
|
||||||
|
if (e.response && e.response.status === 500) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (typeof fail === 'function') {
|
||||||
|
fail(e.response)
|
||||||
|
} else if (e.response && e.response.status === 400) {
|
||||||
|
console.log(400)
|
||||||
|
} else {
|
||||||
|
console.log(e.request)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch((e: any) => {
|
|
||||||
if (e.response && e.response.status === 401) {
|
|
||||||
console.log(e)
|
|
||||||
store.commit('user/logout')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
console.log(e)
|
|
||||||
if (e.response && e.response.status === 500) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (typeof fail === 'function') {
|
|
||||||
fail(e.response)
|
|
||||||
} else if (e.response && e.response.status === 400) {
|
|
||||||
console.log(400)
|
|
||||||
} else {
|
|
||||||
console.log(e.request)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const ajax = {
|
const ajax = {
|
||||||
get(url: '', data = {}, success = {}, fail?: Function) {
|
get(url: '', data = {}, success = {}, fail?: Function) {
|
||||||
return baseRequests(url, 'GET', data, {}, success, fail)
|
return baseRequests(url, 'GET', data, {}, success, fail)
|
||||||
},
|
},
|
||||||
head(url: '', data = {}, success = {}, fail?: Function) {
|
head(url: '', data = {}, success = {}, fail?: Function) {
|
||||||
return baseRequests(url, 'HEAD', data, {}, success, fail)
|
return baseRequests(url, 'HEAD', data, {}, success, fail)
|
||||||
},
|
},
|
||||||
delete(url: '', data = {}, success = {}, fail?: Function) {
|
delete(url: '', data = {}, success = {}, fail?: Function) {
|
||||||
return baseRequests(url, 'DELETE', data, {}, success, fail)
|
return baseRequests(url, 'DELETE', data, {}, success, fail)
|
||||||
},
|
},
|
||||||
post(url: '', data = {}, success = {}, fail?: Function) {
|
post(url: '', data = {}, success = {}, fail?: Function) {
|
||||||
return baseRequests(url, 'POST', {}, data, success, fail)
|
return baseRequests(url, 'POST', {}, data, success, fail)
|
||||||
},
|
},
|
||||||
put(url: '', data = {}, success = {}, fail?: Function) {
|
put(url: '', data = {}, success = {}, fail?: Function) {
|
||||||
return baseRequests(url, 'PUT', {}, data, success, fail)
|
return baseRequests(url, 'PUT', {}, data, success, fail)
|
||||||
},
|
},
|
||||||
patch(url: '', data = {}, success = {}, fail?: Function) {
|
patch(url: '', data = {}, success = {}, fail?: Function) {
|
||||||
return baseRequests(url, 'PATCH', {}, data, success, fail)
|
return baseRequests(url, 'PATCH', {}, data, success, fail)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ajax
|
export default ajax
|
||||||
|
|||||||
@ -4,115 +4,213 @@
|
|||||||
* Distributed under terms of the MIT license.
|
* Distributed under terms of the MIT license.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {App} from 'vue'
|
import Vue from 'vue'
|
||||||
import ajax from './ajax'
|
|
||||||
import {store} from '../store'
|
|
||||||
import {Base64} from 'js-base64'
|
import {Base64} from 'js-base64'
|
||||||
|
import ajax from './ajax'
|
||||||
|
import store from '@/store'
|
||||||
|
|
||||||
export type SuccessFunction<T> = (e: any) => void;
|
export type SuccessFunction<T> = (e: any) => void;
|
||||||
export type FailedFunction<T> = (e: any) => void;
|
export type FailedFunction<T> = (e: any) => void;
|
||||||
|
|
||||||
const Code = {
|
const Code = {
|
||||||
42011: '无操作权限',
|
42011: '无操作权限',
|
||||||
22031: '资源不存在 或 您无权操作该资源'
|
22031: '资源不存在 或 您无权操作该资源'
|
||||||
}
|
}
|
||||||
|
|
||||||
class Interface {
|
class Interface {
|
||||||
private readonly method: Function
|
private readonly method: Function
|
||||||
private readonly api: string
|
private readonly api: string
|
||||||
private readonly data: any
|
private readonly data: any
|
||||||
|
|
||||||
constructor(method: Function, api: string, data?: any) {
|
constructor(method: Function, api: string, data?: any) {
|
||||||
this.method = method
|
this.method = method
|
||||||
this.api = api
|
this.api = api
|
||||||
this.data = data
|
this.data = data
|
||||||
|
}
|
||||||
|
|
||||||
|
Start(success: SuccessFunction<any>, fail?: FailedFunction<any>) {
|
||||||
|
const newFail = function (data: any) {
|
||||||
|
if (data && data.code === 40001) {
|
||||||
|
// no login
|
||||||
|
store.dispatch('handleLogout')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
|
||||||
|
// @ts-ignore
|
||||||
|
if (data && data.code > 0 && Code[data.code]) {
|
||||||
|
}
|
||||||
|
if (fail) {
|
||||||
|
fail(data)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Start(success: SuccessFunction<any>, fail?: FailedFunction<any>) {
|
const newSuccess = function (data: any) {
|
||||||
const newFail = function (data: any) {
|
if (Number(data.status) === 1) {
|
||||||
if (data && data.code === 40001) {
|
if (success) {
|
||||||
// no login
|
success(data.content)
|
||||||
store.commit('user/logout')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
|
|
||||||
// @ts-ignore
|
|
||||||
if (data && data.code && Code[data.code]) {
|
|
||||||
}
|
|
||||||
if (fail) {
|
|
||||||
fail(data.err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
const newSuccess = function (data: any) {
|
newFail(data)
|
||||||
if (Number(data.status) === 1) {
|
if (data.code === 41001) {
|
||||||
if (success) {
|
store.dispatch('handleLogout')
|
||||||
success(data.content)
|
// bus.$emit('log_out')
|
||||||
}
|
|
||||||
} else {
|
|
||||||
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const message = {
|
||||||
|
count() {
|
||||||
|
return new Interface(ajax.get, '/api/message/', {
|
||||||
|
count: true,
|
||||||
|
status: 'UnRead'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
get_content(id: number) {
|
||||||
|
return new Interface(ajax.get, '/api/message/' + Number(id))
|
||||||
|
},
|
||||||
|
list(status: string) {
|
||||||
|
return new Interface(ajax.get, '/api/message/', {status})
|
||||||
|
},
|
||||||
|
update(id: number, status: string) {
|
||||||
|
return new Interface(ajax.patch, '/api/message/' + Number(id), {status})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const role = {
|
||||||
|
local: '/api/role/',
|
||||||
|
get(id: number) {
|
||||||
|
return new Interface(ajax.get, this.local + id)
|
||||||
|
},
|
||||||
|
list() {
|
||||||
|
return new Interface(ajax.get, this.local)
|
||||||
|
},
|
||||||
|
update(id: number, props: any) {
|
||||||
|
return new Interface(ajax.patch, this.local + id, props)
|
||||||
|
},
|
||||||
|
create(props: any) {
|
||||||
|
return new Interface(ajax.post, this.local, props)
|
||||||
|
},
|
||||||
|
del(id: number) {
|
||||||
|
return new Interface(ajax.delete, this.local + id)
|
||||||
|
},
|
||||||
|
bind(id: number, aid: number) {
|
||||||
|
return new Interface(ajax.get, this.local + id + '/bind/' + aid)
|
||||||
|
},
|
||||||
|
unbind(id: number, aid: number) {
|
||||||
|
return new Interface(ajax.get, this.local + id + '/unbind/' + aid)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const app = {
|
const app = {
|
||||||
local: '/api/app/',
|
local: '/api/app/',
|
||||||
self() {
|
self() {
|
||||||
return new Interface(ajax.get, this.local, {is_self: true})
|
return new Interface(ajax.get, this.local, {is_self: true})
|
||||||
},
|
},
|
||||||
get(id: string) {
|
get(id: string) {
|
||||||
return new Interface(ajax.get, this.local + id)
|
return new Interface(ajax.get, this.local + id)
|
||||||
},
|
},
|
||||||
list() {
|
list() {
|
||||||
return new Interface(ajax.get, this.local)
|
return new Interface(ajax.get, this.local)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = {
|
const user = {
|
||||||
local: '/api/user/',
|
local: '/api/user/',
|
||||||
register(username: string, password: string, uuid: string, prop?: any) {
|
register(username: string, password: string, uuid: string, prop?: any) {
|
||||||
const data = Object.assign({
|
const data = Object.assign({
|
||||||
username: username,
|
username: username,
|
||||||
uuid: uuid,
|
uuid: uuid,
|
||||||
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, uuid: string) {
|
login(username: string, password: string, uuid: string) {
|
||||||
return new Interface(ajax.head, this.local + username, {
|
return new Interface(ajax.head, this.local + username, {
|
||||||
uid_type: 'username',
|
uid_type: 'username',
|
||||||
uuid: uuid,
|
uuid: uuid,
|
||||||
password: Base64.encode(password)
|
password: Base64.encode(password)
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
get(id: number) {
|
|
||||||
return new Interface(ajax.get, this.local + id)
|
|
||||||
},
|
|
||||||
list() {
|
|
||||||
return new Interface(ajax.get, this.local)
|
|
||||||
},
|
|
||||||
update(id: number, props: any) {
|
|
||||||
return new Interface(ajax.patch, this.local + id, props)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const api = {
|
const api = {
|
||||||
user: user,
|
role: role,
|
||||||
app: app
|
app: app,
|
||||||
|
user: user,
|
||||||
|
admin: {
|
||||||
|
auths() {
|
||||||
|
return new Interface(ajax.get, '/api/auth/')
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
create(props: any) {
|
||||||
|
const p = Object.assign({}, props)
|
||||||
|
p.password = Base64.encode(props.password)
|
||||||
|
return new Interface(ajax.post, '/api/user/', p)
|
||||||
|
},
|
||||||
|
update(user_id: number, props: any) {
|
||||||
|
return new Interface(ajax.patch, '/api/user/' + user_id, props)
|
||||||
|
},
|
||||||
|
enable(user_id: number) {
|
||||||
|
return new Interface(ajax.patch, '/api/user/' + user_id, {
|
||||||
|
status: 'ok'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
disable(user_id: number) {
|
||||||
|
return new Interface(ajax.patch, '/api/user/' + user_id, {
|
||||||
|
status: 'disabled'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
attach_role(user_id: number, props: any) {
|
||||||
|
return new Interface(ajax.post, '/api/user/' + user_id + '/role/', props)
|
||||||
|
},
|
||||||
|
detach_role(user_id: number, id: any) {
|
||||||
|
return new Interface(ajax.delete, '/api/user/' + user_id + '/role/' + id)
|
||||||
|
},
|
||||||
|
reset_pass(user_id: number, password: string) {
|
||||||
|
return new Interface(ajax.patch, '/api/user/' + user_id, {password})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
auth: {
|
||||||
|
event() {
|
||||||
|
return {
|
||||||
|
local: '/api/user/event/',
|
||||||
|
list() {
|
||||||
|
return new Interface(ajax.get, this.local)
|
||||||
|
},
|
||||||
|
create(title: string, tag: string, start_date: any, end_date: any) {
|
||||||
|
return new Interface(ajax.post, this.local, {title, tag, start_date, end_date})
|
||||||
|
},
|
||||||
|
del(id: number) {
|
||||||
|
return new Interface(ajax.delete, this.local + id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
favorite(name: string, tag: string, ok: boolean) {
|
||||||
|
if (ok) {
|
||||||
|
return new Interface(ajax.post, '/api/user/favorite', {name, tag})
|
||||||
|
}
|
||||||
|
return new Interface(ajax.delete, '/api/user/favorite', {name, tag})
|
||||||
|
},
|
||||||
|
get(id: number) {
|
||||||
|
return new Interface(ajax.get, '/api/user/' + id)
|
||||||
|
},
|
||||||
|
search(username: string) {
|
||||||
|
return new Interface(ajax.get, '/api/user/', {
|
||||||
|
username
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
message: message
|
||||||
}
|
}
|
||||||
|
|
||||||
const Api = {
|
const Api = {
|
||||||
install(vue: App): void {
|
install(vue: typeof Vue): void {
|
||||||
vue.config.globalProperties.$api = api
|
vue.prototype.$api = api
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export {Api}
|
export {Api}
|
||||||
export default api
|
export default api
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
1
oaf/src/assets/logo.svg
Normal file
1
oaf/src/assets/logo.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 87.5 100"><defs><style>.cls-1{fill:#1697f6;}.cls-2{fill:#7bc6ff;}.cls-3{fill:#1867c0;}.cls-4{fill:#aeddff;}</style></defs><title>Artboard 46</title><polyline class="cls-1" points="43.75 0 23.31 0 43.75 48.32"/><polygon class="cls-2" points="43.75 62.5 43.75 100 0 14.58 22.92 14.58 43.75 62.5"/><polyline class="cls-3" points="43.75 0 64.19 0 43.75 48.32"/><polygon class="cls-4" points="64.58 14.58 87.5 14.58 43.75 100 43.75 62.5 64.58 14.58"/></svg>
|
||||||
|
After Width: | Height: | Size: 539 B |
@ -1,52 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { ref } from 'vue'
|
|
||||||
|
|
||||||
defineProps<{ msg: string }>()
|
|
||||||
|
|
||||||
const count = ref(0)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<h1>{{ msg }}</h1>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Recommended IDE setup:
|
|
||||||
<a href="https://code.visualstudio.com/" target="_blank">VSCode</a>
|
|
||||||
+
|
|
||||||
<a href="https://github.com/johnsoncodehk/volar" target="_blank">Volar</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>See <code>README.md</code> for more information.</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<a href="https://vitejs.dev/guide/features.html" target="_blank">
|
|
||||||
Vite Docs
|
|
||||||
</a>
|
|
||||||
|
|
|
||||||
<a href="https://v3.vuejs.org/" target="_blank">Vue 3 Docs</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<button type="button" @click="count++">count is: {{ count }}</button>
|
|
||||||
<p>
|
|
||||||
Edit
|
|
||||||
<code>components/HelloWorld.vue</code> to test hot module replacement.
|
|
||||||
</p>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
a {
|
|
||||||
color: #42b983;
|
|
||||||
}
|
|
||||||
|
|
||||||
label {
|
|
||||||
margin: 0 0.5em;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
code {
|
|
||||||
background-color: #eee;
|
|
||||||
padding: 2px 4px;
|
|
||||||
border-radius: 4px;
|
|
||||||
color: #304455;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,32 +1,43 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="wx_reg"></div>
|
<div id="wx_reg"></div>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang='ts'>
|
<script lang='ts'>
|
||||||
import {onMounted} from 'vue'
|
import {Component, Vue, Prop} from 'vue-property-decorator'
|
||||||
|
import '@/libs/wwLogin.js'
|
||||||
|
|
||||||
function goto(id: string, app: string, url: string, state?: number, href?: string) {
|
@Component({
|
||||||
// eslint-disable-next-line
|
components: {}
|
||||||
// @ts-ignore
|
|
||||||
window.WwLogin({
|
|
||||||
id: 'wx_reg',
|
|
||||||
appid: id,
|
|
||||||
agentid: app,
|
|
||||||
redirect_uri: encodeURIComponent(url),
|
|
||||||
state: state,
|
|
||||||
href: href
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
let aid = ''
|
|
||||||
|
|
||||||
let app = ''
|
|
||||||
|
|
||||||
let url = ''
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
goto(aid, app, url, new Date().getTime())
|
|
||||||
})
|
})
|
||||||
|
export default class WxLogin extends Vue {
|
||||||
|
goto(id: string, app: string, url: string, state?: number, href?: string) {
|
||||||
|
// eslint-disable-next-line
|
||||||
|
// @ts-ignore
|
||||||
|
window.WwLogin({
|
||||||
|
id: 'wx_reg',
|
||||||
|
appid: id,
|
||||||
|
agentid: app,
|
||||||
|
redirect_uri: encodeURIComponent(url),
|
||||||
|
state: state,
|
||||||
|
href: href
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@Prop({default: ''})
|
||||||
|
aid = ''
|
||||||
|
|
||||||
|
@Prop({default: ''})
|
||||||
|
app = ''
|
||||||
|
|
||||||
|
@Prop({default: ''})
|
||||||
|
url = ''
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.goto(this.aid, this.app, this.url, new Date().getTime())
|
||||||
|
}
|
||||||
|
|
||||||
|
created() {
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
|
|||||||
@ -1,29 +0,0 @@
|
|||||||
<template>
|
|
||||||
<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', params: {uuid: core.uuid}})" round :size="80" :src="core.icon">
|
|
||||||
{{ core.icon ? '' : core.name }}
|
|
||||||
</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>
|
|
||||||
</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} from "vue";
|
|
||||||
|
|
||||||
let props = defineProps<{
|
|
||||||
core: any
|
|
||||||
}>()
|
|
||||||
</script>
|
|
||||||
<style scoped>
|
|
||||||
.core {
|
|
||||||
width: 256px;
|
|
||||||
background: #2c3e50;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,61 +0,0 @@
|
|||||||
<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>
|
|
||||||
</div>
|
|
||||||
<template v-slot:main>
|
|
||||||
<div style="height: 100%">
|
|
||||||
<div style="height: calc(100% - 50px)">
|
|
||||||
<div class="w-full px-3">
|
|
||||||
<div class="h-16 flex justify-between items-center">
|
|
||||||
<span style="color: #777">我的账户</span>
|
|
||||||
<span @click="$router.push({name: 'user_setting'});shown=false" class="cursor-pointer"
|
|
||||||
style="color:#f36828">账户中心</span>
|
|
||||||
</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>
|
|
||||||
</div>
|
|
||||||
<div class="col-span-2 text-xs grid grid-cols-1 items-center" style="">
|
|
||||||
<span>昵称:    {{ $store.state.user.nickname }}</span>
|
|
||||||
<span>账户:    {{ $store.state.user.username }}</span>
|
|
||||||
<span>邮箱:    {{ $store.state.user.email }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="">123</div>
|
|
||||||
</div>
|
|
||||||
<hr class="mt-10" style="border:none;border-top:1px solid #777;">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<hr style="border:none;border-top:2px solid #777;">
|
|
||||||
<div style="height: 48px">
|
|
||||||
<div @click="$store.commit('user/logout')"
|
|
||||||
class="w-full h-full flex justify-center items-center cursor-pointer transition duration-500 ease-in-out transform hover:scale-125">
|
|
||||||
<one-icon :color="IsDark?'#eee': '#333'" class="inline-block" style="font-size: 24px;">
|
|
||||||
logout
|
|
||||||
</one-icon>
|
|
||||||
<div>
|
|
||||||
退出登录
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</base_frame>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import base_frame from './frame.vue'
|
|
||||||
import {IsDark} from '../../theme'
|
|
||||||
import {ref} from "vue";
|
|
||||||
|
|
||||||
let shown = ref(false)
|
|
||||||
|
|
||||||
function asd(e) {
|
|
||||||
console.log([e, shown.value])
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
</style>
|
|
||||||
@ -1,82 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<div @click="setValue(true)">
|
|
||||||
<slot>
|
|
||||||
</slot>
|
|
||||||
</div>
|
|
||||||
<div @click.self="setValue(false)" class="core" style="height: 100vh;width: 100vw;" v-if="props.modelValue">
|
|
||||||
<div style="height: 100%; width: 300px" class="core-right">
|
|
||||||
<transition appear enter-active-class="animate__slideInRight">
|
|
||||||
<div class="right-title animate__animated animate__faster">
|
|
||||||
<slot name="title"></slot>
|
|
||||||
<div class="flex items-center float-right h-full px-1">
|
|
||||||
<one-icon @click="setValue(false)" color="#fff" style="font-size: 24px">close</one-icon>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</transition>
|
|
||||||
<div class="right-main">
|
|
||||||
<transition appear enter-active-class="animate__slideInDown">
|
|
||||||
<div class="right-main-core animate__animated animate__faster"
|
|
||||||
:style="{'background': props.isDark ? '#222': '#eee'}">
|
|
||||||
<slot name="main"></slot>
|
|
||||||
</div>
|
|
||||||
</transition>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { defineProps, defineEmits, watch} from "vue";
|
|
||||||
|
|
||||||
let emits = defineEmits<{
|
|
||||||
(e: 'update:modelValue', v: boolean): void
|
|
||||||
}>()
|
|
||||||
let props = defineProps<{
|
|
||||||
isDark: boolean,
|
|
||||||
modelValue: boolean
|
|
||||||
}>()
|
|
||||||
|
|
||||||
function setValue(b: boolean) {
|
|
||||||
emits('update:modelValue', b)
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.core {
|
|
||||||
position: fixed;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
background: rgba(0, 0, 0, 0.4);
|
|
||||||
z-index: 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
.core-right {
|
|
||||||
position: absolute;
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.right-main {
|
|
||||||
width: 100%;
|
|
||||||
height: calc(100% - 50px);
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.right-main-core {
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
-webkit-animation-delay: 0.4s;
|
|
||||||
animation-delay: 0.4s;
|
|
||||||
--animate-duration: 400ms;
|
|
||||||
}
|
|
||||||
|
|
||||||
.right-title {
|
|
||||||
width: 100%;
|
|
||||||
height: 50px;
|
|
||||||
line-height: 50px;
|
|
||||||
background: linear-gradient(90deg, #f74d22, #fa9243);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
import avatar from './avatar.vue'
|
|
||||||
|
|
||||||
export default avatar
|
|
||||||
20
oaf/src/components/demo.vue
Normal file
20
oaf/src/components/demo.vue
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<template>
|
||||||
|
<div></div>
|
||||||
|
</template>
|
||||||
|
<script lang='ts'>
|
||||||
|
import {Component, Vue} from 'vue-property-decorator'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
components: {}
|
||||||
|
})
|
||||||
|
export default class Demo extends Vue {
|
||||||
|
mounted() {
|
||||||
|
}
|
||||||
|
|
||||||
|
created() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@ -1,10 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div></div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
</style>
|
|
||||||
@ -1,71 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div @click="handleFullscreen">
|
|
||||||
<one-icon>{{ props.modelValue ? 'fullscreen-exit' : 'fullscreen' }}</one-icon>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
|
|
||||||
import {defineEmits, onMounted, defineProps} from "vue";
|
|
||||||
|
|
||||||
let emit = defineEmits<{
|
|
||||||
(e: 'update:modelValue', v: boolean): void
|
|
||||||
}>()
|
|
||||||
|
|
||||||
let props = defineProps<{
|
|
||||||
modelValue: boolean
|
|
||||||
}>()
|
|
||||||
|
|
||||||
function handleFullscreen() {
|
|
||||||
let main = document.body
|
|
||||||
if (props.modelValue) {
|
|
||||||
if (document.exitFullscreen) {
|
|
||||||
document.exitFullscreen()
|
|
||||||
} else if (document.mozCancelFullScreen) {
|
|
||||||
document.mozCancelFullScreen()
|
|
||||||
} else if (document.webkitCancelFullScreen) {
|
|
||||||
document.webkitCancelFullScreen()
|
|
||||||
} else if (document.msExitFullscreen) {
|
|
||||||
document.msExitFullscreen()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (main.requestFullscreen) {
|
|
||||||
main.requestFullscreen()
|
|
||||||
} else if (main.mozRequestFullScreen) {
|
|
||||||
main.mozRequestFullScreen()
|
|
||||||
} else if (main.webkitRequestFullScreen) {
|
|
||||||
main.webkitRequestFullScreen()
|
|
||||||
} else if (main.msRequestFullscreen) {
|
|
||||||
main.msRequestFullscreen()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
let isFullscreen =
|
|
||||||
document.fullscreenElement ||
|
|
||||||
document.mozFullScreenElement ||
|
|
||||||
document.webkitFullscreenElement ||
|
|
||||||
document.fullScreen ||
|
|
||||||
document.mozFullScreen ||
|
|
||||||
document.webkitIsFullScreen
|
|
||||||
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)
|
|
||||||
})
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
</style>
|
|
||||||
@ -1,2 +0,0 @@
|
|||||||
import fullscreen from './fullscreen.vue'
|
|
||||||
export default fullscreen
|
|
||||||
28
oaf/src/components/one-icon/icon.vue
Normal file
28
oaf/src/components/one-icon/icon.vue
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<template>
|
||||||
|
<svg class="icon" aria-hidden="true">
|
||||||
|
<use :xlink:href="'#icon-'+icon"></use>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
<script lang='ts'>
|
||||||
|
import {Component, Vue} from 'vue-property-decorator'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
components: {}
|
||||||
|
})
|
||||||
|
export default class OneIcon extends Vue {
|
||||||
|
get icon() {
|
||||||
|
if (this.$slots.default) return this.$slots.default[0].text?.trim()
|
||||||
|
console.warn('blank icon name')
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.icon {
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
vertical-align: -0.15em;
|
||||||
|
fill: currentColor;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
26
oaf/src/components/one-icon/index.ts
Normal file
26
oaf/src/components/one-icon/index.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import Vue from 'vue'
|
||||||
|
import OneIcon from './icon.vue'
|
||||||
|
|
||||||
|
function loadJS(url: string) {
|
||||||
|
const script = document.createElement('script')
|
||||||
|
script.type = 'text/javascript'
|
||||||
|
script.src = url
|
||||||
|
document.getElementsByTagName('head')[0].appendChild(script)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
installed: false,
|
||||||
|
install(vue: typeof Vue, options?: { href: '' }): void {
|
||||||
|
if (this.installed) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.installed = true
|
||||||
|
if (options && options.href) {
|
||||||
|
console.log(options.href)
|
||||||
|
loadJS(options.href)
|
||||||
|
} else {
|
||||||
|
console.error('not set iconfont href')
|
||||||
|
}
|
||||||
|
vue.component('one-icon', OneIcon)
|
||||||
|
}
|
||||||
|
}
|
||||||
8
oaf/src/env.d.ts
vendored
8
oaf/src/env.d.ts
vendored
@ -1,8 +0,0 @@
|
|||||||
/// <reference types="vite/client" />
|
|
||||||
|
|
||||||
declare module '*.vue' {
|
|
||||||
import { DefineComponent } from 'vue'
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
|
|
||||||
const component: DefineComponent<{}, {}, any>
|
|
||||||
export default component
|
|
||||||
}
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
/* ./src/index.css */
|
|
||||||
|
|
||||||
/*! @import */
|
|
||||||
@tailwind base;
|
|
||||||
@tailwind components;
|
|
||||||
@tailwind utilities;
|
|
||||||
@ -27,9 +27,6 @@ const util = {
|
|||||||
document.cookie =
|
document.cookie =
|
||||||
name + '=' + escape(value) + ';expires=' + exp.toLocaleString()
|
name + '=' + escape(value) + ';expires=' + exp.toLocaleString()
|
||||||
},
|
},
|
||||||
getToken() {
|
|
||||||
return localStorage.auth_token
|
|
||||||
},
|
|
||||||
checkLogin() {
|
checkLogin() {
|
||||||
// return parseInt(this.getCookie('stat')) === 1
|
// return parseInt(this.getCookie('stat')) === 1
|
||||||
return Boolean(localStorage.auth_token)
|
return Boolean(localStorage.auth_token)
|
||||||
|
|||||||
@ -1,19 +1,24 @@
|
|||||||
import {createApp} from 'vue'
|
import Vue from 'vue'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
import router from './router'
|
import router from './router'
|
||||||
import {store, key} from './store'
|
import store from './store'
|
||||||
|
import vuetify from './plugins/vuetify'
|
||||||
|
import {Api} from '@/api'
|
||||||
import OneIcon from '@veypi/one-icon'
|
import OneIcon from '@veypi/one-icon'
|
||||||
import naive from 'naive-ui'
|
import Message from 'vue-m-message'
|
||||||
import './index.css'
|
import 'vue-m-message/dist/index.css'
|
||||||
import {Api} from './api'
|
|
||||||
import './assets/icon.js'
|
|
||||||
import 'animate.css'
|
|
||||||
|
|
||||||
const app = createApp(App)
|
Vue.use(Message) // will mount `Vue.prototype.$message`
|
||||||
|
|
||||||
app.use(Api)
|
// Vue.use(OneIcon, {href: 'https://at.alicdn.com/t/font_2872366_7aws02sx9bl.js'})
|
||||||
app.use(naive)
|
Vue.use(OneIcon, {href: './icon.js'})
|
||||||
app.use(OneIcon)
|
Vue.use(Api)
|
||||||
app.use(router)
|
|
||||||
app.use(store, key)
|
Vue.config.productionTip = false
|
||||||
app.mount('#app')
|
|
||||||
|
new Vue({
|
||||||
|
router,
|
||||||
|
store,
|
||||||
|
vuetify,
|
||||||
|
render: h => h(App)
|
||||||
|
}).$mount('#app')
|
||||||
|
|||||||
24
oaf/src/plugins/vuetify.ts
Normal file
24
oaf/src/plugins/vuetify.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import Vue from 'vue'
|
||||||
|
import Vuetify from 'vuetify/lib/framework'
|
||||||
|
|
||||||
|
Vue.use(Vuetify)
|
||||||
|
|
||||||
|
const light = {
|
||||||
|
primary: '#2196f3',
|
||||||
|
secondary: '#00bcd4',
|
||||||
|
accent: '#3f51b5',
|
||||||
|
error: '#f44336',
|
||||||
|
warning: '#ff5722',
|
||||||
|
info: '#ff9800',
|
||||||
|
success: '#4caf50',
|
||||||
|
reset: '#684bff'
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new Vuetify({
|
||||||
|
theme: {
|
||||||
|
dark: false,
|
||||||
|
themes: {
|
||||||
|
light: light
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
@ -1,83 +1,57 @@
|
|||||||
import {createRouter, createWebHistory} from 'vue-router'
|
import Vue from 'vue'
|
||||||
import util from '../libs/util'
|
import VueRouter, {RouteConfig} from 'vue-router'
|
||||||
|
import Home from '../views/Home.vue'
|
||||||
|
import Demo from '@/views/demo.vue'
|
||||||
|
import Login from '@/views/login.vue'
|
||||||
|
import Register from '@/views/register.vue'
|
||||||
|
import NotFound from '@/views/404.vue'
|
||||||
|
|
||||||
declare module 'vue-router' {
|
Vue.use(VueRouter)
|
||||||
interface RouteMeta {
|
// 避免push到相同路径报错
|
||||||
// 是可选的
|
// 获取原型对象上的push函数
|
||||||
isAdmin?: boolean
|
const originalPush = VueRouter.prototype.push
|
||||||
// 每个路由都必须声明
|
// 修改原型对象中的push方法
|
||||||
requiresAuth: boolean
|
VueRouter.prototype.push = function push(location: any) {
|
||||||
}
|
// eslint-disable-next-line
|
||||||
|
// @ts-ignore
|
||||||
|
return originalPush.call(this, location).catch(err => err)
|
||||||
}
|
}
|
||||||
|
|
||||||
const router = createRouter({
|
const routes: Array<RouteConfig> = [
|
||||||
history: createWebHistory(),
|
{
|
||||||
routes: [
|
path: '/',
|
||||||
{
|
name: 'home',
|
||||||
path: '/',
|
component: Home
|
||||||
name: 'home',
|
},
|
||||||
meta: {
|
{
|
||||||
requiresAuth: true,
|
path: '/app',
|
||||||
},
|
name: 'app',
|
||||||
component: () => import('../views/home.vue')
|
component: Demo
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/app/:uuid?',
|
path: '/login/:uuid?',
|
||||||
name: 'app',
|
name: 'login',
|
||||||
meta: {
|
component: Login
|
||||||
requiresAuth: true,
|
},
|
||||||
},
|
{
|
||||||
component: () => import('../views/app.vue')
|
path: '/register/:uuid?',
|
||||||
},
|
name: 'register',
|
||||||
{
|
component: Register
|
||||||
path: '/user/setting',
|
},
|
||||||
name: 'user_setting',
|
{
|
||||||
meta: {
|
path: '/wx',
|
||||||
requiresAuth: true
|
name: 'wx',
|
||||||
},
|
component: () => import('../views/wx.vue')
|
||||||
component: () => import('../views/user_setting.vue')
|
},
|
||||||
},
|
{
|
||||||
{
|
path: '*',
|
||||||
path: '/about',
|
name: '404',
|
||||||
name: 'about',
|
component: NotFound
|
||||||
component: () => import('../views/about.vue')
|
}
|
||||||
},
|
]
|
||||||
{
|
|
||||||
path: '/wx',
|
|
||||||
name: 'wx',
|
|
||||||
component: () => import('../views/wx.vue')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/login/:uuid?',
|
|
||||||
name: 'login',
|
|
||||||
component: () => import('../views/login.vue')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/register/:uuid?',
|
|
||||||
name: 'register',
|
|
||||||
component: () => import('../views/register.vue')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/:path(.*)',
|
|
||||||
name: '404',
|
|
||||||
component: () => import('../views/404.vue')
|
|
||||||
}
|
|
||||||
//...
|
|
||||||
],
|
|
||||||
})
|
|
||||||
|
|
||||||
router.beforeEach((to, from) => {
|
const router = new VueRouter({
|
||||||
// 而不是去检查每条路由记录
|
routes
|
||||||
// to.matched.some(record => record.meta.requiresAuth)
|
|
||||||
if (to.meta.requiresAuth && !util.checkLogin()) {
|
|
||||||
// 此路由需要授权,请检查是否已登录
|
|
||||||
// 如果没有,则重定向到登录页面
|
|
||||||
return {
|
|
||||||
name: 'login',
|
|
||||||
// 保存我们所在的位置,以便以后再来
|
|
||||||
query: {redirect: to.fullPath},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
export default router
|
export default router
|
||||||
|
|||||||
13
oaf/src/shims-tsx.d.ts
vendored
Normal file
13
oaf/src/shims-tsx.d.ts
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import Vue, { VNode } from 'vue'
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
namespace JSX {
|
||||||
|
// tslint:disable no-empty-interface
|
||||||
|
interface Element extends VNode {}
|
||||||
|
// tslint:disable no-empty-interface
|
||||||
|
interface ElementClass extends Vue {}
|
||||||
|
interface IntrinsicElements {
|
||||||
|
[elem: string]: any;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
5
oaf/src/shims-vue.d.ts
vendored
Normal file
5
oaf/src/shims-vue.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
declare module '*.js'
|
||||||
|
declare module '*.vue' {
|
||||||
|
import Vue from 'vue'
|
||||||
|
export default Vue
|
||||||
|
}
|
||||||
4
oaf/src/shims-vuetify.d.ts
vendored
Normal file
4
oaf/src/shims-vuetify.d.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
declare module 'vuetify/lib/framework' {
|
||||||
|
import Vuetify from 'vuetify'
|
||||||
|
export default Vuetify
|
||||||
|
}
|
||||||
@ -1,49 +1,30 @@
|
|||||||
import {InjectionKey} from 'vue'
|
import Vue from 'vue'
|
||||||
import {createStore, useStore as baseUseStore, Store} from 'vuex'
|
import Vuex from 'vuex'
|
||||||
import api from "../api";
|
import api from '@/api'
|
||||||
import {User, UserState} from './user'
|
import router from '@/router'
|
||||||
|
|
||||||
export interface State extends Object {
|
Vue.use(Vuex)
|
||||||
oauuid: string
|
|
||||||
user: UserState
|
|
||||||
apps: []
|
|
||||||
}
|
|
||||||
|
|
||||||
export const key: InjectionKey<Store<State>> = Symbol()
|
export default new Vuex.Store({
|
||||||
|
state: {
|
||||||
export const store = createStore<State>({
|
oauuid: '',
|
||||||
modules: {
|
user: null
|
||||||
user: User
|
},
|
||||||
},
|
mutations: {
|
||||||
// @ts-ignore
|
setOA(state: any, data: any) {
|
||||||
state: {
|
state.oauuid = data.uuid
|
||||||
oauuid: '',
|
|
||||||
apps: []
|
|
||||||
},
|
|
||||||
getters: {},
|
|
||||||
mutations: {
|
|
||||||
setOA(state: any, data: any) {
|
|
||||||
state.oauuid = data.uuid
|
|
||||||
},
|
|
||||||
setApps(state: State, data: any) {
|
|
||||||
state.apps = data
|
|
||||||
}
|
|
||||||
},
|
|
||||||
actions: {
|
|
||||||
fetchSelf({commit}) {
|
|
||||||
api.app.self().Start(d => {
|
|
||||||
commit('setOA', d)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
fetchApps({commit}) {
|
|
||||||
api.app.list().Start(e => {
|
|
||||||
commit('setApps', e)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
fetchSelf({commit}) {
|
||||||
|
api.app.self().Start(d => {
|
||||||
|
commit('setOA', d)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleLogout() {
|
||||||
|
localStorage.removeItem('auth_token')
|
||||||
|
router.push({name: 'login'})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modules: {}
|
||||||
})
|
})
|
||||||
|
|
||||||
// 定义自己的 `useStore` 组合式函数
|
|
||||||
export function useStore() {
|
|
||||||
return baseUseStore(key)
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,73 +0,0 @@
|
|||||||
import {Module} from "vuex";
|
|
||||||
import api from "../api";
|
|
||||||
import util from '../libs/util'
|
|
||||||
import {Base64} from 'js-base64'
|
|
||||||
import {State} from './index'
|
|
||||||
import router from "../router";
|
|
||||||
|
|
||||||
export interface UserState {
|
|
||||||
id: number
|
|
||||||
username: string
|
|
||||||
nickname: string
|
|
||||||
phone: string
|
|
||||||
icon: string
|
|
||||||
email: string
|
|
||||||
ready: boolean
|
|
||||||
auth: [auth?]
|
|
||||||
|
|
||||||
[key: string]: any
|
|
||||||
}
|
|
||||||
|
|
||||||
interface auth {
|
|
||||||
rid: string
|
|
||||||
ruid: string
|
|
||||||
level: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export const User: Module<UserState, State> = {
|
|
||||||
namespaced: true,
|
|
||||||
state: {
|
|
||||||
id: 0,
|
|
||||||
username: '',
|
|
||||||
nickname: '',
|
|
||||||
phone: '',
|
|
||||||
icon: '',
|
|
||||||
email: '',
|
|
||||||
auth: [],
|
|
||||||
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
|
|
||||||
state.ready = true
|
|
||||||
},
|
|
||||||
setAuth(state: UserState, data: any) {
|
|
||||||
state.auth = data
|
|
||||||
},
|
|
||||||
logout(state: UserState) {
|
|
||||||
state.ready = false
|
|
||||||
localStorage.removeItem('auth_token')
|
|
||||||
router.push({name: 'login'})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
actions: {
|
|
||||||
fetchUserData(context) {
|
|
||||||
let token = util.getToken()?.split('.');
|
|
||||||
if (!token || token.length !== 3) {
|
|
||||||
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 => {
|
|
||||||
context.commit('setBase', e)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,72 +0,0 @@
|
|||||||
import {darkTheme} from 'naive-ui/lib/themes'
|
|
||||||
import {BuiltInGlobalTheme} from 'naive-ui/lib/themes/interface'
|
|
||||||
import {lightTheme} from 'naive-ui/lib/themes/light'
|
|
||||||
import {ref} from 'vue'
|
|
||||||
import {useOsTheme, GlobalThemeOverrides} from 'naive-ui'
|
|
||||||
|
|
||||||
interface builtIn extends BuiltInGlobalTheme {
|
|
||||||
overrides: GlobalThemeOverrides
|
|
||||||
me: {
|
|
||||||
lightBox: string,
|
|
||||||
lightBoxShadow: string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let light = lightTheme as builtIn
|
|
||||||
let dark = darkTheme as builtIn
|
|
||||||
let intputNone = {
|
|
||||||
color: 'url(0) no-repeat',
|
|
||||||
colorFocus: 'url(0) no-repeat',
|
|
||||||
colorFocusWarning: 'url(0) no-repeat',
|
|
||||||
colorFocusError: 'url(0) no-repeat'
|
|
||||||
}
|
|
||||||
light.overrides = {
|
|
||||||
Input: Object.assign({}, intputNone)
|
|
||||||
}
|
|
||||||
dark.overrides = {
|
|
||||||
Input: Object.assign({
|
|
||||||
border: '1px solid #aaa'
|
|
||||||
}, intputNone)
|
|
||||||
}
|
|
||||||
light.common.cardColor = '#f4f4f4'
|
|
||||||
light.common.bodyColor = '#eee'
|
|
||||||
dark.common.bodyColor = '#2e2e2e'
|
|
||||||
light.me = {
|
|
||||||
lightBox: '#f4f4f4',
|
|
||||||
lightBoxShadow: '18px 18px 36px #c6c6c6, -18px -18px 36px #fff'
|
|
||||||
}
|
|
||||||
|
|
||||||
dark.me = {
|
|
||||||
lightBox: '#2e2e2e',
|
|
||||||
lightBoxShadow: '21px 21px 42px #272727, -21px -21px 42px #353535'
|
|
||||||
}
|
|
||||||
export const OsThemeRef = useOsTheme()
|
|
||||||
|
|
||||||
let theme = 'light'
|
|
||||||
|
|
||||||
export let Theme = ref(light)
|
|
||||||
|
|
||||||
export let IsDark = ref(false)
|
|
||||||
|
|
||||||
function change(t: string) {
|
|
||||||
if (t === 'dark') {
|
|
||||||
theme = 'dark'
|
|
||||||
Theme.value = dark
|
|
||||||
} else {
|
|
||||||
theme = 'light'
|
|
||||||
Theme.value = light
|
|
||||||
}
|
|
||||||
IsDark.value = theme === 'dark'
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ChangeTheme() {
|
|
||||||
if (IsDark.value) {
|
|
||||||
change('light')
|
|
||||||
} else {
|
|
||||||
change('dark')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (OsThemeRef.value === 'dark') {
|
|
||||||
change('dark')
|
|
||||||
}
|
|
||||||
14
oaf/src/types/vue-prototype.d.ts
vendored
Normal file
14
oaf/src/types/vue-prototype.d.ts
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// 1. 确保在声明补充的类型之前导入 'vue'
|
||||||
|
import Vue from 'vue'
|
||||||
|
import api from '@/api'
|
||||||
|
|
||||||
|
export type PluginFunction<T> = (Vue: typeof Vue, options?: T) => void;
|
||||||
|
|
||||||
|
// 2. 定制一个文件,设置你想要补充的类型
|
||||||
|
// 在 types/vue.d.ts 里 Vue 有构造函数类型
|
||||||
|
declare module 'vue/types/vue' {
|
||||||
|
// 3. 声明为 Vue 补充的东西
|
||||||
|
interface Vue {
|
||||||
|
$api: typeof api;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,32 +1,24 @@
|
|||||||
|
<style>
|
||||||
|
</style>
|
||||||
<template>
|
<template>
|
||||||
<div class="flex justify-center items-center">
|
<div class='home d-flex justify-center align-center'>
|
||||||
<div class="text-center text-xl">
|
<one-icon style="font-size: 100px">404</one-icon>
|
||||||
<one-icon style="font-size: 200px">404</one-icon>
|
|
||||||
<span>
|
|
||||||
路径失效啦! {{count}}秒
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang='ts'>
|
||||||
import {useRouter, useRoute} from 'vue-router'
|
import {Component, Vue} from 'vue-property-decorator'
|
||||||
import {onMounted, ref} from "vue";
|
import util from '@/libs/util'
|
||||||
|
|
||||||
const route = useRoute()
|
@Component({
|
||||||
const router = useRouter()
|
components: {}
|
||||||
let count = ref(5)
|
|
||||||
onMounted(() => {
|
|
||||||
console.log([route.path, route.params])
|
|
||||||
let timer = setInterval(()=> {
|
|
||||||
count.value--
|
|
||||||
if (count.value === 0) {
|
|
||||||
router.push('/')
|
|
||||||
clearInterval(timer)
|
|
||||||
}
|
|
||||||
}, 1000)
|
|
||||||
})
|
})
|
||||||
</script>
|
export default class NotFound extends Vue {
|
||||||
<style scoped>
|
mounted() {
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
created() {
|
||||||
|
util.title('404')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|||||||
43
oaf/src/views/Home.vue
Normal file
43
oaf/src/views/Home.vue
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<style>
|
||||||
|
.home {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<template>
|
||||||
|
<div class='home d-flex justify-center align-center'>
|
||||||
|
<one-icon style="color: aqua;font-size: 50px">glassdoor</one-icon>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang='ts'>
|
||||||
|
import {Component, Vue} from 'vue-property-decorator'
|
||||||
|
import util from '@/libs/util'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
components: {}
|
||||||
|
})
|
||||||
|
export default class Home extends Vue {
|
||||||
|
apps = []
|
||||||
|
|
||||||
|
getApps() {
|
||||||
|
this.$api.app.list().Start(d => {
|
||||||
|
console.log(d)
|
||||||
|
this.apps = d
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.getApps()
|
||||||
|
}
|
||||||
|
|
||||||
|
created() {
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeCreate() {
|
||||||
|
if (!util.checkLogin()) {
|
||||||
|
this.$router.push({name: 'login', query: this.$route.query, params: this.$route.params})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@ -1,12 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
about
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
</style>
|
|
||||||
@ -1,28 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
{{ uuid }}
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import {useRoute, useRouter} from "vue-router";
|
|
||||||
import {computed, onMounted} from "vue";
|
|
||||||
import api from "../api";
|
|
||||||
|
|
||||||
let route = useRoute()
|
|
||||||
let router = useRouter()
|
|
||||||
let uuid = computed(() => route.params.uuid)
|
|
||||||
onMounted(() => {
|
|
||||||
if (uuid.value === '') {
|
|
||||||
router.push({name: '404', params: {path: route.path}})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
api.app.get(uuid.value as string).Start(e => {
|
|
||||||
console.log(e)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
</style>
|
|
||||||
@ -1,10 +1,21 @@
|
|||||||
|
<style>
|
||||||
|
</style>
|
||||||
<template>
|
<template>
|
||||||
<div></div>
|
<div class='home d-flex justify-center align-center'>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang='ts'>
|
||||||
|
import {Component, Vue} from 'vue-property-decorator'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
components: {}
|
||||||
|
})
|
||||||
|
export default class Demo extends Vue {
|
||||||
|
mounted() {
|
||||||
|
}
|
||||||
|
|
||||||
|
created() {
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|||||||
@ -1,10 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div></div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
</style>
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
<template>
|
|
||||||
<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 class="flex items-center justify-center" v-for="(item, k) in apps" :key="k">
|
|
||||||
<AppCard :core="item"></AppCard>
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center justify-center" v-for="(item) in '1234567890'" :key="item">
|
|
||||||
<AppCard :core="{}"></AppCard>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import {onMounted, ref} from "vue";
|
|
||||||
import api from "../api";
|
|
||||||
import AppCard from '../components/app.vue'
|
|
||||||
|
|
||||||
let apps = ref([])
|
|
||||||
|
|
||||||
function getApps() {
|
|
||||||
api.app.list().Start(e => {
|
|
||||||
apps.value = e
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
getApps()
|
|
||||||
})
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
</style>
|
|
||||||
@ -1,98 +1,123 @@
|
|||||||
<template>
|
<style>
|
||||||
<div class="flex items-center justify-center">
|
|
||||||
<div
|
|
||||||
:style="{background:Theme.me.lightBox, 'box-shadow': Theme.me.lightBoxShadow}"
|
|
||||||
class="px-10 pb-9 pt-28 rounded-xl w-96">
|
|
||||||
<n-form label-width="70px" label-align="left" :model="data" ref="form_ref" label-placement="left" :rules="rules">
|
|
||||||
<n-form-item required label="用户名" path="username">
|
|
||||||
<n-input @keydown.enter="divs[1].focus()" :ref="el => {if (el)divs[0]=el}"
|
|
||||||
v-model:value="data.username"></n-input>
|
|
||||||
</n-form-item>
|
|
||||||
<n-form-item required label="密码" path="password">
|
|
||||||
<n-input @keydown.enter="login" :ref="el => {if (el) divs[1]=el}" v-model:value="data.password"
|
|
||||||
type="password"></n-input>
|
|
||||||
</n-form-item>
|
|
||||||
<div class="flex justify-around mt-4">
|
|
||||||
<n-button @click="login">登录</n-button>
|
|
||||||
<n-button @click="router.push({name:'register'})">注册</n-button>
|
|
||||||
</div>
|
|
||||||
</n-form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import {computed, onMounted, ref} from "vue";
|
|
||||||
import {Theme} from "../theme";
|
|
||||||
import {useMessage} from 'naive-ui'
|
|
||||||
import api from "../api"
|
|
||||||
import {useRoute, useRouter} from "vue-router";
|
|
||||||
import {store} from "../store";
|
|
||||||
|
|
||||||
let msg = useMessage()
|
|
||||||
const route = useRoute()
|
|
||||||
const router = useRouter()
|
|
||||||
|
|
||||||
const divs = ref([])
|
|
||||||
let form_ref = ref(null)
|
|
||||||
let data = ref({
|
|
||||||
username: '',
|
|
||||||
password: ''
|
|
||||||
})
|
|
||||||
let rules = {
|
|
||||||
username: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
validator(r: any, v: any) {
|
|
||||||
return (v && v.length >= 3 && v.length <= 16) || new Error('长度要求3~16')
|
|
||||||
},
|
|
||||||
trigger: ['input', 'blur']
|
|
||||||
}
|
|
||||||
],
|
|
||||||
password: [{
|
|
||||||
required: true,
|
|
||||||
validator(r: any, v: any) {
|
|
||||||
return (v && v.length >= 6 && v.length <= 16) || new Error('长度要求6~16')
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
|
|
||||||
let uuid = computed(() => {
|
|
||||||
return route.params.uuid || store.state.oauuid
|
|
||||||
})
|
|
||||||
|
|
||||||
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) => {
|
|
||||||
msg.success('登录成功')
|
|
||||||
store.dispatch('user/fetchUserData')
|
|
||||||
let target = url
|
|
||||||
if (route.query.redirect) {
|
|
||||||
target = route.query.redirect as string
|
|
||||||
}
|
|
||||||
if (target && target.startsWith('http')) {
|
|
||||||
window.location.href = target
|
|
||||||
} else if (target) {
|
|
||||||
router.push(target)
|
|
||||||
} else {
|
|
||||||
router.push({name: 'home'})
|
|
||||||
}
|
|
||||||
}, e => {
|
|
||||||
console.log(e)
|
|
||||||
msg.warning('登录失败:' + e)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
if (divs.value[0]) {
|
|
||||||
// @ts-ignore
|
|
||||||
divs.value[0].focus()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
</style>
|
</style>
|
||||||
|
<template>
|
||||||
|
<v-row align="center" class="fill-height" justify="center" style="background: #ebebeb">
|
||||||
|
<v-col cols="12" sm="8" md="6" lg="4" xl="3">
|
||||||
|
<v-card class="elevation-12 mx-5" style="opacity: 0.8">
|
||||||
|
<v-row justify="center">
|
||||||
|
<v-col cols="10">
|
||||||
|
<v-card class="elevation-1 mt-n12 primary theme--dark">
|
||||||
|
<v-card-text class="text-center">
|
||||||
|
<h1 class="display-2 font-weight-bold mb-2">Login</h1>
|
||||||
|
<v-tooltip left>
|
||||||
|
<template v-slot:activator="{ on }">
|
||||||
|
<v-btn icon large v-on="on">
|
||||||
|
<v-icon>mdi-cellphone</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
||||||
|
<span style="font-family:'Noto Sans Armenian'">手机登录</span>
|
||||||
|
</v-tooltip>
|
||||||
|
<v-tooltip right>
|
||||||
|
<template v-slot:activator="{ on }">
|
||||||
|
<v-btn icon large v-on="on">
|
||||||
|
<v-icon>mdi-barcode</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
||||||
|
<span>授权码登录</span>
|
||||||
|
</v-tooltip>
|
||||||
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
<v-card-text>
|
||||||
|
<v-form ref="form">
|
||||||
|
<v-text-field
|
||||||
|
v-model="formInline.user"
|
||||||
|
:counter="16"
|
||||||
|
:rules="ruleInline.user"
|
||||||
|
label="账号"
|
||||||
|
required
|
||||||
|
prepend-inner-icon="mdi-account-circle"
|
||||||
|
></v-text-field>
|
||||||
|
<v-text-field
|
||||||
|
v-model="formInline.password"
|
||||||
|
type="password"
|
||||||
|
:counter="16"
|
||||||
|
:rules="ruleInline.password"
|
||||||
|
label="密码"
|
||||||
|
prepend-inner-icon="mdi-lock"
|
||||||
|
@keyup.enter="handleSubmit"
|
||||||
|
required
|
||||||
|
></v-text-field>
|
||||||
|
</v-form>
|
||||||
|
</v-card-text>
|
||||||
|
<v-card-actions>
|
||||||
|
<v-spacer/>
|
||||||
|
<v-btn type="primary" @click="handleSubmit">登录</v-btn>
|
||||||
|
<router-link :to="{name: 'register', query:$route.query, params: $route.params}"
|
||||||
|
style="text-decoration: none;">
|
||||||
|
<v-btn type="primary" style="margin-left:8px">注册</v-btn>
|
||||||
|
</router-link>
|
||||||
|
</v-card-actions>
|
||||||
|
</v-card>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang='ts'>
|
||||||
|
import {Component, Vue} from 'vue-property-decorator'
|
||||||
|
import util from '@/libs/util'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
components: {}
|
||||||
|
})
|
||||||
|
export default class Login extends Vue {
|
||||||
|
formInline = {
|
||||||
|
user: '',
|
||||||
|
password: ''
|
||||||
|
}
|
||||||
|
|
||||||
|
ruleInline = {
|
||||||
|
user: [
|
||||||
|
(v: string) => !!v || 'required',
|
||||||
|
(v: string) => (v && v.length >= 3 && v.length <= 16) || '长度要求3~16'
|
||||||
|
],
|
||||||
|
password: [
|
||||||
|
(v: string) => !!v || 'required',
|
||||||
|
(v: string) => (v && v.length >= 6 && v.length <= 16) || '长度要求6~16'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
get app_uuid() {
|
||||||
|
return this.$route.params.uuid || this.$store.state.oauuid
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSubmit() {
|
||||||
|
// eslint-disable-next-line
|
||||||
|
// @ts-ignore
|
||||||
|
if (!this.$refs.form.validate()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.$api.user.login(this.formInline.user, this.formInline.password, this.app_uuid).Start(
|
||||||
|
data => {
|
||||||
|
console.log(data)
|
||||||
|
if (util.checkLogin()) {
|
||||||
|
// this.$message.success('登录成功')
|
||||||
|
// EventBus.$emit('login', true)
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (this.$route.query.redirect) {
|
||||||
|
window.location.href = this.$route.query.redirect as string
|
||||||
|
}
|
||||||
|
this.$router.push({name: 'home'})
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// this.$message.error('用户名或密码错误')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
// this.$message.error('网络错误!')
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|||||||
@ -1,10 +1,124 @@
|
|||||||
|
<style>
|
||||||
|
</style>
|
||||||
<template>
|
<template>
|
||||||
<div></div>
|
<v-row class="fill-height" align="center" justify="center" style="background: #ebebeb">
|
||||||
|
<v-col cols="12" sm="8" md="6" lg="4" xl="3">
|
||||||
|
<v-card class="elevation-12 mx-5" style="opacity: 0.8">
|
||||||
|
<v-row justify="center">
|
||||||
|
<v-card class="elevation-1 mt-n7 primary" style="width: 80%">
|
||||||
|
<v-card-actions>
|
||||||
|
<v-row>
|
||||||
|
<v-icon
|
||||||
|
style="position: absolute;left: 10px;top:19px;z-index: 1"
|
||||||
|
@click="$router.back()"
|
||||||
|
size="36"
|
||||||
|
>mdi-arrow-left-circle
|
||||||
|
</v-icon>
|
||||||
|
<v-col cols="12" class="text-center">
|
||||||
|
<h1 class="display-2 ">注册</h1>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-card-actions>
|
||||||
|
</v-card>
|
||||||
|
</v-row>
|
||||||
|
<v-card-text class="text-center">
|
||||||
|
<v-form ref="form">
|
||||||
|
<v-text-field
|
||||||
|
type="text"
|
||||||
|
prepend-inner-icon="mdi-account-circle"
|
||||||
|
v-model="form.username"
|
||||||
|
label="账号"
|
||||||
|
:rules="ruleInline.user"
|
||||||
|
:counter="16"
|
||||||
|
>
|
||||||
|
</v-text-field>
|
||||||
|
<v-text-field
|
||||||
|
type="password"
|
||||||
|
v-model="form.passwd"
|
||||||
|
label="密码"
|
||||||
|
prepend-inner-icon="mdi-lock"
|
||||||
|
:rules="ruleInline.password"
|
||||||
|
:counter="16"
|
||||||
|
></v-text-field>
|
||||||
|
<v-text-field
|
||||||
|
type="password"
|
||||||
|
v-model="form.passwdCheck"
|
||||||
|
label="密码"
|
||||||
|
prepend-inner-icon="mdi-lock"
|
||||||
|
:rules="ruleInline.passwordCheck"
|
||||||
|
:counter="16"
|
||||||
|
@keyup.enter="handleSubmit"
|
||||||
|
></v-text-field>
|
||||||
|
</v-form>
|
||||||
|
</v-card-text>
|
||||||
|
<v-card-actions>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-btn type="primary" @click="handleSubmit">提交</v-btn>
|
||||||
|
<v-btn @click="handleReset()">重置</v-btn>
|
||||||
|
</v-card-actions>
|
||||||
|
</v-card>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang='ts'>
|
||||||
|
import {Component, Vue} from 'vue-property-decorator'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
components: {}
|
||||||
|
})
|
||||||
|
export default class Register extends Vue {
|
||||||
|
form = {
|
||||||
|
passwd: '',
|
||||||
|
passwdCheck: '',
|
||||||
|
email: '',
|
||||||
|
username: ''
|
||||||
|
}
|
||||||
|
|
||||||
|
ruleInline = {
|
||||||
|
user: [
|
||||||
|
(v: string) => !!v || 'required',
|
||||||
|
(v: string) => (v && v.length >= 3 && v.length <= 16) || '长度要求3~16'
|
||||||
|
],
|
||||||
|
password: [
|
||||||
|
(v: string) => !!v || 'required',
|
||||||
|
(v: string) => (v && v.length >= 6 && v.length <= 16) || '长度要求6~16'
|
||||||
|
],
|
||||||
|
passwordCheck: [
|
||||||
|
(v: string) => !!v || 'required',
|
||||||
|
(v: string) => (v && v === this.form.passwd) || '密码不一致'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
get app_uuid() {
|
||||||
|
return this.$route.params.uuid || this.$store.state.oauuid
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSubmit() {
|
||||||
|
// eslint-disable-next-line
|
||||||
|
// @ts-ignore
|
||||||
|
if (!this.$refs.form.validate()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.$api.user.register(this.form.username, this.form.passwd, this.app_uuid).Start(
|
||||||
|
(data) => {
|
||||||
|
this.$message.success('注册成功!')
|
||||||
|
this.$router.push({name: 'login', params: this.$route.params, query: this.$route.query})
|
||||||
|
},
|
||||||
|
(data) => {
|
||||||
|
if (data && data.code === '31011') {
|
||||||
|
this.$message.error('用户名重复')
|
||||||
|
} else {
|
||||||
|
this.$message.error('注册失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
handleReset() {
|
||||||
|
this.form.username = ''
|
||||||
|
this.form.passwd = ''
|
||||||
|
this.form.passwdCheck = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|||||||
@ -1,96 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="pt-10">
|
|
||||||
<div class="flex justify-center">
|
|
||||||
<div class="relative rounded-xl text-lg text-black" :style="{background: IsDark?'#555': '#d5d5d5'}">
|
|
||||||
<div @click="ifInfo=true" class="inline-block px-5 rounded-xl" :style="{background: ifInfo ? '#fc0005': ''}">
|
|
||||||
个人信息
|
|
||||||
</div>
|
|
||||||
<div @click="ifInfo=false" class="inline-block px-5 rounded-xl" :style="{background: ifInfo ? '': '#fc0005'}">
|
|
||||||
账户管理
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="inline-block flex justify-center mt-10">
|
|
||||||
<transition mode="out-in" enter-active-class="animate__fadeInLeft" leave-active-class="animate__fadeOutRight">
|
|
||||||
<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-form-item>
|
|
||||||
<n-form-item label="头像">
|
|
||||||
<n-upload
|
|
||||||
action=""
|
|
||||||
:headers="{'': ''}"
|
|
||||||
:data="{}"
|
|
||||||
>
|
|
||||||
<n-avatar size="large" round :src="user.icon">
|
|
||||||
</n-avatar>
|
|
||||||
</n-upload>
|
|
||||||
</n-form-item>
|
|
||||||
</n-form>
|
|
||||||
</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>
|
|
||||||
<n-form-item label="phone">
|
|
||||||
<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-form-item>
|
|
||||||
</n-form>
|
|
||||||
</div>
|
|
||||||
</transition>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</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";
|
|
||||||
|
|
||||||
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 emailOptions = computed(() => {
|
|
||||||
return ['@gmail.com', '@163.com', '@qq.com'].map((suffix) => {
|
|
||||||
const prefix = user.value.email.split('@')[0]
|
|
||||||
return {
|
|
||||||
label: prefix + suffix,
|
|
||||||
value: prefix + suffix
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
function update(key: string) {
|
|
||||||
// @ts-ignore
|
|
||||||
let v = user.value[key]
|
|
||||||
if (v === store.state.user[key]) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
api.user.update(store.state.user.id, {[key]: v}).Start(e => {
|
|
||||||
msg.success('更新成功')
|
|
||||||
store.state.user[key] = v
|
|
||||||
}, e => {
|
|
||||||
msg.error('更新失败: ' + e.err)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
</style>
|
|
||||||
@ -1,49 +1,64 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class='home d-flex justify-center align-center'>
|
<div class='home d-flex justify-center align-center'>
|
||||||
<wx-login v-if="enable" :aid="aid" :app="agentID" :url="url"></wx-login>
|
<wx-login v-if="enable" :aid="aid" :app="agentID" :url="url"></wx-login>
|
||||||
|
<v-overlay :value="!enable">
|
||||||
|
<v-progress-circular
|
||||||
|
indeterminate
|
||||||
|
size="64"
|
||||||
|
></v-progress-circular>
|
||||||
|
</v-overlay>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang='ts'>
|
<script lang='ts'>
|
||||||
import WxLogin from '../components/WxLogin.vue'
|
import {Component, Vue} from 'vue-property-decorator'
|
||||||
import {computed, onMounted} from "vue";
|
import WxLogin from '@/components/WxLogin.vue'
|
||||||
import {useRoute} from 'vue-router'
|
|
||||||
import api from '../api'
|
|
||||||
|
|
||||||
let route = useRoute()
|
@Component({
|
||||||
|
components: {
|
||||||
let aid = ''
|
WxLogin
|
||||||
let agentID = ''
|
|
||||||
let url = ''
|
|
||||||
let uuid = computed(() => {
|
|
||||||
return route.query.uuid
|
|
||||||
})
|
|
||||||
let enable = computed(() => {
|
|
||||||
return uuid && aid && agentID && url
|
|
||||||
})
|
|
||||||
let code = computed(() => {
|
|
||||||
return route.query.code
|
|
||||||
})
|
|
||||||
|
|
||||||
let state = computed(() => {
|
|
||||||
return route.query.state
|
|
||||||
})
|
|
||||||
|
|
||||||
let msg = computed(() => {
|
|
||||||
return route.query.msg
|
|
||||||
})
|
|
||||||
onMounted(() => {
|
|
||||||
if (msg) {
|
|
||||||
console.log(msg)
|
|
||||||
alert(msg)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
export default class Wx extends Vue {
|
||||||
|
aid = ''
|
||||||
|
agentID = ''
|
||||||
|
url = ''
|
||||||
|
|
||||||
if (uuid) {
|
get enable() {
|
||||||
api.app.get(uuid.value as string).Start(e => {
|
return this.uuid && this.aid && this.agentID && this.url
|
||||||
url = e.wx.url + '/api/wx/login/' + uuid
|
}
|
||||||
aid = e.wx.corp_id
|
|
||||||
agentID = e.wx.agent_id
|
get uuid() {
|
||||||
})
|
return this.$route.query.uuid
|
||||||
|
}
|
||||||
|
|
||||||
|
get code() {
|
||||||
|
return this.$route.query.code
|
||||||
|
}
|
||||||
|
|
||||||
|
get state() {
|
||||||
|
return this.$route.query.state
|
||||||
|
}
|
||||||
|
|
||||||
|
get msg() {
|
||||||
|
return this.$route.query.msg
|
||||||
|
}
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
if (this.msg) {
|
||||||
|
console.log(this.msg)
|
||||||
|
alert(this.msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
created() {
|
||||||
|
if (this.uuid) {
|
||||||
|
this.$api.app.get(this.uuid as string).Start(e => {
|
||||||
|
this.url = e.wx.url + '/api/wx/login/' + this.uuid
|
||||||
|
this.aid = e.wx.corp_id
|
||||||
|
this.agentID = e.wx.agent_id
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
14
oaf/src/vuex.d.ts
vendored
14
oaf/src/vuex.d.ts
vendored
@ -1,14 +0,0 @@
|
|||||||
import {ComponentCustomProperties} from 'vue'
|
|
||||||
import {Store} from 'vuex'
|
|
||||||
import {State as root} from './store'
|
|
||||||
|
|
||||||
declare module '@vue/runtime-core' {
|
|
||||||
// 声明自己的 store state
|
|
||||||
interface State extends root {
|
|
||||||
}
|
|
||||||
|
|
||||||
// 为 `this.$store` 提供类型声明
|
|
||||||
interface ComponentCustomProperties {
|
|
||||||
$store: Store<State>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
purge: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
|
|
||||||
darkMode: false, // or 'media' or 'class'
|
|
||||||
theme: {
|
|
||||||
extend: {},
|
|
||||||
},
|
|
||||||
variants: {
|
|
||||||
extend: {},
|
|
||||||
},
|
|
||||||
plugins: [],
|
|
||||||
}
|
|
||||||
@ -1,15 +1,40 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "esnext",
|
"target": "esnext",
|
||||||
"useDefineForClassFields": true,
|
|
||||||
"module": "esnext",
|
"module": "esnext",
|
||||||
"moduleResolution": "node",
|
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"jsx": "preserve",
|
"jsx": "preserve",
|
||||||
"sourceMap": true,
|
"importHelpers": true,
|
||||||
"resolveJsonModule": true,
|
"moduleResolution": "node",
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"lib": ["esnext", "dom"]
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"baseUrl": ".",
|
||||||
|
"types": [
|
||||||
|
"webpack-env"
|
||||||
|
],
|
||||||
|
"paths": {
|
||||||
|
"@/*": [
|
||||||
|
"src/*"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"lib": [
|
||||||
|
"esnext",
|
||||||
|
"dom",
|
||||||
|
"dom.iterable",
|
||||||
|
"scripthost"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
|
"include": [
|
||||||
|
"src/**/*.ts",
|
||||||
|
"src/**/*.tsx",
|
||||||
|
"src/**/*.vue",
|
||||||
|
"tests/**/*.ts",
|
||||||
|
"tests/**/*.tsx"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,36 +0,0 @@
|
|||||||
import {defineConfig} from 'vite'
|
|
||||||
import vue from '@vitejs/plugin-vue'
|
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [vue()],
|
|
||||||
server: {
|
|
||||||
// host: '0.0.0.0',
|
|
||||||
host: '127.0.0.1',
|
|
||||||
port: 8080,
|
|
||||||
proxy: {
|
|
||||||
'/api': {
|
|
||||||
target: 'http://127.0.0.1:4001/',
|
|
||||||
changeOrigin: true,
|
|
||||||
ws: true
|
|
||||||
},
|
|
||||||
'/media': {
|
|
||||||
target: 'http://127.0.0.1:4001/',
|
|
||||||
changeOrigin: true,
|
|
||||||
ws: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
build: {
|
|
||||||
outDir: '../sub/static/',
|
|
||||||
assetsDir: './',
|
|
||||||
rollupOptions: {
|
|
||||||
output: {
|
|
||||||
// 重点在这里哦
|
|
||||||
entryFileNames: `static/[name].[hash].js`,
|
|
||||||
chunkFileNames: `static/[name].[hash].js`,
|
|
||||||
assetFileNames: `static/[name].[hash].[ext]`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
28
oaf/vue.config.js
Normal file
28
oaf/vue.config.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
module.exports = {
|
||||||
|
transpileDependencies: [
|
||||||
|
'vuetify'
|
||||||
|
],
|
||||||
|
configureWebpack: {
|
||||||
|
output: {
|
||||||
|
filename: '[name].[hash].js'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
outputDir: '../sub/static',
|
||||||
|
devServer: {
|
||||||
|
host: '0.0.0.0',
|
||||||
|
port: 19520,
|
||||||
|
disableHostCheck: true,
|
||||||
|
proxy: {
|
||||||
|
'^/api': {
|
||||||
|
target: 'http://127.0.0.1:4001',
|
||||||
|
ws: true,
|
||||||
|
changeOrigin: true
|
||||||
|
},
|
||||||
|
'^/media': {
|
||||||
|
target: 'http://127.0.0.1:4001',
|
||||||
|
ws: true,
|
||||||
|
changeOrigin: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
10143
oaf/yarn.lock
10143
oaf/yarn.lock
File diff suppressed because it is too large
Load Diff
26
sub/init.go
26
sub/init.go
@ -5,6 +5,7 @@ import (
|
|||||||
"OneAuth/libs/auth"
|
"OneAuth/libs/auth"
|
||||||
"OneAuth/models"
|
"OneAuth/models"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
|
"github.com/veypi/utils/cmd"
|
||||||
"github.com/veypi/utils/log"
|
"github.com/veypi/utils/log"
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
@ -19,7 +20,6 @@ func runInit(c *cli.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 初始化项目
|
// 初始化项目
|
||||||
var appid uint
|
|
||||||
|
|
||||||
func InitSystem() error {
|
func InitSystem() error {
|
||||||
db()
|
db()
|
||||||
@ -27,9 +27,15 @@ func InitSystem() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
appid = self.ID
|
cfg.CFG.APPID = self.ID
|
||||||
|
cfg.CFG.APPKey = self.Key
|
||||||
|
err = cmd.DumpCfg(cfg.Path, cfg.CFG)
|
||||||
|
// TODO
|
||||||
|
//if err != nil {
|
||||||
|
// return err
|
||||||
|
//}
|
||||||
err = role(self.InitRoleID == 0)
|
err = role(self.InitRoleID == 0)
|
||||||
return err
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func db() {
|
func db() {
|
||||||
@ -50,14 +56,14 @@ func selfApp() (*models.App, error) {
|
|||||||
self := &models.App{
|
self := &models.App{
|
||||||
Name: "OA",
|
Name: "OA",
|
||||||
Icon: "",
|
Icon: "",
|
||||||
UUID: cfg.CFG.APPUUID,
|
UUID: "jU5Jo5hM",
|
||||||
Des: "",
|
Des: "",
|
||||||
Creator: 0,
|
Creator: 0,
|
||||||
UserCount: 0,
|
UserCount: 0,
|
||||||
Hide: false,
|
Hide: false,
|
||||||
Host: "",
|
Host: "",
|
||||||
UserRefreshUrl: "/",
|
UserRefreshUrl: "/",
|
||||||
Key: cfg.CFG.APPKey,
|
Key: "cB43wF94MLTksyBK",
|
||||||
EnableRegister: true,
|
EnableRegister: true,
|
||||||
EnableUserKey: true,
|
EnableUserKey: true,
|
||||||
EnableUser: true,
|
EnableUser: true,
|
||||||
@ -80,7 +86,7 @@ func role(reset_init_role bool) error {
|
|||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
adminRole := &models.Role{
|
adminRole := &models.Role{
|
||||||
AppID: appid,
|
AppID: cfg.CFG.APPID,
|
||||||
Name: "admin",
|
Name: "admin",
|
||||||
IsUnique: false,
|
IsUnique: false,
|
||||||
}
|
}
|
||||||
@ -90,7 +96,7 @@ func role(reset_init_role bool) error {
|
|||||||
}
|
}
|
||||||
for _, na := range n {
|
for _, na := range n {
|
||||||
a := &models.Resource{
|
a := &models.Resource{
|
||||||
AppID: appid,
|
AppID: cfg.CFG.APPID,
|
||||||
Name: na,
|
Name: na,
|
||||||
Tag: "",
|
Tag: "",
|
||||||
Des: "",
|
Des: "",
|
||||||
@ -106,7 +112,7 @@ func role(reset_init_role bool) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
userRole := &models.Role{
|
userRole := &models.Role{
|
||||||
AppID: appid,
|
AppID: cfg.CFG.APPID,
|
||||||
Name: "user",
|
Name: "user",
|
||||||
IsUnique: false,
|
IsUnique: false,
|
||||||
}
|
}
|
||||||
@ -114,12 +120,12 @@ func role(reset_init_role bool) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = auth.BindRoleAuth(cfg.DB(), userRole.ID, authMap[auth.APP].ID, models.AuthRead, strconv.Itoa(int(appid)))
|
err = auth.BindRoleAuth(cfg.DB(), userRole.ID, authMap[auth.APP].ID, models.AuthRead, strconv.Itoa(int(cfg.CFG.APPID)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if reset_init_role {
|
if reset_init_role {
|
||||||
return cfg.DB().Model(&models.App{}).Where("id = ?", appid).Update("init_role_id", adminRole.ID).Error
|
return cfg.DB().Model(&models.App{}).Where("id = ?", cfg.CFG.APPID).Update("init_role_id", adminRole.ID).Error
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,17 +1 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="/favicon.ico"><title>oaf</title><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mdi/font@latest/css/materialdesignicons.min.css"><link href="/css/chunk-6cfd0404.487f8d50.css" rel="prefetch"><link href="/js/about.a3055c06.js" rel="prefetch"><link href="/js/chunk-6cfd0404.65d5baaf.js" rel="prefetch"><link href="/app.b131f8d0f62acd99ab8e.js" rel="preload" as="script"><link href="/css/app.dafd5329.css" rel="preload" as="style"><link href="/css/chunk-vendors.dfe6062e.css" rel="preload" as="style"><link href="/js/chunk-vendors.83bac771.js" rel="preload" as="script"><link href="/css/chunk-vendors.dfe6062e.css" rel="stylesheet"><link href="/css/app.dafd5329.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but oaf doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="/js/chunk-vendors.83bac771.js"></script><script src="/app.b131f8d0f62acd99ab8e.js"></script></body></html>
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<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>
|
|
||||||
<script type="module" crossorigin src="/static/index.dddecd43.js"></script>
|
|
||||||
<link rel="modulepreload" href="/static/vendor.ba3bd51d.js">
|
|
||||||
<link rel="stylesheet" href="/static/vendor.3a295b6b.css">
|
|
||||||
<link rel="stylesheet" href="/static/index.c49db26f.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="app"></div>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
40
sub/web.go
40
sub/web.go
@ -6,15 +6,16 @@ import (
|
|||||||
"embed"
|
"embed"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
"github.com/veypi/OneBD"
|
"github.com/veypi/OneBD"
|
||||||
|
"github.com/veypi/OneBD/core"
|
||||||
|
"github.com/veypi/OneBD/rfc"
|
||||||
"github.com/veypi/utils/log"
|
"github.com/veypi/utils/log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed static/static
|
//go:embed static
|
||||||
var staticFiles embed.FS
|
var staticFiles embed.FS
|
||||||
|
|
||||||
//go:embed static/favicon.ico
|
|
||||||
var icon []byte
|
|
||||||
|
|
||||||
//go:embed static/index.html
|
//go:embed static/index.html
|
||||||
var indexFile []byte
|
var indexFile []byte
|
||||||
|
|
||||||
@ -36,14 +37,33 @@ func RunWeb(c *cli.Context) error {
|
|||||||
LoggerPath: cfg.CFG.LoggerPath,
|
LoggerPath: cfg.CFG.LoggerPath,
|
||||||
LoggerLevel: ll,
|
LoggerLevel: ll,
|
||||||
})
|
})
|
||||||
|
app.Router().EmbedFile("/", indexFile)
|
||||||
api.Router(app.Router().SubRouter("api"))
|
app.Router().EmbedDir("/", staticFiles, "static/")
|
||||||
|
|
||||||
// TODO media 文件需要检验权限
|
// TODO media 文件需要检验权限
|
||||||
app.Router().SubRouter("/media/").Static("/", cfg.CFG.MediaDir)
|
//app.Router().SubRouter("/media/").Static("/", cfg.CFG.EXEDir+"/media")
|
||||||
app.Router().EmbedDir("/static", staticFiles, "static/static/")
|
|
||||||
app.Router().EmbedFile("/favicon.ico", icon)
|
app.Router().SetNotFoundFunc(func(m core.Meta) {
|
||||||
app.Router().EmbedFile("/*", indexFile)
|
f, err := os.Open(cfg.CFG.EXEDir + "/static/index.html")
|
||||||
|
if err != nil {
|
||||||
|
m.WriteHeader(rfc.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
info, err := f.Stat()
|
||||||
|
if err != nil {
|
||||||
|
m.WriteHeader(rfc.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if info.IsDir() {
|
||||||
|
// TODO:: dir list
|
||||||
|
m.WriteHeader(rfc.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
http.ServeContent(m, m.Request(), info.Name(), info.ModTime(), f)
|
||||||
|
})
|
||||||
|
|
||||||
|
api.Router(app.Router().SubRouter("api"))
|
||||||
|
|
||||||
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…
x
Reference in New Issue
Block a user