update
This commit is contained in:
parent
935653ba28
commit
3d194e935d
4
.gitignore
vendored
4
.gitignore
vendored
@ -271,4 +271,6 @@ Sessionx.vim
|
|||||||
tags
|
tags
|
||||||
# Persistent undo
|
# Persistent undo
|
||||||
[._]*.un~
|
[._]*.un~
|
||||||
|
oa.db
|
||||||
|
static
|
||||||
|
OneAuth
|
||||||
|
|||||||
22
api/api.go
Normal file
22
api/api.go
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"OneAuth/api/app"
|
||||||
|
"OneAuth/api/user"
|
||||||
|
"OneAuth/api/wx"
|
||||||
|
"github.com/veypi/OneBD"
|
||||||
|
"github.com/veypi/OneBD/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Router(r OneBD.Router) {
|
||||||
|
r.SetNotFoundFunc(func(m core.Meta) {
|
||||||
|
m.Write([]byte("{\"status\": 0}"))
|
||||||
|
})
|
||||||
|
r.SetInternalErrorFunc(func(m core.Meta) {
|
||||||
|
m.Write([]byte("{\"status\": 0}"))
|
||||||
|
})
|
||||||
|
user.Router(r.SubRouter("/auth/user"))
|
||||||
|
wx.Router(r.SubRouter("wx"))
|
||||||
|
app.Router(r.SubRouter("app"))
|
||||||
|
//message.Router(r.SubRouter("/message"))
|
||||||
|
}
|
||||||
39
api/app/app.go
Normal file
39
api/app/app.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"OneAuth/cfg"
|
||||||
|
"OneAuth/libs/base"
|
||||||
|
"OneAuth/libs/oerr"
|
||||||
|
"OneAuth/models"
|
||||||
|
"github.com/veypi/OneBD"
|
||||||
|
"github.com/veypi/OneBD/rfc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Router(r OneBD.Router) {
|
||||||
|
r.Set("/:id", appHandlerP, rfc.MethodGet)
|
||||||
|
}
|
||||||
|
|
||||||
|
var appHandlerP = OneBD.NewHandlerPool(func() OneBD.Handler {
|
||||||
|
h := &appHandler{}
|
||||||
|
h.Ignore(rfc.MethodGet)
|
||||||
|
return h
|
||||||
|
})
|
||||||
|
|
||||||
|
type appHandler struct {
|
||||||
|
base.ApiHandler
|
||||||
|
query *models.App
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *appHandler) Get() (interface{}, error) {
|
||||||
|
id := h.Meta().Params("id")
|
||||||
|
if id == "" {
|
||||||
|
return nil, oerr.ApiArgsMissing
|
||||||
|
}
|
||||||
|
h.query = &models.App{}
|
||||||
|
h.query.UUID = id
|
||||||
|
err := cfg.DB().Where(h.query).Preload("Wx").First(h.query).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return h.query, nil
|
||||||
|
}
|
||||||
22
api/role/auth.go
Normal file
22
api/role/auth.go
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package role
|
||||||
|
|
||||||
|
import (
|
||||||
|
"OneAuth/cfg"
|
||||||
|
"OneAuth/libs/base"
|
||||||
|
"OneAuth/models"
|
||||||
|
"github.com/veypi/OneBD"
|
||||||
|
"github.com/veypi/OneBD/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
var authP = OneBD.NewHandlerPool(func() core.Handler {
|
||||||
|
return &authHandler{}
|
||||||
|
})
|
||||||
|
|
||||||
|
type authHandler struct {
|
||||||
|
base.ApiHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *authHandler) Get() (interface{}, error) {
|
||||||
|
l := make([]*models.Auth, 0, 10)
|
||||||
|
return &l, cfg.DB().Find(&l).Error
|
||||||
|
}
|
||||||
138
api/role/role.go
Normal file
138
api/role/role.go
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
package role
|
||||||
|
|
||||||
|
import (
|
||||||
|
"OneAuth/cfg"
|
||||||
|
"OneAuth/libs/base"
|
||||||
|
"OneAuth/libs/oerr"
|
||||||
|
"OneAuth/models"
|
||||||
|
"errors"
|
||||||
|
"github.com/veypi/OneBD"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
var roleP = OneBD.NewHandlerPool(func() OneBD.Handler {
|
||||||
|
return &roleHandler{}
|
||||||
|
})
|
||||||
|
|
||||||
|
type roleHandler struct {
|
||||||
|
base.ApiHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *roleHandler) Get() (interface{}, error) {
|
||||||
|
id := h.Meta().ParamsInt("id")
|
||||||
|
aid := h.Meta().ParamsInt("aid")
|
||||||
|
act := h.Meta().Params("action")
|
||||||
|
if id > 0 {
|
||||||
|
role := &models.Role{}
|
||||||
|
role.ID = uint(id)
|
||||||
|
err := cfg.DB().Preload("Auths").Preload("Users").First(role).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if aid <= 0 {
|
||||||
|
return role, nil
|
||||||
|
}
|
||||||
|
if !h.CheckAuth("admin", "").CanDoAny() {
|
||||||
|
return nil, oerr.NoAuth
|
||||||
|
}
|
||||||
|
at := &models.RoleAuth{}
|
||||||
|
at.RoleID = role.ID
|
||||||
|
at.AuthID = uint(aid)
|
||||||
|
defer models.SyncGlobalRoles()
|
||||||
|
if act == "bind" {
|
||||||
|
err = cfg.DB().Where(at).FirstOrCreate(at).Error
|
||||||
|
} else if act == "unbind" {
|
||||||
|
err = cfg.DB().Where(at).Delete(at).Error
|
||||||
|
}
|
||||||
|
return role, nil
|
||||||
|
}
|
||||||
|
roles := make([]*models.Role, 0, 10)
|
||||||
|
err := cfg.DB().Preload("Auths").Preload("Users").Find(&roles).Error
|
||||||
|
return roles, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *roleHandler) Post() (interface{}, error) {
|
||||||
|
if !h.CheckAuth("role", "").CanCreate() {
|
||||||
|
return nil, oerr.NoAuth
|
||||||
|
}
|
||||||
|
role := &models.Role{}
|
||||||
|
err := h.Meta().ReadJson(role)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
role.ID = 0
|
||||||
|
if role.Category == 0 || role.Name == "" {
|
||||||
|
return nil, oerr.ApiArgsMissing
|
||||||
|
}
|
||||||
|
defer models.SyncGlobalRoles()
|
||||||
|
return role, cfg.DB().Where(role).FirstOrCreate(role).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *roleHandler) Patch() (interface{}, error) {
|
||||||
|
if !h.CheckAuth("role", "").CanUpdate() {
|
||||||
|
return nil, oerr.NoAuth
|
||||||
|
}
|
||||||
|
query := &struct {
|
||||||
|
Name *string `json:"name"`
|
||||||
|
// 角色标签
|
||||||
|
Tag *string `json:"tag" gorm:"default:''"`
|
||||||
|
IsUnique *bool `json:"is_unique" gorm:"default:false"`
|
||||||
|
}{}
|
||||||
|
err := h.Meta().ReadJson(query)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
rid := h.Meta().ParamsInt("id")
|
||||||
|
if rid <= 0 {
|
||||||
|
return nil, oerr.ApiArgsError
|
||||||
|
}
|
||||||
|
role := &models.Role{}
|
||||||
|
role.ID = uint(rid)
|
||||||
|
err = cfg.DB().Preload("Users").Where(role).First(role).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return nil, cfg.DB().Transaction(func(tx *gorm.DB) error {
|
||||||
|
var err error
|
||||||
|
if query.Tag != nil && *query.Tag != role.Tag {
|
||||||
|
err = tx.Model(role).Update("tag", *query.Tag).Error
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if query.Name != nil && *query.Name != role.Name {
|
||||||
|
err = tx.Model(role).Update("name", *query.Name).Error
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if query.IsUnique != nil && *query.IsUnique != role.IsUnique {
|
||||||
|
if *query.IsUnique && len(role.Users) > 1 {
|
||||||
|
return errors.New("该角色绑定用户已超过1个,请解绑后在修改")
|
||||||
|
}
|
||||||
|
err = tx.Table("roles").Where("id = ?", role.ID).Update("is_unique", *query.IsUnique).Error
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *roleHandler) Delete() (interface{}, error) {
|
||||||
|
if !h.CheckAuth("role").CanDelete() {
|
||||||
|
return nil, oerr.NoAuth
|
||||||
|
}
|
||||||
|
rid := h.Meta().ParamsInt("id")
|
||||||
|
if rid <= 2 {
|
||||||
|
return nil, oerr.NoAuth
|
||||||
|
}
|
||||||
|
role := &models.Role{}
|
||||||
|
role.ID = uint(rid)
|
||||||
|
err := cfg.DB().Where(role).First(role).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer models.SyncGlobalRoles()
|
||||||
|
return nil, cfg.DB().Delete(role).Error
|
||||||
|
}
|
||||||
13
api/role/router.go
Normal file
13
api/role/router.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package role
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/veypi/OneBD"
|
||||||
|
"github.com/veypi/OneBD/rfc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Router(r OneBD.Router) {
|
||||||
|
r.Set("/role/", roleP, rfc.MethodGet, rfc.MethodPost)
|
||||||
|
r.Set("/role/:id", roleP, rfc.MethodGet, rfc.MethodDelete, rfc.MethodPatch)
|
||||||
|
r.Set("/role/:id/:action/:aid", roleP, rfc.MethodGet)
|
||||||
|
r.Set("/auth/", authP, rfc.MethodGet)
|
||||||
|
}
|
||||||
242
api/user/user.go
Normal file
242
api/user/user.go
Normal file
@ -0,0 +1,242 @@
|
|||||||
|
package user
|
||||||
|
|
||||||
|
import (
|
||||||
|
"OneAuth/cfg"
|
||||||
|
"OneAuth/libs/base"
|
||||||
|
"OneAuth/libs/oerr"
|
||||||
|
"OneAuth/models"
|
||||||
|
//"OneAuth/ws"
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"github.com/veypi/OneBD"
|
||||||
|
"github.com/veypi/OneBD/rfc"
|
||||||
|
"github.com/veypi/utils/log"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
"math/rand"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Router(r OneBD.Router) {
|
||||||
|
pool := OneBD.NewHandlerPool(func() OneBD.Handler {
|
||||||
|
h := &handler{}
|
||||||
|
h.Ignore(rfc.MethodHead)
|
||||||
|
return h
|
||||||
|
})
|
||||||
|
r.Set("/", pool, rfc.MethodGet, rfc.MethodPost) // list
|
||||||
|
r.Set("/:user_id", pool, rfc.MethodGet, rfc.MethodPatch, rfc.MethodHead, rfc.MethodDelete)
|
||||||
|
r.Set("/:user_id/role/", userRoleP, rfc.MethodPost)
|
||||||
|
r.Set("/:user_id/role/:role_id", userRoleP, rfc.MethodDelete)
|
||||||
|
//r.WS("/ws", func(m OneBD.Meta) (conn OneBD.WebsocketConn, err error) {
|
||||||
|
//return ws.User.Upgrade(m.ResponseWriter(), m.Request())
|
||||||
|
//})
|
||||||
|
}
|
||||||
|
|
||||||
|
type handler struct {
|
||||||
|
base.ApiHandler
|
||||||
|
User *models.User
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get get user data
|
||||||
|
func (h *handler) Get() (interface{}, error) {
|
||||||
|
username := h.Meta().Query("username")
|
||||||
|
if username != "" {
|
||||||
|
users := make([]*models.User, 0, 10)
|
||||||
|
err := cfg.DB().Preload("Scores").Preload("Roles.Auths").Where("username LIKE ? OR nickname LIKE ?", "%"+username+"%", "%"+username+"%").Find(&users).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return users, nil
|
||||||
|
}
|
||||||
|
userID := h.Meta().ParamsInt("user_id")
|
||||||
|
if userID != 0 {
|
||||||
|
user := &models.User{}
|
||||||
|
user.ID = uint(userID)
|
||||||
|
return user, cfg.DB().Where(user).Preload("Scores").Preload("Roles.Auths").Preload("Favorites").First(user).Error
|
||||||
|
} else {
|
||||||
|
users := make([]models.User, 10)
|
||||||
|
skip, err := strconv.Atoi(h.Meta().Query("skip"))
|
||||||
|
if err != nil || skip < 0 {
|
||||||
|
skip = 0
|
||||||
|
}
|
||||||
|
if err := cfg.DB().Preload("Scores").Preload("Roles.Auths").Offset(skip).Find(&users).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return users, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Post register user
|
||||||
|
func (h *handler) Post() (interface{}, error) {
|
||||||
|
if !h.CheckAuth("user").CanCreate() {
|
||||||
|
return nil, oerr.NoAuth
|
||||||
|
}
|
||||||
|
var userdata = struct {
|
||||||
|
Username string `json:"username"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
Nickname string `json:"nickname"`
|
||||||
|
Phone string `json:"phone"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
Domain string `json:"domain"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Position string `json:"position"`
|
||||||
|
}{}
|
||||||
|
if err := h.Meta().ReadJson(&userdata); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pass, err := base64.StdEncoding.DecodeString(userdata.Password)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(pass) > 32 || len(pass) < 6 {
|
||||||
|
return nil, oerr.PassError
|
||||||
|
}
|
||||||
|
|
||||||
|
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||||
|
h.User = new(models.User)
|
||||||
|
h.User.Icon = fmt.Sprintf("/media/icon/default/%04d.jpg", r.Intn(230))
|
||||||
|
h.User.Nickname = userdata.Nickname
|
||||||
|
h.User.Phone = userdata.Phone
|
||||||
|
h.User.Username = userdata.Username
|
||||||
|
h.User.Email = userdata.Email
|
||||||
|
h.User.Position = userdata.Position
|
||||||
|
if err := h.User.UpdateAuth(string(pass)); err != nil {
|
||||||
|
log.HandlerErrs(err)
|
||||||
|
return nil, oerr.ResourceCreatedFailed
|
||||||
|
}
|
||||||
|
tx := cfg.DB().Begin()
|
||||||
|
if err := tx.Create(&h.User).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return nil, oerr.ResourceDuplicated
|
||||||
|
}
|
||||||
|
tx.Commit()
|
||||||
|
return h.User, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Patch update user data
|
||||||
|
func (h *handler) Patch() (interface{}, error) {
|
||||||
|
uid := h.Meta().Params("user_id")
|
||||||
|
opts := struct {
|
||||||
|
Password string `json:"password"`
|
||||||
|
Nickname string `json:"nickname"`
|
||||||
|
Phone string `json:"phone" gorm:"type:varchar(20);unique;default:null" json:",omitempty"`
|
||||||
|
Email string `json:"email" gorm:"type:varchar(50);unique;default:null" json:",omitempty"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
Position string `json:"position"`
|
||||||
|
}{}
|
||||||
|
if err := h.Meta().ReadJson(&opts); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
target := models.User{}
|
||||||
|
if tempID, err := strconv.Atoi(uid); err != nil || tempID <= 0 {
|
||||||
|
return nil, oerr.ApiArgsError.Attach(err)
|
||||||
|
} else {
|
||||||
|
target.ID = uint(tempID)
|
||||||
|
}
|
||||||
|
tx := cfg.DB().Begin()
|
||||||
|
if err := cfg.DB().Where(&target).First(&target).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if target.ID != h.Payload.ID && !h.CheckAuth("admin").CanDoAny() {
|
||||||
|
return nil, oerr.NoAuth
|
||||||
|
}
|
||||||
|
if len(opts.Password) >= 6 {
|
||||||
|
if err := target.UpdateAuth(opts.Password); err != nil {
|
||||||
|
log.HandlerErrs(err)
|
||||||
|
return nil, oerr.ApiArgsError.AttachStr(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if opts.Nickname != "" {
|
||||||
|
target.Nickname = opts.Nickname
|
||||||
|
}
|
||||||
|
if opts.Position != "" {
|
||||||
|
target.Position = opts.Position
|
||||||
|
}
|
||||||
|
if opts.Phone != "" {
|
||||||
|
target.Phone = opts.Phone
|
||||||
|
}
|
||||||
|
if opts.Email != "" {
|
||||||
|
target.Email = opts.Email
|
||||||
|
}
|
||||||
|
if opts.Status != "" {
|
||||||
|
target.Status = opts.Status
|
||||||
|
}
|
||||||
|
if err := tx.Updates(&target).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tx.Commit()
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete delete user
|
||||||
|
func (h *handler) Delete() (interface{}, error) {
|
||||||
|
// TODO::
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Head user login
|
||||||
|
func (h *handler) Head() (interface{}, error) {
|
||||||
|
uid := h.Meta().Params("user_id")
|
||||||
|
pass, err := base64.StdEncoding.DecodeString(h.Meta().Query("password"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, oerr.ApiArgsError.Attach(err)
|
||||||
|
}
|
||||||
|
password := string(pass)
|
||||||
|
if len(uid) == 0 || len(password) == 0 {
|
||||||
|
return nil, oerr.ApiArgsError
|
||||||
|
}
|
||||||
|
h.User = new(models.User)
|
||||||
|
uidType := h.Meta().Query("uid_type")
|
||||||
|
switch uidType {
|
||||||
|
case "username":
|
||||||
|
h.User.Username = uid
|
||||||
|
case "phone":
|
||||||
|
h.User.Phone = uid
|
||||||
|
case "email":
|
||||||
|
h.User.Email = uid
|
||||||
|
default:
|
||||||
|
h.User.Username = uid
|
||||||
|
}
|
||||||
|
if err := cfg.DB().Preload("Roles").Where(h.User).First(h.User).Error; err != nil {
|
||||||
|
if err.Error() == gorm.ErrRecordNotFound.Error() {
|
||||||
|
// admin 登录自动注册
|
||||||
|
if h.User.Username == "admin" {
|
||||||
|
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||||
|
h.User.Icon = fmt.Sprintf("/media/icon/default/%04d.jpg", r.Intn(230))
|
||||||
|
err = h.User.UpdateAuth(password)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
role := &models.Role{}
|
||||||
|
role.ID = 1
|
||||||
|
h.User.Roles = []*models.Role{role}
|
||||||
|
err = cfg.DB().Create(h.User).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return nil, oerr.AccountNotExist
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.HandlerErrs(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
isAuth, err := h.User.CheckLogin(password)
|
||||||
|
if err != nil || !isAuth {
|
||||||
|
return nil, oerr.PassError.Attach(err)
|
||||||
|
}
|
||||||
|
if h.User.Status == "disabled" {
|
||||||
|
return nil, oerr.DisableLogin
|
||||||
|
}
|
||||||
|
token, err := h.User.GetToken(cfg.CFG.Key)
|
||||||
|
if err != nil {
|
||||||
|
log.HandlerErrs(err)
|
||||||
|
return nil, oerr.Unknown.Attach(err)
|
||||||
|
}
|
||||||
|
h.Meta().SetHeader("auth_token", token)
|
||||||
|
log.Info().Msg(h.User.Username + " login")
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
86
api/user/user_role.go
Normal file
86
api/user/user_role.go
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
package user
|
||||||
|
|
||||||
|
import (
|
||||||
|
"OneAuth/cfg"
|
||||||
|
"OneAuth/libs/base"
|
||||||
|
"OneAuth/libs/oerr"
|
||||||
|
"OneAuth/models"
|
||||||
|
"errors"
|
||||||
|
"github.com/veypi/OneBD"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
var userRoleP = OneBD.NewHandlerPool(func() OneBD.Handler {
|
||||||
|
return &userRoleHandler{}
|
||||||
|
})
|
||||||
|
|
||||||
|
type userRoleHandler struct {
|
||||||
|
base.ApiHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *userRoleHandler) Post() (interface{}, error) {
|
||||||
|
if !h.CheckAuth("role").CanCreate() {
|
||||||
|
return nil, oerr.NoAuth
|
||||||
|
}
|
||||||
|
uid := h.Meta().ParamsInt("user_id")
|
||||||
|
if uid <= 0 {
|
||||||
|
return nil, oerr.ApiArgsMissing
|
||||||
|
}
|
||||||
|
query := &models.Role{}
|
||||||
|
err := h.Meta().ReadJson(query)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if query.ID != 0 {
|
||||||
|
err = cfg.DB().First(query, query.ID).Error
|
||||||
|
} else if query.Name != "" {
|
||||||
|
err = cfg.DB().Where(map[string]interface{}{
|
||||||
|
"name": query.Name,
|
||||||
|
"category": query.Category,
|
||||||
|
"tag": query.Tag,
|
||||||
|
}).First(query).Error
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
err = cfg.DB().Create(query).Error
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return nil, oerr.ApiArgsMissing
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if query.IsUnique {
|
||||||
|
}
|
||||||
|
link := &models.UserRole{}
|
||||||
|
link.UserID = uint(uid)
|
||||||
|
link.RoleID = query.ID
|
||||||
|
err = cfg.DB().Transaction(func(tx *gorm.DB) (err error) {
|
||||||
|
if query.IsUnique {
|
||||||
|
err = tx.Where("role_id = ?", query.ID).Delete(models.UserRole{}).Error
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tx.Where(link).FirstOrCreate(link).Error
|
||||||
|
})
|
||||||
|
return link, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *userRoleHandler) Delete() (interface{}, error) {
|
||||||
|
if !h.CheckAuth("role").CanDelete() {
|
||||||
|
return nil, oerr.NoAuth
|
||||||
|
}
|
||||||
|
uid := h.Meta().ParamsInt("user_id")
|
||||||
|
id := h.Meta().ParamsInt("role_id")
|
||||||
|
if uid <= 0 || id <= 0 {
|
||||||
|
return nil, oerr.ApiArgsMissing
|
||||||
|
}
|
||||||
|
link := &models.UserRole{}
|
||||||
|
link.UserID = uint(uid)
|
||||||
|
link.RoleID = uint(id)
|
||||||
|
err := cfg.DB().Where(link).First(link).Error
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return nil, cfg.DB().Delete(link).Error
|
||||||
|
}
|
||||||
160
api/wx/login.go
Normal file
160
api/wx/login.go
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
package wx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"OneAuth/cfg"
|
||||||
|
"OneAuth/libs/tools"
|
||||||
|
"OneAuth/models"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/veypi/OneBD"
|
||||||
|
"github.com/veypi/OneBD/rfc"
|
||||||
|
"github.com/veypi/utils"
|
||||||
|
"github.com/veypi/utils/log"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var tokens = map[uint]string{
|
||||||
|
1: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
func login(m OneBD.Meta) {
|
||||||
|
var loc = ""
|
||||||
|
defer func() {
|
||||||
|
m.Header().Set("Location", loc)
|
||||||
|
log.Warn().Msg(loc)
|
||||||
|
m.WriteHeader(rfc.StatusPermanentRedirect)
|
||||||
|
}()
|
||||||
|
app := &models.App{
|
||||||
|
UUID: m.Params("id"),
|
||||||
|
}
|
||||||
|
err := cfg.DB().Preload("Wx").Where(app).First(app).Error
|
||||||
|
loc = fmt.Sprintf("/#/wx?uuid=%s&msg=", app.UUID)
|
||||||
|
if err != nil {
|
||||||
|
loc += err.Error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if app.Wx == nil {
|
||||||
|
loc += "微信登录未绑定"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if tokens[app.Wx.ID] == "" {
|
||||||
|
tokens[app.Wx.ID], err = requestCorpToken(app.Wx.CorpID, app.Wx.CorpSecret)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn().Msg("get corp token failed: " + err.Error())
|
||||||
|
loc += err.Error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
user, err := getUserID(tokens[app.Wx.ID], m.Query("code"))
|
||||||
|
if err != nil {
|
||||||
|
if strings.Contains(err.Error(), "access_token expired") {
|
||||||
|
tokens[app.Wx.ID], err = requestCorpToken(app.Wx.CorpID, app.Wx.CorpSecret)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn().Msg("refresh corp token failed: " + err.Error())
|
||||||
|
loc += err.Error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
user, err = getUserID(tokens[app.Wx.ID], m.Query("code"))
|
||||||
|
if err != nil {
|
||||||
|
log.Warn().Msg("get user token failed: " + err.Error())
|
||||||
|
loc += err.Error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Warn().Msg("get user token failed: " + err.Error())
|
||||||
|
loc += err.Error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
info, err := getUserInfo(tokens[app.Wx.ID], user)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn().Msg("get user info failed: " + err.Error())
|
||||||
|
loc += err.Error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Warn().Msgf("\ncode= %s\nstate= %s\nu = %s\n%v",
|
||||||
|
m.Query("code"), m.Query("state"), user, info)
|
||||||
|
pass, err := utils.AesEncrypt(fmt.Sprintf("%s.%d", user, time.Now().Unix()), []byte(app.UUID))
|
||||||
|
if err != nil {
|
||||||
|
loc += err.Error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Warn().Msgf("pass: %s", pass)
|
||||||
|
v := url.Values{}
|
||||||
|
v.Add("wid", pass)
|
||||||
|
u, err := url.Parse(app.Host)
|
||||||
|
u.RawQuery = v.Encode()
|
||||||
|
if err != nil {
|
||||||
|
loc += err.Error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
loc = u.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func requestCorpToken(corpid, corpsecret string) (string, error) {
|
||||||
|
addr := "https://qyapi.weixin.qq.com/cgi-bin/gettoken"
|
||||||
|
query := map[string]string{
|
||||||
|
"corpid": corpid,
|
||||||
|
"corpsecret": corpsecret,
|
||||||
|
}
|
||||||
|
res := &struct {
|
||||||
|
Errmsg string `json:"errmsg"`
|
||||||
|
Errcode *uint `json:"errcode"`
|
||||||
|
AccessToken string `json:"access_token"`
|
||||||
|
}{}
|
||||||
|
err := tools.Query(addr, query, res)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.New("request token response json parse err :" + err.Error())
|
||||||
|
}
|
||||||
|
if res.Errcode != nil && *res.Errcode == 0 {
|
||||||
|
return res.AccessToken, nil
|
||||||
|
} else {
|
||||||
|
//返回错误信息
|
||||||
|
err = errors.New(fmt.Sprintf("%d:%s", res.Errcode, res.Errmsg))
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getUserID(token, code string) (string, error) {
|
||||||
|
addr := "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo"
|
||||||
|
res := &struct {
|
||||||
|
Errmsg string `json:"errmsg"`
|
||||||
|
Errcode *uint `json:"errcode"`
|
||||||
|
UserId string `json:"UserId"`
|
||||||
|
DeviceId string `json:"device_id"`
|
||||||
|
}{}
|
||||||
|
query := map[string]string{
|
||||||
|
"access_token": token,
|
||||||
|
"code": code,
|
||||||
|
}
|
||||||
|
err := tools.Query(addr, query, res)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if res.Errcode != nil && *res.Errcode == 0 {
|
||||||
|
return res.UserId, nil
|
||||||
|
}
|
||||||
|
return "", errors.New(fmt.Sprintf("%d:%s", res.Errcode, res.Errmsg))
|
||||||
|
}
|
||||||
|
|
||||||
|
func getUserInfo(token, id string) (interface{}, error) {
|
||||||
|
addr := "https://qyapi.weixin.qq.com/cgi-bin/user/get"
|
||||||
|
res := map[string]interface{}{}
|
||||||
|
query := map[string]string{
|
||||||
|
"access_token": token,
|
||||||
|
"userid": id,
|
||||||
|
}
|
||||||
|
err := tools.Query(addr, query, &res)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
errcode := int(res["errcode"].(float64))
|
||||||
|
errmsg := res["errmsg"].(string)
|
||||||
|
if errcode == 0 {
|
||||||
|
log.Warn().Msgf("%v", res)
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
return "", errors.New(fmt.Sprintf("%d:%s", errcode, errmsg))
|
||||||
|
}
|
||||||
10
api/wx/router.go
Normal file
10
api/wx/router.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package wx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/veypi/OneBD"
|
||||||
|
"github.com/veypi/OneBD/rfc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Router(r OneBD.Router) {
|
||||||
|
r.Set("/login/:id", login, rfc.MethodGet)
|
||||||
|
}
|
||||||
77
cfg/cfg.go
Normal file
77
cfg/cfg.go
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
package cfg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/veypi/utils/cmd"
|
||||||
|
"gorm.io/driver/mysql"
|
||||||
|
"gorm.io/driver/sqlite"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
var Path = cmd.GetCfgPath("OneAuth", "oa")
|
||||||
|
|
||||||
|
var CFG = &struct {
|
||||||
|
AdminUser string
|
||||||
|
Host string
|
||||||
|
LoggerPath string
|
||||||
|
LoggerLevel string
|
||||||
|
Key string
|
||||||
|
TimeFormat string
|
||||||
|
Debug bool
|
||||||
|
EXEDir string
|
||||||
|
DB struct {
|
||||||
|
Type string
|
||||||
|
Addr string
|
||||||
|
User string
|
||||||
|
Pass string
|
||||||
|
DB string
|
||||||
|
}
|
||||||
|
}{
|
||||||
|
AdminUser: "admin",
|
||||||
|
Host: "0.0.0.0:19528",
|
||||||
|
LoggerPath: "",
|
||||||
|
LoggerLevel: "debug",
|
||||||
|
TimeFormat: "2006/01/02 15:04:05",
|
||||||
|
Debug: true,
|
||||||
|
EXEDir: "./",
|
||||||
|
DB: struct {
|
||||||
|
Type string
|
||||||
|
Addr string
|
||||||
|
User string
|
||||||
|
Pass string
|
||||||
|
DB string
|
||||||
|
}{
|
||||||
|
Type: "sqlite",
|
||||||
|
//Addr: "127.0.0.1:3306",
|
||||||
|
Addr: "oa.db",
|
||||||
|
User: "root",
|
||||||
|
Pass: "123456",
|
||||||
|
DB: "one_auth",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
db *gorm.DB
|
||||||
|
)
|
||||||
|
|
||||||
|
func DB() *gorm.DB {
|
||||||
|
if db == nil {
|
||||||
|
ConnectDB()
|
||||||
|
}
|
||||||
|
return db
|
||||||
|
}
|
||||||
|
func ConnectDB() *gorm.DB {
|
||||||
|
var err error
|
||||||
|
conn := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8&parseTime=True&loc=Local", CFG.DB.User, CFG.DB.Pass, CFG.DB.Addr, CFG.DB.DB)
|
||||||
|
if CFG.DB.Type == "sqlite" {
|
||||||
|
conn = CFG.DB.Addr
|
||||||
|
db, err = gorm.Open(sqlite.Open(conn), &gorm.Config{})
|
||||||
|
} else {
|
||||||
|
db, err = gorm.Open(mysql.Open(conn), &gorm.Config{})
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return db
|
||||||
|
}
|
||||||
14
go.mod
Normal file
14
go.mod
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
module OneAuth
|
||||||
|
|
||||||
|
go 1.16
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/json-iterator/go v1.1.10
|
||||||
|
github.com/urfave/cli/v2 v2.2.0
|
||||||
|
github.com/veypi/OneBD v0.4.1
|
||||||
|
github.com/veypi/utils v0.2.2
|
||||||
|
gorm.io/driver/mysql v1.0.5
|
||||||
|
gorm.io/driver/sqlite v1.1.4
|
||||||
|
gorm.io/gorm v1.21.3
|
||||||
|
)
|
||||||
|
replace github.com/veypi/OneBD v0.4.1 => ../OceanCurrent/OneBD
|
||||||
77
go.sum
Normal file
77
go.sum
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||||
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
|
||||||
|
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||||
|
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||||
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||||
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
|
github.com/jinzhu/now v1.1.1 h1:g39TucaRWyV3dwDO++eEc6qf8TVIQ/Da48WmqjZ3i7E=
|
||||||
|
github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
|
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
|
||||||
|
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
|
github.com/kardianos/service v1.1.0 h1:QV2SiEeWK42P0aEmGcsAgjApw/lRxkwopvT+Gu6t1/0=
|
||||||
|
github.com/kardianos/service v1.1.0/go.mod h1:RrJI2xn5vve/r32U5suTbeaSGoMU6GbNPoj36CVYcHc=
|
||||||
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.5 h1:1IdxlwTNazvbKJQSxoJ5/9ECbEeaTTyeU7sEAZ5KKTQ=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.5/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
||||||
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||||
|
github.com/rs/zerolog v1.17.2 h1:RMRHFw2+wF7LO0QqtELQwo8hqSmqISyCJeFeAAuWcRo=
|
||||||
|
github.com/rs/zerolog v1.17.2/go.mod h1:9nvC1axdVrAHcu/s9taAVfBuIdTZLVQmKQyvrUjF5+I=
|
||||||
|
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
||||||
|
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||||
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||||
|
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/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/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/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8=
|
||||||
|
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
||||||
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||||
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gorm.io/driver/mysql v1.0.5 h1:WAAmvLK2rG0tCOqrf5XcLi2QUwugd4rcVJ/W3aoon9o=
|
||||||
|
gorm.io/driver/mysql v1.0.5/go.mod h1:N1OIhHAIhx5SunkMGqWbGFVeh4yTNWKmMo1GOAsohLI=
|
||||||
|
gorm.io/driver/sqlite v1.1.4 h1:PDzwYE+sI6De2+mxAneV9Xs11+ZyKV6oxD3wDGkaNvM=
|
||||||
|
gorm.io/driver/sqlite v1.1.4/go.mod h1:mJCeTFr7+crvS+TRnWc5Z3UvwxUN1BGBLMrf5LA9DYw=
|
||||||
|
gorm.io/gorm v1.20.7/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
|
||||||
|
gorm.io/gorm v1.21.3 h1:qDFi55ZOsjZTwk5eN+uhAmHi8GysJ/qCTichM/yO7ME=
|
||||||
|
gorm.io/gorm v1.21.3/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
|
||||||
43
libs/auth/auth.go
Normal file
43
libs/auth/auth.go
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"OneAuth/cfg"
|
||||||
|
"OneAuth/libs/oerr"
|
||||||
|
"OneAuth/models"
|
||||||
|
"github.com/veypi/OneBD"
|
||||||
|
"github.com/veypi/OneBD/rfc"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Auth struct {
|
||||||
|
Payload *models.PayLoad
|
||||||
|
ignoreMethod map[rfc.Method]bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Auth) Init(m OneBD.Meta) error {
|
||||||
|
if a.ignoreMethod != nil && a.ignoreMethod[m.Method()] {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
a.Payload = new(models.PayLoad)
|
||||||
|
token := m.GetHeader("auth_token")
|
||||||
|
if token == "" {
|
||||||
|
return oerr.NotLogin
|
||||||
|
}
|
||||||
|
ok, err := models.ParseToken(token, cfg.CFG.Key, a.Payload)
|
||||||
|
if ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return oerr.NotLogin.Attach(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Auth) Ignore(methods ...rfc.Method) {
|
||||||
|
if a.ignoreMethod == nil {
|
||||||
|
a.ignoreMethod = make(map[rfc.Method]bool)
|
||||||
|
}
|
||||||
|
for _, m := range methods {
|
||||||
|
a.ignoreMethod[m] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Auth) CheckAuth(name string, tags ...string) models.AuthLevel {
|
||||||
|
return a.Payload.CheckAuth(name, tags...)
|
||||||
|
}
|
||||||
72
libs/base/api_handler.go
Normal file
72
libs/base/api_handler.go
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
package base
|
||||||
|
|
||||||
|
import (
|
||||||
|
"OneAuth/libs/auth"
|
||||||
|
"OneAuth/libs/oerr"
|
||||||
|
"OneAuth/libs/tools"
|
||||||
|
"github.com/json-iterator/go"
|
||||||
|
"github.com/veypi/OneBD"
|
||||||
|
"github.com/veypi/OneBD/rfc"
|
||||||
|
"github.com/veypi/utils/log"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var json = jsoniter.ConfigFastest
|
||||||
|
|
||||||
|
type ApiHandler struct {
|
||||||
|
OneBD.BaseHandler
|
||||||
|
auth.Auth
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *ApiHandler) Init(m OneBD.Meta) error {
|
||||||
|
return tools.MultiIniter(m, &h.BaseHandler, &h.Auth)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *ApiHandler) OnResponse(data interface{}) {
|
||||||
|
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) {
|
||||||
|
log.WithNoCaller.Warn().Err(err).Msg(h.Meta().RequestPath())
|
||||||
|
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 limitLocker = sync.RWMutex{}
|
||||||
|
|
||||||
|
func (h *ApiHandler) SetAccessDelta(d time.Duration) error {
|
||||||
|
// 尽量对写操作加频率限制
|
||||||
|
now := time.Now()
|
||||||
|
limitLocker.Lock()
|
||||||
|
label := h.Meta().RemoteAddr() + h.Meta().RequestPath()
|
||||||
|
last, ok := ioNumLimit[label]
|
||||||
|
defer func() {
|
||||||
|
ioNumLimit[label] = now
|
||||||
|
limitLocker.Unlock()
|
||||||
|
}()
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
} else if now.Sub(last) >= d {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return oerr.AccessTooFast
|
||||||
|
}
|
||||||
210
libs/oerr/oerr.go
Normal file
210
libs/oerr/oerr.go
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
package oerr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 错误描述
|
||||||
|
|
||||||
|
type Code uint
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
5位10进制码表示错误, 00000 etc.
|
||||||
|
|
||||||
|
0 代表未知,或不必定义的有通用意义的错误
|
||||||
|
|
||||||
|
## 第1位 错误类型
|
||||||
|
- 1 : 系统级错误 比如 内存申请失败, 系统调用失败,文件打开失败等等
|
||||||
|
- 2 : 数据库错误
|
||||||
|
- 3 : 保留
|
||||||
|
- 4 : 权限错误
|
||||||
|
- 5 : 配置错误
|
||||||
|
- 6 : 参数错误
|
||||||
|
- 7 : 时序(控制)错误
|
||||||
|
|
||||||
|
## 第2位 2级错误类型
|
||||||
|
|
||||||
|
## 第3,4位 具体错误编号
|
||||||
|
|
||||||
|
## 第5位 错误严重程度
|
||||||
|
- 0 : 无任何影响错误,简单重试可以解决
|
||||||
|
- 1 : 无影响错误,重试不可解决
|
||||||
|
- 2 : 有影响用户体验或系统性能错误, 重试可解决
|
||||||
|
- 3 : 有影响用户体验或系统性能错误, 重试不可解决
|
||||||
|
- 4 : 有影响组件功能的错误, 重试可解决
|
||||||
|
- 5 : 有影响组件功能的错误, 重试不可解决
|
||||||
|
- 6 : 有影响服务运行的错误, 重启可解决
|
||||||
|
- 7 : 有影响服务运行的错误,重启不可解决
|
||||||
|
- 8 : 有影响系统运行的错误
|
||||||
|
- 9 : 本不可能发生的错误,例如被人攻击导致数据异常产生的逻辑错误
|
||||||
|
|
||||||
|
*/
|
||||||
|
const (
|
||||||
|
Unknown Code = 0
|
||||||
|
)
|
||||||
|
const (
|
||||||
|
// 2 数据库错误
|
||||||
|
// -1 系统错误
|
||||||
|
// -2 数据读写错误
|
||||||
|
DBErr Code = 20001
|
||||||
|
ResourceCreatedFailed Code = 22012
|
||||||
|
ResourceDuplicated Code = 22021
|
||||||
|
ResourceNotExist Code = 22031
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// 3
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// 4 权限类型错误
|
||||||
|
// 1: 登录权限
|
||||||
|
// 2: 资源操作权限
|
||||||
|
NotLogin Code = 41001
|
||||||
|
LoginExpired Code = 41011
|
||||||
|
PassError Code = 41021
|
||||||
|
DisableLogin Code = 41031
|
||||||
|
AccountNotExist Code = 41041
|
||||||
|
NoAuth Code = 42011
|
||||||
|
)
|
||||||
|
|
||||||
|
// 6 参数类型错误
|
||||||
|
/*
|
||||||
|
-1: 协议参数
|
||||||
|
-2: 接口参数
|
||||||
|
-3: 函数参数
|
||||||
|
-4: 数据依赖错误
|
||||||
|
*/
|
||||||
|
const (
|
||||||
|
MethodNotSupport Code = 61111
|
||||||
|
MethodNotAllowed Code = 61121
|
||||||
|
|
||||||
|
ApiArgsError Code = 62001
|
||||||
|
ApiArgsMissing Code = 62011
|
||||||
|
TableArgsMissing Code = 62021
|
||||||
|
TableArgsErr Code = 62031
|
||||||
|
|
||||||
|
FuncArgsError Code = 63001
|
||||||
|
UrlPatternNotSupport Code = 63117
|
||||||
|
UrlDefinedDuplicate Code = 63127
|
||||||
|
UrlParamDuplicate Code = 63137
|
||||||
|
|
||||||
|
DataError Code = 64009
|
||||||
|
)
|
||||||
|
|
||||||
|
// 7 : 时序(控制)错误
|
||||||
|
/*
|
||||||
|
-1: 访问控制
|
||||||
|
*/
|
||||||
|
|
||||||
|
const (
|
||||||
|
AccessErr Code = 71001
|
||||||
|
AccessTooFast Code = 71010
|
||||||
|
)
|
||||||
|
|
||||||
|
var codeMap = map[Code]string{
|
||||||
|
Unknown: "unknown error",
|
||||||
|
DBErr: "db error",
|
||||||
|
ResourceCreatedFailed: "resource created failed",
|
||||||
|
ResourceDuplicated: "resource duplicated",
|
||||||
|
ResourceNotExist: "Resource not exist",
|
||||||
|
MethodNotSupport: "this http method is not supported",
|
||||||
|
MethodNotAllowed: "this http method is not allowed",
|
||||||
|
ApiArgsError: "base args error",
|
||||||
|
ApiArgsMissing: "missing args",
|
||||||
|
TableArgsMissing: "missing data",
|
||||||
|
TableArgsErr: "invalid table data",
|
||||||
|
FuncArgsError: "func args error",
|
||||||
|
UrlPatternNotSupport: "this router's url pattern is not supported.",
|
||||||
|
UrlDefinedDuplicate: "this router's url has been defined",
|
||||||
|
UrlParamDuplicate: "this param defined in router's url duplicated",
|
||||||
|
DataError: "data error",
|
||||||
|
NotLogin: "not login",
|
||||||
|
LoginExpired: "login expired",
|
||||||
|
DisableLogin: "disabled to login",
|
||||||
|
PassError: "password/account error",
|
||||||
|
AccountNotExist: "account not exist",
|
||||||
|
NoAuth: "no auth to access",
|
||||||
|
AccessErr: "access error",
|
||||||
|
AccessTooFast: "access too fast",
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Code) Error() string {
|
||||||
|
return strconv.Itoa(int(c)) + ":" + c.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Code) String() string {
|
||||||
|
s, ok := codeMap[c]
|
||||||
|
if ok && len(s) > 0 {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
return codeMap[Unknown]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 附加错误详细原因
|
||||||
|
func (c Code) Attach(errs ...error) (e error) {
|
||||||
|
e = c
|
||||||
|
for _, err := range errs {
|
||||||
|
if err != nil {
|
||||||
|
e = &wrapErr{msg: e.Error() + "\n" + err.Error(), err: e}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Code) AttachStr(errs ...string) (e error) {
|
||||||
|
e = c
|
||||||
|
for _, m := range errs {
|
||||||
|
if m != "" {
|
||||||
|
e = &wrapErr{
|
||||||
|
msg: e.Error() + "\n" + m,
|
||||||
|
err: e,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func OfType(errMsg string) Code {
|
||||||
|
s := ""
|
||||||
|
if gorm.ErrRecordNotFound.Error() == errMsg {
|
||||||
|
return ResourceNotExist
|
||||||
|
}
|
||||||
|
for _, v := range errMsg {
|
||||||
|
if v == ':' {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
s += string(v)
|
||||||
|
}
|
||||||
|
c, _ := strconv.Atoi(s)
|
||||||
|
return Code(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
type wrapErr struct {
|
||||||
|
msg string
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wrapErr) Error() string {
|
||||||
|
return w.msg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wrapErr) UnWrap() error {
|
||||||
|
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
|
||||||
|
}
|
||||||
44
libs/tools/tools.go
Normal file
44
libs/tools/tools.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package tools
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/veypi/OneBD"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Initer interface {
|
||||||
|
Init(OneBD.Meta) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func MultiIniter(m OneBD.Meta, is ...Initer) (err error) {
|
||||||
|
for _, i := range is {
|
||||||
|
err = i.Init(m)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func Query(addr string, query map[string]string, res interface{}) error {
|
||||||
|
u, err := url.Parse(addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
paras := &url.Values{}
|
||||||
|
//设置请求参数
|
||||||
|
for k, v := range query {
|
||||||
|
paras.Set(k, v)
|
||||||
|
}
|
||||||
|
u.RawQuery = paras.Encode()
|
||||||
|
resp, err := http.Get(u.String())
|
||||||
|
//关闭资源
|
||||||
|
if resp != nil && resp.Body != nil {
|
||||||
|
defer resp.Body.Close()
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return json.NewDecoder(resp.Body).Decode(res)
|
||||||
|
}
|
||||||
79
main.go
Normal file
79
main.go
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"OneAuth/cfg"
|
||||||
|
"OneAuth/sub"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
"github.com/veypi/utils/cmd"
|
||||||
|
"github.com/veypi/utils/log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
const Version = "v0.1.0"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
cmd.LoadCfg(cfg.Path, cfg.CFG)
|
||||||
|
app := cli.NewApp()
|
||||||
|
app.Name = "OneAuth"
|
||||||
|
app.Usage = "one auth"
|
||||||
|
app.Version = Version
|
||||||
|
app.Flags = []cli.Flag{
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "debug",
|
||||||
|
Aliases: []string{"d"},
|
||||||
|
Value: cfg.CFG.Debug,
|
||||||
|
Destination: &cfg.CFG.Debug,
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "log_level,log",
|
||||||
|
Value: cfg.CFG.LoggerLevel,
|
||||||
|
Destination: &cfg.CFG.LoggerLevel,
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "log_path",
|
||||||
|
Value: cfg.CFG.LoggerPath,
|
||||||
|
Destination: &cfg.CFG.LoggerPath,
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "key",
|
||||||
|
Value: cfg.CFG.Key,
|
||||||
|
Destination: &cfg.CFG.Key,
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "exe_dir",
|
||||||
|
Value: cfg.CFG.EXEDir,
|
||||||
|
Destination: &cfg.CFG.EXEDir,
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "host",
|
||||||
|
Value: cfg.CFG.Host,
|
||||||
|
Destination: &cfg.CFG.Host,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
app.Commands = []*cli.Command{
|
||||||
|
&sub.Web,
|
||||||
|
}
|
||||||
|
srv, err := cmd.NewSrv(app, sub.RunWeb, cfg.CFG, cfg.Path)
|
||||||
|
if err != nil {
|
||||||
|
log.Warn().Msg(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
srv.SetExecMax(1)
|
||||||
|
srv.SetStopFunc(func() {
|
||||||
|
})
|
||||||
|
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 {
|
||||||
|
cfg.CFG.LoggerLevel = "debug"
|
||||||
|
}
|
||||||
|
cfg.ConnectDB()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
_ = app.Run(os.Args)
|
||||||
|
|
||||||
|
}
|
||||||
22
models/app.go
Normal file
22
models/app.go
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
type App struct {
|
||||||
|
BaseModel
|
||||||
|
Name string `json:"name"`
|
||||||
|
UUID string `json:"uuid"`
|
||||||
|
Host string `json:"host"`
|
||||||
|
WxID string `json:"wx_id" gorm:""`
|
||||||
|
Wx *Wechat `json:"wx" gorm:"association_foreignkey:ID"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Wechat struct {
|
||||||
|
BaseModel
|
||||||
|
// 网页授权登录用
|
||||||
|
WxID string `json:"wx_id"`
|
||||||
|
AgentID string `json:"agent_id"`
|
||||||
|
Url string `json:"url"`
|
||||||
|
|
||||||
|
// 获取access_token用
|
||||||
|
CorpID string `json:"corp_id"`
|
||||||
|
CorpSecret string `json:"corp_secret"`
|
||||||
|
}
|
||||||
102
models/init.go
Normal file
102
models/init.go
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"OneAuth/cfg"
|
||||||
|
"bytes"
|
||||||
|
"database/sql/driver"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type JSON []byte
|
||||||
|
|
||||||
|
func (j JSON) Value() (driver.Value, error) {
|
||||||
|
if j.IsNull() {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return string(j), nil
|
||||||
|
}
|
||||||
|
func (j *JSON) Scan(value interface{}) error {
|
||||||
|
if value == nil {
|
||||||
|
*j = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
s, ok := value.([]byte)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("invalid scan source")
|
||||||
|
}
|
||||||
|
*j = append((*j)[0:0], s...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (j JSON) MarshalJSON() ([]byte, error) {
|
||||||
|
if j == nil {
|
||||||
|
return []byte("null"), nil
|
||||||
|
}
|
||||||
|
return j, nil
|
||||||
|
}
|
||||||
|
func (j *JSON) UnmarshalJSON(data []byte) error {
|
||||||
|
if j == nil {
|
||||||
|
return errors.New("null point exception")
|
||||||
|
}
|
||||||
|
*j = append((*j)[0:0], data...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (j JSON) IsNull() bool {
|
||||||
|
return len(j) == 0 || string(j) == "null"
|
||||||
|
}
|
||||||
|
func (j JSON) Equals(j1 JSON) bool {
|
||||||
|
return bytes.Equal(j, j1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// JSONTime custom json time
|
||||||
|
type JSONTime struct {
|
||||||
|
time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func Now() *JSONTime {
|
||||||
|
return &JSONTime{time.Now()}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON 实现它的json序列化方法
|
||||||
|
func (jt JSONTime) MarshalJSON() ([]byte, error) {
|
||||||
|
var stamp = fmt.Sprintf("\"%s\"", jt.Format(cfg.CFG.TimeFormat))
|
||||||
|
return []byte(stamp), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON 反序列化方法
|
||||||
|
func (jt *JSONTime) UnmarshalJSON(data []byte) (err error) {
|
||||||
|
now, err := time.ParseInLocation(`"`+cfg.CFG.TimeFormat+`"`, string(data), time.Local)
|
||||||
|
*jt = JSONTime{now}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value insert timestamp into mysql need this function.
|
||||||
|
func (jt JSONTime) Value() (driver.Value, error) {
|
||||||
|
var zeroTime time.Time
|
||||||
|
if jt.Time.UnixNano() == zeroTime.UnixNano() {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return jt.Time, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan value of time.Time
|
||||||
|
func (jt *JSONTime) Scan(v interface{}) error {
|
||||||
|
value, ok := v.(time.Time)
|
||||||
|
if ok {
|
||||||
|
*jt = JSONTime{Time: value}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("can not convert %v to timestamp", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (jt *JSONTime) SetTime(t time.Time) {
|
||||||
|
jt.Time = t
|
||||||
|
}
|
||||||
|
|
||||||
|
type BaseModel struct {
|
||||||
|
ID uint `json:"id" gorm:"primary_key"`
|
||||||
|
CreatedAt JSONTime `json:"created_at"`
|
||||||
|
UpdatedAt JSONTime `json:"updated_at"`
|
||||||
|
DeletedAt *JSONTime `json:"deleted_at" sql:"index"`
|
||||||
|
}
|
||||||
104
models/role.go
Normal file
104
models/role.go
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"OneAuth/cfg"
|
||||||
|
"github.com/veypi/utils/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
var GlobalRoles = make(map[uint]*Role)
|
||||||
|
|
||||||
|
func SyncGlobalRoles() {
|
||||||
|
roles := make([]*Role, 0, 10)
|
||||||
|
err := cfg.DB().Preload("Auths").Find(&roles).Error
|
||||||
|
if err != nil {
|
||||||
|
log.Warn().Msgf("sync global roles error: %s", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, r := range roles {
|
||||||
|
GlobalRoles[r.ID] = r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserRole struct {
|
||||||
|
BaseModel
|
||||||
|
UserID uint `json:"user_id"`
|
||||||
|
RoleID uint `json:"role_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RoleAuth struct {
|
||||||
|
BaseModel
|
||||||
|
RoleID uint `json:"role_id"`
|
||||||
|
AuthID uint `json:"auth_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Role struct {
|
||||||
|
BaseModel
|
||||||
|
Name string `json:"name"`
|
||||||
|
// 角色类型
|
||||||
|
// 0: 系统角色 1: 用户角色
|
||||||
|
Category uint `json:"category" gorm:"default:0"`
|
||||||
|
// 角色标签
|
||||||
|
Tag string `json:"tag" gorm:"default:''"`
|
||||||
|
Users []*User `json:"users" gorm:"many2many:user_role;"`
|
||||||
|
// 具体权限
|
||||||
|
Auths []*Auth `json:"auths" gorm:"many2many:role_auth;"`
|
||||||
|
IsUnique bool `json:"is_unique" gorm:"default:false"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r Role) CheckAuth(name string, tags ...string) AuthLevel {
|
||||||
|
res := AuthNone
|
||||||
|
tag := ""
|
||||||
|
if len(tags) > 0 {
|
||||||
|
tag = tags[0]
|
||||||
|
}
|
||||||
|
for _, a := range r.Auths {
|
||||||
|
if a.Name == "admin" && a.Tag == "" || (a.Name == "admin" && a.Tag == tag) || (a.Name == name && a.Tag == tag) {
|
||||||
|
if a.Level > res {
|
||||||
|
res = a.Level
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
type AuthLevel uint
|
||||||
|
|
||||||
|
const (
|
||||||
|
AuthNone AuthLevel = 0
|
||||||
|
AuthRead AuthLevel = 1
|
||||||
|
AuthCreate AuthLevel = 2
|
||||||
|
AuthUpdate AuthLevel = 3
|
||||||
|
AuthDelete AuthLevel = 4
|
||||||
|
)
|
||||||
|
|
||||||
|
func (a AuthLevel) CanRead() bool {
|
||||||
|
return a >= AuthRead
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a AuthLevel) CanCreate() bool {
|
||||||
|
return a >= AuthCreate
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a AuthLevel) CanUpdate() bool {
|
||||||
|
return a >= AuthUpdate
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a AuthLevel) CanDelete() bool {
|
||||||
|
return a >= AuthDelete
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a AuthLevel) CanDoAny() bool {
|
||||||
|
return a >= AuthDelete
|
||||||
|
}
|
||||||
|
|
||||||
|
// 资源权限
|
||||||
|
|
||||||
|
type Auth struct {
|
||||||
|
BaseModel
|
||||||
|
Name string `json:"name"`
|
||||||
|
AppID uint `json:"app_id"`
|
||||||
|
// 权限标签
|
||||||
|
Tag string `json:"tag"`
|
||||||
|
// 权限等级 0 相当于没有 1 读权限 2 创建权限 3 修改权限 4 删除权限
|
||||||
|
Level AuthLevel `json:"level"`
|
||||||
|
}
|
||||||
159
models/user.go
Normal file
159
models/user.go
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"github.com/veypi/utils"
|
||||||
|
"github.com/veypi/utils/log"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// User db user model
|
||||||
|
type User struct {
|
||||||
|
BaseModel
|
||||||
|
Username string `json:"username" gorm:"type:varchar(100);unique;not null"`
|
||||||
|
Nickname string `json:"nickname" gorm:"type:varchar(100)" json:",omitempty"`
|
||||||
|
Phone string `json:"phone" gorm:"type:varchar(20);unique;default:null" json:",omitempty"`
|
||||||
|
Email string `json:"email" gorm:"type:varchar(50);unique;default:null" json:",omitempty"`
|
||||||
|
CheckCode string `gorm:"type:varchar(64);not null" json:"-"`
|
||||||
|
RealCode string `gorm:"type:varchar(32);not null" json:"-"`
|
||||||
|
Position string `json:"position"`
|
||||||
|
// disabled 禁用
|
||||||
|
Status string `json:"status"`
|
||||||
|
|
||||||
|
Icon string `json:"icon"`
|
||||||
|
Roles []*Role `json:"roles" gorm:"many2many:user_role;"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO:: roles 是否会造成token过大 ?
|
||||||
|
type PayLoad struct {
|
||||||
|
ID uint `json:"id"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
Nickname string `json:"nickname"`
|
||||||
|
Icon string `json:"icon"`
|
||||||
|
Iat int64 `json:"iat"` //token time
|
||||||
|
Exp int64 `json:"exp"`
|
||||||
|
Roles []uint `json:"roles"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PayLoad) CheckAuth(name string, tags ...string) AuthLevel {
|
||||||
|
res := AuthNone
|
||||||
|
if p == nil || p.Roles == nil {
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
for _, id := range p.Roles {
|
||||||
|
r := GlobalRoles[id]
|
||||||
|
if r == nil {
|
||||||
|
log.Warn().Msgf("not found role id: %d", id)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
t := r.CheckAuth(name, tags...)
|
||||||
|
if t > res {
|
||||||
|
res = t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *User) String() string {
|
||||||
|
return u.Username + ":" + u.Nickname
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *User) CheckAuth(name string, tags ...string) AuthLevel {
|
||||||
|
res := AuthNone
|
||||||
|
if u == nil || u.Roles == nil {
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
for _, t := range u.Roles {
|
||||||
|
r := GlobalRoles[t.ID]
|
||||||
|
if r == nil {
|
||||||
|
log.Warn().Msgf("not found role id: %d", t.ID)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
t := r.CheckAuth(name, tags...)
|
||||||
|
if t > res {
|
||||||
|
res = t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *User) GetToken(key string) (string, error) {
|
||||||
|
header := map[string]string{
|
||||||
|
"typ": "JWT",
|
||||||
|
"alg": "HS256",
|
||||||
|
}
|
||||||
|
//header := "{\"typ\": \"JWT\", \"alg\": \"HS256\"}"
|
||||||
|
now := time.Now().Unix()
|
||||||
|
payload := PayLoad{
|
||||||
|
ID: u.ID,
|
||||||
|
Username: u.Username,
|
||||||
|
Nickname: u.Nickname,
|
||||||
|
Icon: u.Icon,
|
||||||
|
Iat: now,
|
||||||
|
Exp: now + 60*60*24,
|
||||||
|
}
|
||||||
|
for _, r := range u.Roles {
|
||||||
|
payload.Roles = append(payload.Roles, r.ID)
|
||||||
|
}
|
||||||
|
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))
|
||||||
|
hmacCipher.Write([]byte(A + "." + B))
|
||||||
|
C := hmacCipher.Sum(nil)
|
||||||
|
return A + "." + B + "." + base64.StdEncoding.EncodeToString(C), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
InvalidToken = errors.New("invalid token")
|
||||||
|
ExpiredToken = errors.New("expired token")
|
||||||
|
)
|
||||||
|
|
||||||
|
func ParseToken(token string, key string, payload *PayLoad) (bool, error) {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
hmacCipher := hmac.New(sha256.New, []byte(key))
|
||||||
|
hmacCipher.Write([]byte(A + "." + B))
|
||||||
|
tempC := hmacCipher.Sum(nil)
|
||||||
|
if !hmac.Equal([]byte(C), []byte(base64.StdEncoding.EncodeToString(tempC))) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
tempPayload, err := base64.StdEncoding.DecodeString(B)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(tempPayload, payload); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
if time.Now().Unix() > payload.Exp {
|
||||||
|
return false, ExpiredToken
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *User) UpdateAuth(ps string) (err error) {
|
||||||
|
u.RealCode = utils.RandSeq(32)
|
||||||
|
u.CheckCode, err = utils.AesEncrypt(u.RealCode, []byte(ps))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *User) CheckLogin(ps string) (bool, error) {
|
||||||
|
temp, err := utils.AesDecrypt(u.CheckCode, []byte(ps))
|
||||||
|
return temp == u.RealCode, err
|
||||||
|
}
|
||||||
1
oaf/.eslintignore
Normal file
1
oaf/.eslintignore
Normal file
@ -0,0 +1 @@
|
|||||||
|
src/libs/wwLogin.js
|
||||||
@ -12,6 +12,10 @@ module.exports = {
|
|||||||
ecmaVersion: 2020
|
ecmaVersion: 2020
|
||||||
},
|
},
|
||||||
rules: {
|
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-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
|
||||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
|
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,9 @@
|
|||||||
"lint": "vue-cli-service lint"
|
"lint": "vue-cli-service lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"axios": "^0.21.1",
|
||||||
"core-js": "^3.6.5",
|
"core-js": "^3.6.5",
|
||||||
|
"js-base64": "^3.6.0",
|
||||||
"vue": "^2.6.11",
|
"vue": "^2.6.11",
|
||||||
"vue-class-component": "^7.2.3",
|
"vue-class-component": "^7.2.3",
|
||||||
"vue-property-decorator": "^9.1.2",
|
"vue-property-decorator": "^9.1.2",
|
||||||
|
|||||||
@ -6,56 +6,33 @@
|
|||||||
dark
|
dark
|
||||||
>
|
>
|
||||||
<div class="d-flex align-center">
|
<div class="d-flex align-center">
|
||||||
<v-img
|
|
||||||
alt="Vuetify Logo"
|
|
||||||
class="shrink mr-2"
|
|
||||||
contain
|
|
||||||
src="https://cdn.vuetifyjs.com/images/logos/vuetify-logo-dark.png"
|
|
||||||
transition="scale-transition"
|
|
||||||
width="40"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<v-img
|
|
||||||
alt="Vuetify Name"
|
|
||||||
class="shrink mt-1 hidden-sm-and-down"
|
|
||||||
contain
|
|
||||||
min-width="100"
|
|
||||||
src="https://cdn.vuetifyjs.com/images/logos/vuetify-name-dark.png"
|
|
||||||
width="100"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<v-spacer></v-spacer>
|
<v-spacer></v-spacer>
|
||||||
|
<v-btn text class="font-italic" style="font-size: 20px">
|
||||||
<v-btn
|
统一认证
|
||||||
href="https://github.com/vuetifyjs/vuetify/releases/latest"
|
|
||||||
target="_blank"
|
|
||||||
text
|
|
||||||
>
|
|
||||||
<span class="mr-2">Latest Release</span>
|
|
||||||
<v-icon>mdi-open-in-new</v-icon>
|
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</v-app-bar>
|
</v-app-bar>
|
||||||
|
|
||||||
<v-main>
|
<v-main>
|
||||||
<HelloWorld/>
|
<router-view></router-view>
|
||||||
</v-main>
|
</v-main>
|
||||||
</v-app>
|
</v-app>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import HelloWorld from './components/HelloWorld.vue'
|
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
name: 'App',
|
name: 'App',
|
||||||
|
|
||||||
components: {
|
components: {},
|
||||||
HelloWorld
|
|
||||||
},
|
|
||||||
|
|
||||||
data: () => ({
|
data: () => ({
|
||||||
//
|
//
|
||||||
})
|
}),
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
document.title = '统一认证'
|
||||||
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
65
oaf/src/api/ajax.ts
Normal file
65
oaf/src/api/ajax.ts
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import axios from 'axios'
|
||||||
|
import store from '@/store'
|
||||||
|
|
||||||
|
function baseRequests(url: string, method: any = 'GET', query: any, data: any, success: any, fail?: Function) {
|
||||||
|
return axios({
|
||||||
|
url: url,
|
||||||
|
params: query,
|
||||||
|
data: data,
|
||||||
|
method: method,
|
||||||
|
headers: {
|
||||||
|
auth_token: localStorage.auth_token
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((res: any) => {
|
||||||
|
if ('auth_token' in res.headers) {
|
||||||
|
localStorage.auth_token = res.headers.auth_token
|
||||||
|
}
|
||||||
|
if (method === 'HEAD') {
|
||||||
|
success(res.headers)
|
||||||
|
} 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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const ajax = {
|
||||||
|
get(url: '', data = {}, success = {}, fail?: Function) {
|
||||||
|
return baseRequests(url, 'GET', data, {}, success, fail)
|
||||||
|
},
|
||||||
|
head(url: '', data = {}, success = {}, fail?: Function) {
|
||||||
|
return baseRequests(url, 'HEAD', data, {}, success, fail)
|
||||||
|
},
|
||||||
|
delete(url: '', data = {}, success = {}, fail?: Function) {
|
||||||
|
return baseRequests(url, 'DELETE', data, {}, success, fail)
|
||||||
|
},
|
||||||
|
post(url: '', data = {}, success = {}, fail?: Function) {
|
||||||
|
return baseRequests(url, 'POST', {}, data, success, fail)
|
||||||
|
},
|
||||||
|
put(url: '', data = {}, success = {}, fail?: Function) {
|
||||||
|
return baseRequests(url, 'PUT', {}, data, success, fail)
|
||||||
|
},
|
||||||
|
patch(url: '', data = {}, success = {}, fail?: Function) {
|
||||||
|
return baseRequests(url, 'PATCH', {}, data, success, fail)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ajax
|
||||||
200
oaf/src/api/index.ts
Normal file
200
oaf/src/api/index.ts
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019 light <light@light-laptop>
|
||||||
|
*
|
||||||
|
* Distributed under terms of the MIT license.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {Base64} from 'js-base64'
|
||||||
|
import ajax from './ajax'
|
||||||
|
import store from '@/store'
|
||||||
|
|
||||||
|
const Code = {
|
||||||
|
42011: '无操作权限',
|
||||||
|
22031: '资源不存在 或 您无权操作该资源'
|
||||||
|
}
|
||||||
|
|
||||||
|
class Interface {
|
||||||
|
private readonly method: Function
|
||||||
|
private readonly api: string
|
||||||
|
private readonly data: any
|
||||||
|
|
||||||
|
constructor(method: Function, api: string, data?: any) {
|
||||||
|
this.method = method
|
||||||
|
this.api = api
|
||||||
|
this.data = data
|
||||||
|
}
|
||||||
|
|
||||||
|
Start(success: Function, fail?: Function) {
|
||||||
|
const newFail = function (data: any) {
|
||||||
|
if (data && data.code === 40001) {
|
||||||
|
// no login
|
||||||
|
store.dispatch('handleLogOut')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (data && data.code > 0 && Code[data.code]) {
|
||||||
|
// Message.warning({message: Code[data.code] || data.err, offset: 100})
|
||||||
|
}
|
||||||
|
if (fail) {
|
||||||
|
fail(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const newSuccess = function (data: any) {
|
||||||
|
if (Number(data.status) === 1) {
|
||||||
|
if (success) {
|
||||||
|
success(data.content)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
newFail(data)
|
||||||
|
if (data.code === 41001) {
|
||||||
|
store.dispatch('handleLogOut')
|
||||||
|
// bus.$emit('log_out')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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 = {
|
||||||
|
local: '/api/app/',
|
||||||
|
get(id: string) {
|
||||||
|
return new Interface(ajax.get, this.local + id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const api = {
|
||||||
|
role: role,
|
||||||
|
app: app,
|
||||||
|
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
|
||||||
|
})
|
||||||
|
},
|
||||||
|
login(username: string, password: string) {
|
||||||
|
return new Interface(ajax.head, '/api/user/' + username, {
|
||||||
|
password: Base64.encode(password)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// @title 职位
|
||||||
|
// @domain 部门
|
||||||
|
register(username: string, password: string, domain?: string, title?: string) {
|
||||||
|
return new Interface(ajax.post, '/api/user/', {
|
||||||
|
username: username,
|
||||||
|
password: Base64.encode(password),
|
||||||
|
domain: domain,
|
||||||
|
title: title
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
message: message
|
||||||
|
}
|
||||||
|
|
||||||
|
const Api = {
|
||||||
|
install(Vue: any) {
|
||||||
|
Vue.prototype.api = api
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export {Api}
|
||||||
|
export default api
|
||||||
@ -1,153 +0,0 @@
|
|||||||
<template>
|
|
||||||
<v-container>
|
|
||||||
<v-row class="text-center">
|
|
||||||
<v-col cols="12">
|
|
||||||
<v-img
|
|
||||||
:src="require('../assets/logo.svg')"
|
|
||||||
class="my-3"
|
|
||||||
contain
|
|
||||||
height="200"
|
|
||||||
/>
|
|
||||||
</v-col>
|
|
||||||
|
|
||||||
<v-col class="mb-4">
|
|
||||||
<h1 class="display-2 font-weight-bold mb-3">
|
|
||||||
Welcome to Vuetify
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
<p class="subheading font-weight-regular">
|
|
||||||
For help and collaboration with other Vuetify developers,
|
|
||||||
<br>please join our online
|
|
||||||
<a
|
|
||||||
href="https://community.vuetifyjs.com"
|
|
||||||
target="_blank"
|
|
||||||
>Discord Community</a>
|
|
||||||
</p>
|
|
||||||
</v-col>
|
|
||||||
|
|
||||||
<v-col
|
|
||||||
class="mb-5"
|
|
||||||
cols="12"
|
|
||||||
>
|
|
||||||
<h2 class="headline font-weight-bold mb-3">
|
|
||||||
What's next?
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<v-row justify="center">
|
|
||||||
<a
|
|
||||||
v-for="(next, i) in whatsNext"
|
|
||||||
:key="i"
|
|
||||||
:href="next.href"
|
|
||||||
class="subheading mx-3"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
{{ next.text }}
|
|
||||||
</a>
|
|
||||||
</v-row>
|
|
||||||
</v-col>
|
|
||||||
|
|
||||||
<v-col
|
|
||||||
class="mb-5"
|
|
||||||
cols="12"
|
|
||||||
>
|
|
||||||
<h2 class="headline font-weight-bold mb-3">
|
|
||||||
Important Links
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<v-row justify="center">
|
|
||||||
<a
|
|
||||||
v-for="(link, i) in importantLinks"
|
|
||||||
:key="i"
|
|
||||||
:href="link.href"
|
|
||||||
class="subheading mx-3"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
{{ link.text }}
|
|
||||||
</a>
|
|
||||||
</v-row>
|
|
||||||
</v-col>
|
|
||||||
|
|
||||||
<v-col
|
|
||||||
class="mb-5"
|
|
||||||
cols="12"
|
|
||||||
>
|
|
||||||
<h2 class="headline font-weight-bold mb-3">
|
|
||||||
Ecosystem
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<v-row justify="center">
|
|
||||||
<a
|
|
||||||
v-for="(eco, i) in ecosystem"
|
|
||||||
:key="i"
|
|
||||||
:href="eco.href"
|
|
||||||
class="subheading mx-3"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
{{ eco.text }}
|
|
||||||
</a>
|
|
||||||
</v-row>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</v-container>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import Vue from 'vue'
|
|
||||||
|
|
||||||
export default Vue.extend({
|
|
||||||
name: 'HelloWorld',
|
|
||||||
|
|
||||||
data: () => ({
|
|
||||||
ecosystem: [
|
|
||||||
{
|
|
||||||
text: 'vuetify-loader',
|
|
||||||
href: 'https://github.com/vuetifyjs/vuetify-loader'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: 'github',
|
|
||||||
href: 'https://github.com/vuetifyjs/vuetify'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: 'awesome-vuetify',
|
|
||||||
href: 'https://github.com/vuetifyjs/awesome-vuetify'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
importantLinks: [
|
|
||||||
{
|
|
||||||
text: 'Documentation',
|
|
||||||
href: 'https://vuetifyjs.com'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: 'Chat',
|
|
||||||
href: 'https://community.vuetifyjs.com'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: 'Made with Vuetify',
|
|
||||||
href: 'https://madewithvuejs.com/vuetify'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: 'Twitter',
|
|
||||||
href: 'https://twitter.com/vuetifyjs'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: 'Articles',
|
|
||||||
href: 'https://medium.com/vuetify'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
whatsNext: [
|
|
||||||
{
|
|
||||||
text: 'Explore components',
|
|
||||||
href: 'https://vuetifyjs.com/components/api-explorer'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: 'Select a layout',
|
|
||||||
href: 'https://vuetifyjs.com/getting-started/pre-made-layouts'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: 'Frequently Asked Questions',
|
|
||||||
href: 'https://vuetifyjs.com/getting-started/frequently-asked-questions'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
42
oaf/src/components/WxLogin.vue
Normal file
42
oaf/src/components/WxLogin.vue
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<template>
|
||||||
|
<div id="wx_reg"></div>
|
||||||
|
</template>
|
||||||
|
<script lang='ts'>
|
||||||
|
import {Component, Vue, Prop} from 'vue-property-decorator'
|
||||||
|
import '@/libs/wwLogin.js'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
components: {}
|
||||||
|
})
|
||||||
|
export default class WxLogin extends Vue {
|
||||||
|
goto(id: string, app: string, url: string, state?: string, href?: string) {
|
||||||
|
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>
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
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>
|
||||||
2
oaf/src/libs/wwLogin.js
Normal file
2
oaf/src/libs/wwLogin.js
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
!function(a,b,c){function d(c){var d=b.createElement("iframe"),e="https://open.work.weixin.qq.com/wwopen/sso/qrConnect?appid="+c.appid+"&agentid="+c.agentid+"&redirect_uri="+c.redirect_uri+"&state="+c.state+"&login_type=jssdk";e+=c.style?"&style="+c.style:"",e+=c.href?"&href="+c.href:"",d.src=e,d.frameBorder="0",d.allowTransparency="true",d.scrolling="no",d.width="300px",d.height="400px";var f=b.getElementById(c.id);f.innerHTML="",f.appendChild(d),d.onload=function(){d.contentWindow.postMessage&&a.addEventListener&&(a.addEventListener("message",function(b){
|
||||||
|
b.data&&b.origin.indexOf("work.weixin.qq.com")>-1&&(a.location.href=b.data)}),d.contentWindow.postMessage("ask_usePostMessage","*"))}}a.WwLogin=d}(window,document);
|
||||||
@ -3,6 +3,9 @@ import App from './App.vue'
|
|||||||
import router from './router'
|
import router from './router'
|
||||||
import store from './store'
|
import store from './store'
|
||||||
import vuetify from './plugins/vuetify'
|
import vuetify from './plugins/vuetify'
|
||||||
|
import {Api} from '@/api'
|
||||||
|
|
||||||
|
Vue.use(Api)
|
||||||
|
|
||||||
Vue.config.productionTip = false
|
Vue.config.productionTip = false
|
||||||
|
|
||||||
|
|||||||
@ -3,5 +3,22 @@ import Vuetify from 'vuetify/lib/framework'
|
|||||||
|
|
||||||
Vue.use(Vuetify)
|
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({
|
export default new Vuetify({
|
||||||
|
theme: {
|
||||||
|
dark: false,
|
||||||
|
themes: {
|
||||||
|
light: light
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import VueRouter, { RouteConfig } from 'vue-router'
|
import VueRouter, {RouteConfig} from 'vue-router'
|
||||||
import Home from '../views/Home.vue'
|
import Home from '../views/Home.vue'
|
||||||
|
|
||||||
Vue.use(VueRouter)
|
Vue.use(VueRouter)
|
||||||
@ -17,6 +17,11 @@ const routes: Array<RouteConfig> = [
|
|||||||
// this generates a separate chunk (about.[hash].js) for this route
|
// this generates a separate chunk (about.[hash].js) for this route
|
||||||
// which is lazy-loaded when the route is visited.
|
// which is lazy-loaded when the route is visited.
|
||||||
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
|
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/wx',
|
||||||
|
name: 'wx',
|
||||||
|
component: () => import('../views/wx.vue')
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@ -1,18 +1,25 @@
|
|||||||
|
<style>
|
||||||
|
.home {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
<template>
|
<template>
|
||||||
<div class="home">
|
<div class='home d-flex justify-center align-center'>
|
||||||
<img alt="Vue logo" src="../assets/logo.png">
|
|
||||||
<HelloWorld msg="Welcome to Your Vue.js + TypeScript App"/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang='ts'>
|
||||||
import { Component, Vue } from 'vue-property-decorator'
|
import {Component, Vue} from 'vue-property-decorator'
|
||||||
import HelloWorld from '@/components/HelloWorld.vue' // @ is an alias to /src
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
components: {
|
components: {}
|
||||||
HelloWorld
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
export default class Home extends Vue {}
|
export default class Home extends Vue {
|
||||||
|
mounted() {
|
||||||
|
}
|
||||||
|
|
||||||
|
created() {
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
66
oaf/src/views/wx.vue
Normal file
66
oaf/src/views/wx.vue
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
<template>
|
||||||
|
<div class='home d-flex justify-center align-center'>
|
||||||
|
<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>
|
||||||
|
</template>
|
||||||
|
<script lang='ts'>
|
||||||
|
import {Component, Vue} from 'vue-property-decorator'
|
||||||
|
import WxLogin from '@/components/WxLogin.vue'
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
components: {
|
||||||
|
WxLogin
|
||||||
|
}
|
||||||
|
})
|
||||||
|
export default class Wx extends Vue {
|
||||||
|
aid = ''
|
||||||
|
agentID = ''
|
||||||
|
url = ''
|
||||||
|
|
||||||
|
get enable() {
|
||||||
|
return this.uuid && this.aid && this.agentID && this.url
|
||||||
|
}
|
||||||
|
|
||||||
|
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).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>
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
@ -1,5 +1,28 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
transpileDependencies: [
|
transpileDependencies: [
|
||||||
'vuetify'
|
'vuetify'
|
||||||
]
|
],
|
||||||
|
configureWebpack: {
|
||||||
|
output: {
|
||||||
|
filename: '[name].[hash].js'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
outputDir: '../sub/static',
|
||||||
|
devServer: {
|
||||||
|
host: '0.0.0.0',
|
||||||
|
port: 19528,
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1937,6 +1937,13 @@ aws4@^1.8.0:
|
|||||||
resolved "https://registry.npm.taobao.org/aws4/download/aws4-1.11.0.tgz?cache=0&sync_timestamp=1604101421225&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Faws4%2Fdownload%2Faws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59"
|
resolved "https://registry.npm.taobao.org/aws4/download/aws4-1.11.0.tgz?cache=0&sync_timestamp=1604101421225&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Faws4%2Fdownload%2Faws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59"
|
||||||
integrity sha1-1h9G2DslGSUOJ4Ta9bCUeai0HFk=
|
integrity sha1-1h9G2DslGSUOJ4Ta9bCUeai0HFk=
|
||||||
|
|
||||||
|
axios@^0.21.1:
|
||||||
|
version "0.21.1"
|
||||||
|
resolved "https://registry.npm.taobao.org/axios/download/axios-0.21.1.tgz?cache=0&sync_timestamp=1608609419814&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Faxios%2Fdownload%2Faxios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8"
|
||||||
|
integrity sha1-IlY0gZYvTWvemnbVFu8OXTwJsrg=
|
||||||
|
dependencies:
|
||||||
|
follow-redirects "^1.10.0"
|
||||||
|
|
||||||
babel-code-frame@^6.22.0:
|
babel-code-frame@^6.22.0:
|
||||||
version "6.26.0"
|
version "6.26.0"
|
||||||
resolved "https://registry.npm.taobao.org/babel-code-frame/download/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
|
resolved "https://registry.npm.taobao.org/babel-code-frame/download/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
|
||||||
@ -4187,7 +4194,7 @@ flush-write-stream@^1.0.0:
|
|||||||
inherits "^2.0.3"
|
inherits "^2.0.3"
|
||||||
readable-stream "^2.3.6"
|
readable-stream "^2.3.6"
|
||||||
|
|
||||||
follow-redirects@^1.0.0:
|
follow-redirects@^1.0.0, follow-redirects@^1.10.0:
|
||||||
version "1.13.3"
|
version "1.13.3"
|
||||||
resolved "https://registry.npm.taobao.org/follow-redirects/download/follow-redirects-1.13.3.tgz?cache=0&sync_timestamp=1614436958094&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffollow-redirects%2Fdownload%2Ffollow-redirects-1.13.3.tgz#e5598ad50174c1bc4e872301e82ac2cd97f90267"
|
resolved "https://registry.npm.taobao.org/follow-redirects/download/follow-redirects-1.13.3.tgz?cache=0&sync_timestamp=1614436958094&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffollow-redirects%2Fdownload%2Ffollow-redirects-1.13.3.tgz#e5598ad50174c1bc4e872301e82ac2cd97f90267"
|
||||||
integrity sha1-5VmK1QF0wbxOhyMB6CrCzZf5Amc=
|
integrity sha1-5VmK1QF0wbxOhyMB6CrCzZf5Amc=
|
||||||
@ -5323,6 +5330,11 @@ jest-worker@^25.4.0:
|
|||||||
merge-stream "^2.0.0"
|
merge-stream "^2.0.0"
|
||||||
supports-color "^7.0.0"
|
supports-color "^7.0.0"
|
||||||
|
|
||||||
|
js-base64@^3.6.0:
|
||||||
|
version "3.6.0"
|
||||||
|
resolved "https://registry.npm.taobao.org/js-base64/download/js-base64-3.6.0.tgz?cache=0&sync_timestamp=1604448740467&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjs-base64%2Fdownload%2Fjs-base64-3.6.0.tgz#773e1de628f4f298d65a7e9842c50244751f5756"
|
||||||
|
integrity sha1-dz4d5ij08pjWWn6YQsUCRHUfV1Y=
|
||||||
|
|
||||||
js-message@1.0.7:
|
js-message@1.0.7:
|
||||||
version "1.0.7"
|
version "1.0.7"
|
||||||
resolved "https://registry.npm.taobao.org/js-message/download/js-message-1.0.7.tgz?cache=0&sync_timestamp=1605135348213&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjs-message%2Fdownload%2Fjs-message-1.0.7.tgz#fbddd053c7a47021871bb8b2c95397cc17c20e47"
|
resolved "https://registry.npm.taobao.org/js-message/download/js-message-1.0.7.tgz?cache=0&sync_timestamp=1605135348213&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjs-message%2Fdownload%2Fjs-message-1.0.7.tgz#fbddd053c7a47021871bb8b2c95397cc17c20e47"
|
||||||
|
|||||||
1
sub/static/index.html
Normal file
1
sub/static/index.html
Normal file
@ -0,0 +1 @@
|
|||||||
|
<!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>
|
||||||
86
sub/web.go
Normal file
86
sub/web.go
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
package sub
|
||||||
|
|
||||||
|
import (
|
||||||
|
"OneAuth/api"
|
||||||
|
"OneAuth/cfg"
|
||||||
|
"OneAuth/models"
|
||||||
|
"embed"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
|
"github.com/veypi/OneBD"
|
||||||
|
"github.com/veypi/OneBD/core"
|
||||||
|
"github.com/veypi/OneBD/rfc"
|
||||||
|
"github.com/veypi/utils/log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed static
|
||||||
|
var staticFiles embed.FS
|
||||||
|
|
||||||
|
//go:embed static/index.html
|
||||||
|
var indexFile []byte
|
||||||
|
|
||||||
|
var Web = cli.Command{
|
||||||
|
Name: "web",
|
||||||
|
Usage: "",
|
||||||
|
Description: "oa 核心http服务",
|
||||||
|
Action: RunWeb,
|
||||||
|
Flags: []cli.Flag{},
|
||||||
|
}
|
||||||
|
|
||||||
|
func RunWeb(c *cli.Context) error {
|
||||||
|
_ = runSyncDB(c)
|
||||||
|
ll := log.InfoLevel
|
||||||
|
if l, err := log.ParseLevel(cfg.CFG.LoggerLevel); err == nil {
|
||||||
|
ll = l
|
||||||
|
}
|
||||||
|
app := OneBD.New(&OneBD.Config{
|
||||||
|
Host: cfg.CFG.Host,
|
||||||
|
LoggerPath: cfg.CFG.LoggerPath,
|
||||||
|
LoggerLevel: ll,
|
||||||
|
})
|
||||||
|
app.Router().EmbedFile("/", indexFile)
|
||||||
|
app.Router().EmbedDir("/", staticFiles, "static/")
|
||||||
|
|
||||||
|
// TODO media 文件需要检验权限
|
||||||
|
//app.Router().SubRouter("/media/").Static("/", cfg.CFG.EXEDir+"/media")
|
||||||
|
|
||||||
|
app.Router().SetNotFoundFunc(func(m core.Meta) {
|
||||||
|
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())
|
||||||
|
return app.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
func runSyncDB(*cli.Context) error {
|
||||||
|
db := cfg.DB()
|
||||||
|
log.HandlerErrs(
|
||||||
|
db.SetupJoinTable(&models.User{}, "Roles", &models.UserRole{}),
|
||||||
|
db.SetupJoinTable(&models.Role{}, "Users", &models.UserRole{}),
|
||||||
|
db.SetupJoinTable(&models.Role{}, "Auths", &models.RoleAuth{}),
|
||||||
|
db.AutoMigrate(&models.User{}, &models.Role{}, &models.Auth{}),
|
||||||
|
)
|
||||||
|
log.HandlerErrs(
|
||||||
|
db.AutoMigrate(&models.App{}, &models.Wechat{}),
|
||||||
|
)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user