v3
veypi 3 months ago
parent e83f2da265
commit 1a29442c1c

@ -78,9 +78,10 @@ var _ = Router.Patch("/:user_id", auth.Check("user", "user_id", auth.DoUpdate),
type patchOpts struct { type patchOpts struct {
ID string `json:"id" parse:"path@user_id"` ID string `json:"id" parse:"path@user_id"`
Username *string `json:"username" parse:"json"`
Nickname *string `json:"nickname" parse:"json"` Nickname *string `json:"nickname" parse:"json"`
Icon *string `json:"icon" parse:"json"` Icon *string `json:"icon" parse:"json"`
Username *string `json:"username" parse:"json"`
Email *string `json:"email" parse:"json"` Email *string `json:"email" parse:"json"`
Phone *string `json:"phone" parse:"json"` Phone *string `json:"phone" parse:"json"`
Status *uint `json:"status" parse:"json"` Status *uint `json:"status" parse:"json"`
@ -98,7 +99,7 @@ func userPatch(x *vigo.X) (any, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
optsMap := make(map[string]interface{}) optsMap := make(map[string]any)
if opts.Username != nil { if opts.Username != nil {
optsMap["username"] = opts.Username optsMap["username"] = opts.Username
} }

@ -8,23 +8,25 @@ replace github.com/vyes/vigo => ../vigo/
require ( require (
github.com/glebarez/sqlite v1.11.0 github.com/glebarez/sqlite v1.11.0
github.com/golang-jwt/jwt/v5 v5.2.2 github.com/golang-jwt/jwt/v5 v5.2.3
github.com/google/uuid v1.6.0 github.com/google/uuid v1.6.0
github.com/veypi/vyes-ui v0.0.0-00010101000000-000000000000 github.com/veypi/vyes-ui v0.0.0-00010101000000-000000000000
github.com/vyes/vigo v0.0.0-00010101000000-000000000000 github.com/vyes/vigo v0.0.0-00010101000000-000000000000
gorm.io/driver/mysql v1.5.7 golang.org/x/crypto v0.40.0
gorm.io/driver/postgres v1.5.11 gorm.io/driver/mysql v1.6.0
gorm.io/gorm v1.30.0 gorm.io/driver/postgres v1.6.0
gorm.io/gorm v1.30.1
) )
require ( require (
filippo.io/edwards25519 v1.1.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect
github.com/glebarez/go-sqlite v1.21.2 // indirect github.com/glebarez/go-sqlite v1.21.2 // indirect
github.com/go-sql-driver/mysql v1.7.0 // indirect github.com/go-sql-driver/mysql v1.8.1 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/pgx/v5 v5.5.5 // indirect github.com/jackc/pgx/v5 v5.6.0 // indirect
github.com/jackc/puddle/v2 v2.2.1 // indirect github.com/jackc/puddle/v2 v2.2.2 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect github.com/jinzhu/now v1.1.5 // indirect
github.com/kr/text v0.2.0 // indirect github.com/kr/text v0.2.0 // indirect
@ -33,7 +35,6 @@ require (
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect
github.com/rs/zerolog v1.34.0 // indirect github.com/rs/zerolog v1.34.0 // indirect
golang.org/x/crypto v0.40.0 // indirect
golang.org/x/net v0.42.0 // indirect golang.org/x/net v0.42.0 // indirect
golang.org/x/sync v0.16.0 // indirect golang.org/x/sync v0.16.0 // indirect
golang.org/x/sys v0.34.0 // indirect golang.org/x/sys v0.34.0 // indirect

@ -1,3 +1,5 @@
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -9,23 +11,23 @@ github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9g
github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k= github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k=
github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw= github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ= github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= github.com/golang-jwt/jwt/v5 v5.2.3 h1:kkGXqQOBSDDWRhWNXTFpqGSCMyh/PLnqUvMGJPDJDs0=
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang-jwt/jwt/v5 v5.2.3/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw= github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY=
github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw=
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= 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/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
@ -55,8 +57,6 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/veypi/utils v0.4.2 h1:utIQwkLRDssddbAcZz2Q9xPm3nfANPKsD5lmIGBa7CA=
github.com/veypi/utils v0.4.2/go.mod h1:GDeV3o1EE73dRrZ3cu/j97gPwXyzqtkWiM3D2I+e+6k=
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
@ -78,13 +78,12 @@ gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYs
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo= gorm.io/driver/mysql v1.6.0 h1:eNbLmNTpPpTOVZi8MMxCi2aaIm0ZpInbORNXDwyLGvg=
gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= gorm.io/driver/mysql v1.6.0/go.mod h1:D/oCC2GWK3M/dqoLxnOlaNKmXz8WNTfcS9y5ovaSqKo=
gorm.io/driver/postgres v1.5.11 h1:ubBVAfbKEUld/twyKZ0IYn9rSQh448EdelLYk9Mv314= gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4=
gorm.io/driver/postgres v1.5.11/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI= gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo=
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= gorm.io/gorm v1.30.1 h1:lSHg33jJTBxs2mgJRfRZeLDG+WZaHYCk3Wtfl6Ngzo4=
gorm.io/gorm v1.30.0 h1:qbT5aPv1UH8gI99OsRlvDToLxW5zR7FzS9acZDOZcgs= gorm.io/gorm v1.30.1/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=
gorm.io/gorm v1.30.0/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=
modernc.org/libc v1.22.5 h1:91BNch/e5B0uPbJFgqbxXuOnxBQjlS//icfQEGmvyjE= modernc.org/libc v1.22.5 h1:91BNch/e5B0uPbJFgqbxXuOnxBQjlS//icfQEGmvyjE=
modernc.org/libc v1.22.5/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY= modernc.org/libc v1.22.5/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY=
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ= modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=

@ -11,6 +11,7 @@ import (
"github.com/veypi/OneAuth/api" "github.com/veypi/OneAuth/api"
"github.com/veypi/vyes-ui" "github.com/veypi/vyes-ui"
"github.com/vyes/vigo" "github.com/vyes/vigo"
"github.com/vyes/vigo/contrib/cors"
"github.com/vyes/vigo/contrib/vyes" "github.com/vyes/vigo/contrib/vyes"
) )
@ -22,5 +23,6 @@ var uifs embed.FS
var ( var (
_ = Router.Extend("v", vyesui.Router) _ = Router.Extend("v", vyesui.Router)
_ = Router.Extend("api", api.Router) _ = Router.Extend("api", api.Router)
_ = Router.SubRouter("/*path").UseBefore(cors.AllowAny)
_ = vyes.WrapUI(Router, uifs) _ = vyes.WrapUI(Router, uifs)
) )

@ -15,10 +15,17 @@ import (
"github.com/golang-jwt/jwt/v5" "github.com/golang-jwt/jwt/v5"
"github.com/veypi/OneAuth/cfg" "github.com/veypi/OneAuth/cfg"
"github.com/veypi/OneAuth/errs"
"github.com/vyes/vigo" "github.com/vyes/vigo"
) )
var (
AuthNotFound = vigo.NewError("auth not found").WithCode(40100)
AuthFailed = vigo.NewError("auth failed").WithCode(40101)
AuthExpired = vigo.NewError("auth expired").WithCode(40102)
AuthInvalid = vigo.NewError("auth invalid").WithCode(40103)
AuthNoPerm = vigo.NewError("auth no permission").WithCode(40104)
)
func GenJwt(claim *Claims) (string, error) { func GenJwt(claim *Claims) (string, error) {
return GenJwtWithKey(claim, cfg.Config.Key) return GenJwtWithKey(claim, cfg.Config.Key)
} }
@ -31,20 +38,24 @@ func GenJwtWithKey(claim *Claims, key string) (string, error) {
} }
func ParseJwt(tokenString string) (*Claims, error) { func ParseJwt(tokenString string, keys ...string) (*Claims, error) {
key := cfg.Config.Key
if len(keys) > 0 {
key = keys[0]
}
claims := &Claims{} claims := &Claims{}
token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) { token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (any, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
} }
return []byte(cfg.Config.Key), nil return []byte(key), nil
}) })
if errors.Is(err, jwt.ErrTokenExpired) { if errors.Is(err, jwt.ErrTokenExpired) {
return nil, errs.AuthExpired return nil, AuthExpired
} }
if err != nil || !token.Valid { if err != nil || !token.Valid {
return nil, errs.AuthInvalid return nil, AuthInvalid
} }
return claims, nil return claims, nil
} }
@ -54,7 +65,7 @@ func checkJWT(x *vigo.X) (*Claims, error) {
if authHeader == "" { if authHeader == "" {
authHeader = x.Request.URL.Query().Get("Authorization") authHeader = x.Request.URL.Query().Get("Authorization")
if authHeader == "" { if authHeader == "" {
return nil, errs.AuthNotFound return nil, AuthNotFound
} }
} }
// Token is typically in the format "Bearer <token>" // Token is typically in the format "Bearer <token>"
@ -66,6 +77,7 @@ func checkJWT(x *vigo.X) (*Claims, error) {
return nil, err return nil, err
} }
x.Set("token", claims) x.Set("token", claims)
x.Set("uid", claims.UID)
return claims, nil return claims, nil
} }
@ -86,7 +98,7 @@ func Check(target string, pid string, l AuthLevel) func(x *vigo.X) (any, error)
tid = x.Params.Get(pid) tid = x.Params.Get(pid)
} }
if !claims.Access.Check(target, tid, l) { if !claims.Access.Check(target, tid, l) {
// return nil, errs.AuthNoPerm return nil, AuthNoPerm
} }
return claims, nil return claims, nil
} }

@ -12,27 +12,27 @@ const AUSTATUS_REJECT = "reject"
type App struct { type App struct {
BaseModel BaseModel
Name string `json:"name" methods:"post,*patch,*list" parse:"json"` Name string `json:"name" parse:"json"`
Icon string `json:"icon" methods:"post,*patch" parse:"json"` Icon string `json:"icon" parse:"json"`
Des string `json:"des" methods:"*post,*patch" parse:"json"` Des string `json:"des" parse:"json"`
Typ string `json:"typ" gorm:"default:public" methods:"post,*patch" parse:"json"` Typ string `json:"typ" gorm:"default:public" parse:"json"`
Status string `json:"status" gorm:"default:ok" methods:"post,*patch" parse:"json"` Status string `json:"status" gorm:"default:ok" parse:"json"`
InitRoleID *string `json:"init_role_id" gorm:"index;type:varchar(32);default: null" methods:"*patch" parse:"json"` InitRoleID *string `json:"init_role_id" gorm:"index;type:varchar(32);default: null" parse:"json"`
InitRole *Role `json:"init_role" gorm:"foreignKey:InitRoleID;references:ID"` InitRole *Role `json:"init_role" gorm:"foreignKey:InitRoleID;references:ID"`
InitUrl string `json:"init_url" methods:"*patch" parse:"json"` InitUrl string `json:"init_url" parse:"json"`
UserCount uint `json:"user_count"` UserCount uint `json:"user_count" default:"0"`
Key string `json:"-"` Key string `json:"-"`
} }
type AppUser struct { type AppUser struct {
BaseModel BaseModel
AppID string `json:"app_id" methods:"get,list,post,patch,delete" parse:"path"` AppID string `json:"app_id" parse:"path"`
App *App `json:"app" gorm:"foreignKey:AppID;references:ID"` App *App `json:"app" gorm:"foreignKey:AppID;references:ID"`
UserID string `json:"user_id" methods:"get,*list,post" parse:"json"` UserID string `json:"user_id" parse:"json"`
User *User `json:"user" gorm:"foreignKey:UserID;references:ID"` User *User `json:"user" gorm:"foreignKey:UserID;references:ID"`
Status string `json:"status" methods:"post,*patch,*list" parse:"json"` Status string `json:"status" parse:"json"`
} }
func (m *AppUser) onOk(tx *gorm.DB) (err error) { func (m *AppUser) onOk(tx *gorm.DB) (err error) {
@ -77,11 +77,11 @@ type Resource struct {
type Role struct { type Role struct {
BaseModel BaseModel
AppID string `json:"app_id" methods:"*list,post" parse:"path"` AppID string `json:"app_id" parse:"path@app_id"`
App *App `json:"-" gorm:"foreignKey:AppID;references:ID"` App *App `json:"-" gorm:"foreignKey:AppID;references:ID"`
Name string `json:"name" methods:"post,*patch,*list" parse:"json"` Name string `json:"name" parse:"json"`
Des string `json:"des" methods:"post,*patch" parse:"json"` Des string `json:"des" parse:"json"`
UserCount uint `json:"user_count"` UserCount uint `json:"user_count" default:"0"`
Access []*Access `json:"-"` Access []*Access `json:"-"`
} }

@ -54,8 +54,7 @@ func InitDB() error {
logv.AssertError(cfg.DB().Where("id = ?", app.ID).Attrs(app).FirstOrCreate(app).Error) logv.AssertError(cfg.DB().Where("id = ?", app.ID).Attrs(app).FirstOrCreate(app).Error)
initRole := map[string]map[string]uint{ initRole := map[string]map[string]uint{
"user": {"admin": 5, "normal": 1}, "user": {"admin": 5, "normal": 1},
"app": {"admin": 5, "normal": 2}, "app": {"admin": 5, "normal": 1},
"fs": {"admin": 5, "normal": 5},
} }
adminID := "" adminID := ""
for r, roles := range initRole { for r, roles := range initRole {

@ -7,12 +7,12 @@ import "time"
// OverPerm 非oa应用获取oa数据的权限由用户设定 // OverPerm 非oa应用获取oa数据的权限由用户设定
type Token struct { type Token struct {
BaseModel BaseModel
UserID string `json:"user_id" gorm:"index;type:varchar(32)" methods:"*post,list" parse:"json"` UserID string `json:"user_id" gorm:"index;type:varchar(32)" parse:"json"`
User *User `json:"-"` User *User `json:"-"`
AppID string `json:"app_id" gorm:"index;type:varchar(32)" methods:"post,list" parse:"json"` AppID string `json:"app_id" gorm:"index;type:varchar(32)" parse:"json"`
App *App `json:"-"` App *App `json:"-"`
ExpiredAt time.Time `json:"expired_at" methods:"*post,*patch" parse:"json"` ExpiredAt time.Time `json:"expired_at" parse:"json"`
OverPerm string `json:"over_perm" methods:"*post,*patch" parse:"json"` OverPerm string `json:"over_perm" parse:"json"`
Device string `json:"device" methods:"*post" parse:"json"` Device string `json:"device" parse:"json"`
Ip string `json:"ip"` Ip string `json:"ip"`
} }

@ -9,17 +9,17 @@ import (
// code 64 hex / 32 byte / 256 bit // code 64 hex / 32 byte / 256 bit
type User struct { type User struct {
BaseModel BaseModel
Username string `json:"username" gorm:"type:varchar(100);unique;default:not null" methods:" post,*patch,*list" parse:"json"` Username string `json:"username" gorm:"type:varchar(100);unique;default:not null" parse:"json"`
Nickname string `json:"nickname" gorm:"type:varchar(100)" methods:"*post,*patch,*list" parse:"json"` Nickname string `json:"nickname" gorm:"type:varchar(100)" parse:"json"`
Icon string `json:"icon" methods:"*post,*patch" parse:"json"` Icon string `json:"icon" parse:"json"`
Email string `json:"email" gorm:"unique;type:varchar(50);default:null" methods:"*post,*patch,*list" parse:"json"` Email string `json:"email" gorm:"unique;type:varchar(50);default:null" parse:"json"`
Phone string `json:"phone" gorm:"type:varchar(30);unique;default:null" methods:"*post,*patch,*list" parse:"json"` Phone string `json:"phone" gorm:"type:varchar(30);unique;default:null" parse:"json"`
Status uint `json:"status" methods:"*patch,*list" parse:"json"` Status uint `json:"status" parse:"json"`
Salt string `json:"-" gorm:"type:varchar(32)"` Salt string `json:"-" gorm:"type:varchar(32)"`
Code string `json:"-" gorm:"type:varchar(64)" methods:"post" parse:"json"` Code string `json:"-" gorm:"type:varchar(64)" parse:"json"`
} }
type UserRole struct { type UserRole struct {

@ -1,6 +1,6 @@
:root { :root {
/* 颜色 */ /* 颜色 */
--color-primary: #4361ee; --color-primary: #92a5ff;
--color-secondary: #3f37c9; --color-secondary: #3f37c9;
--color-success: #28a745; --color-success: #28a745;
--color-danger: #dc3545; --color-danger: #dc3545;

@ -5,8 +5,8 @@ export default ($env) => {
console.log($env, $vyes.root) console.log($env, $vyes.root)
token.setBaseUrl($env.root) token.setBaseUrl($env.root)
token.wrapAxios($env.$axios) token.wrapAxios($env.$axios)
$env.$global.token = token $env.$G.token = token
$env.$global.user = token.body() $env.$G.user = token.body()
$env.$router.addRoutes(routes) $env.$router.addRoutes(routes)
@ -16,7 +16,7 @@ export default ($env) => {
await token.refresh() await token.refresh()
} }
if (token.isExpired()) { if (token.isExpired()) {
token.logout(to.path) token.logout(to.fullPath)
} }
} else { } else {
next(); next();

@ -70,10 +70,10 @@
</style> </style>
<body> <body>
<div class="header-user ml-auto" v-if="user.name"> <div class="header-user ml-auto" v-if="$G.user.name">
<img :src="user.icon" class="user-avatar" alt="用户头像"> <img :src="$G.user.icon" class="user-avatar" alt="用户头像">
<a href='/profile' class="user-name">{{ user.name }}</a> <a href='/profile' class="user-name">{{ $G.user.name }}</a>
<button @click="token.logout()" class="auth-btn logout-btn"> <button @click="$G.token.logout()" class="auth-btn logout-btn">
退出 退出
</button> </button>
</div> </div>
@ -84,9 +84,4 @@
</div> </div>
</body> </body>
<script setup>
import token from 'token'
user = token.body() || {}
</script>
</html> </html>

@ -54,7 +54,7 @@
.menu a[active] { .menu a[active] {
background: #409EFF; background: #c3e1ff;
} }
.content { .content {
@ -85,7 +85,7 @@
</div> </div>
<a class="ml-auto" href="/">应用权限管理</a> <a class="ml-auto" href="/">应用权限管理</a>
<!-- 新增跳转到/home的链接 --> <!-- 新增跳转到/home的链接 -->
<div refu='ico' class="ml-auto" style="margin-left: auto;"></div> <div vsrc='ico' class="ml-auto" style="margin-left: auto;"></div>
</header> </header>
@ -109,8 +109,9 @@
<script setup> <script setup>
id = $router.params.id; id = $router.params.id;
try { try {
$env.app = await $axios.get(`/api/app/${id}`) app = await $axios.get(`/api/app/${id}`)
document.title = `${app.name} - 项目主页` document.title = `${app.name} - 项目主页`
$G.app = app
} catch (e) { } catch (e) {
$router.push('/') $router.push('/')
console.log(e) console.log(e)

@ -54,7 +54,7 @@
.menu a[active] { .menu a[active] {
background: #409EFF; background: #c3e1ff;
} }
.content { .content {
@ -84,13 +84,13 @@
<a href="@/">首页</a> <a href="@/">首页</a>
</div> </div>
<a class="ml-auto" href="/">应用权限管理</a> <a class="ml-auto" href="/">应用权限管理</a>
<!-- 新增跳转到/home的链接 --> <div vsrc='ico.html' class="ml-auto" style="margin-left: auto;"></div>
<div refu='ico' class="ml-auto" style="margin-left: auto;"></div>
</header> </header>
<div class="main-container"> <div class="main-container">
<vslot v='user' class="menu" name='menu'> <vslot v='user' class="menu" name='menu'>
<a href="/" class="">HOME</a>
<a href="/app" class="">应用管理</a> <a href="/app" class="">应用管理</a>
<a href="/profile" class="">个人中心</a> <a href="/profile" class="">个人中心</a>
<a href="/settings">系统设置</a> <a href="/settings">系统设置</a>

@ -7,6 +7,12 @@
<title>应用</title> <title>应用</title>
<style> <style>
body {
width: 100%;
height: 100%;
padding: 20px;
}
header { header {
display: flex; display: flex;
justify-content: start; justify-content: start;
@ -19,7 +25,7 @@
header h1 { header h1 {
font-size: 2.5rem; font-size: 2.5rem;
font-weight: 700; font-weight: 700;
color: var(--primary-color); color: var(--color-primary);
margin: 0; margin: 0;
} }
@ -42,7 +48,7 @@
.search-bar input:focus { .search-bar input:focus {
outline: none; outline: none;
border-color: var(--primary-color); border-color: var(--color-primary);
box-shadow: 0 0 0 3px rgba(74, 108, 247, 0.2); box-shadow: 0 0 0 3px rgba(74, 108, 247, 0.2);
} }
@ -76,9 +82,9 @@
} }
.filter-btn.active { .filter-btn.active {
background-color: var(--primary-color); background-color: var(--color-primary);
color: white; color: white;
border-color: var(--primary-color); border-color: var(--color-primary);
} }
.app-grid { .app-grid {
@ -200,8 +206,8 @@
} }
.btn-primary { .btn-primary {
background-color: var(--primary-color); background-color: var(--color-primary);
color: white; color: var(--color-text);
border: none; border: none;
} }
@ -211,8 +217,8 @@
.btn-outline { .btn-outline {
background-color: transparent; background-color: transparent;
color: var(--primary-color); color: var(--color-primary);
border: 1px solid var(--primary-color); border: 1px solid var(--color-primary);
} }
.btn-outline:hover { .btn-outline:hover {
@ -232,7 +238,7 @@
width: 40px; width: 40px;
height: 40px; height: 40px;
border: 4px solid rgba(74, 108, 247, 0.1); border: 4px solid rgba(74, 108, 247, 0.1);
border-left-color: var(--primary-color); border-left-color: var(--color-primary);
border-radius: 50%; border-radius: 50%;
animation: spin 1s linear infinite; animation: spin 1s linear infinite;
margin-bottom: 15px; margin-bottom: 15px;

@ -42,6 +42,8 @@
<div class="sub-header">角色表</div> <div class="sub-header">角色表</div>
<v-table :axios='$axios' :onerr="onerr" :keys="role_keys" :api='role_url'> <v-table :axios='$axios' :onerr="onerr" :keys="role_keys" :api='role_url'>
<v-btn vslot='_addon' size='sm' @click='show_user(row)' color='#2c9'>权限表</v-btn> <v-btn vslot='_addon' size='sm' @click='show_user(row)' color='#2c9'>权限表</v-btn>
<v-btn v-if='row.id!==$G.app.init_role_id' vslot='_addon' size='sm' @click='update_app({init_role_id: row.id})'
color='#953'>设为初始角色</v-btn>
</v-table> </v-table>
<v-dialog v:show='show'> <v-dialog v:show='show'>
<div class="dialog"> <div class="dialog">
@ -73,7 +75,7 @@
{name: 'id', label: 'ID', disabled: true, style: {width: '2rem'}}, {name: 'id', label: 'ID', disabled: true, style: {width: '2rem'}},
{name: 'name', required: true, label: '资源名'}, {name: 'name', required: true, label: '资源名'},
{name: 'tid', label: '限制域', editable: true}, {name: 'tid', label: '限制域', editable: true},
{name: 'level', label: '权限等级', editable: true}, {name: 'level', label: '权限等级', type: 'number', editable: true},
{name: 'created_at', label: '创建时间', disabled: true, field: (r) => new Date(r.created_at).toLocaleString()}, {name: 'created_at', label: '创建时间', disabled: true, field: (r) => new Date(r.created_at).toLocaleString()},
{name: 'updated_at', label: '更新时间', disabled: true, field: (r) => new Date(r.updated_at).toLocaleString()}, {name: 'updated_at', label: '更新时间', disabled: true, field: (r) => new Date(r.updated_at).toLocaleString()},
] ]
@ -92,10 +94,20 @@
access_url = `` access_url = ``
role_url = `/api/app/${id}/role` role_url = `/api/app/${id}/role`
user_role_url = `/api/user/_/user_role/${selected_role.id}` user_role_url = `/api/user/_/user_role/${selected_role.id}`
update_app = (props) => {
$axios.patch(`/api/app/${id}`, props).then((res) => {
if (res) {
Object.assign($G.app, res)
}
}).catch((e) => {
console.warn(e)
})
}
onerr = (e) => { onerr = (e) => {
console.warn(e) console.warn(e)
// history.back() // history.back()
} }
console.log($G.app)
</script> </script>
</html> </html>

@ -204,7 +204,7 @@
<i class="fas fa-key"></i> <i class="fas fa-key"></i>
</div> </div>
<div class="stat-info"> <div class="stat-info">
<h3>{{app.id ? app.id.substring(0, 8) : 'N/A'}}</h3> <h3>{{$G.app.id ? $G.app.id.substring(0, 8) : 'N/A'}}</h3>
<p>项目ID (缩略)</p> <p>项目ID (缩略)</p>
</div> </div>
</div> </div>

@ -35,7 +35,7 @@
<v-dialog v:show='show'> <v-dialog v:show='show'>
<div class="dialog"> <div class="dialog">
<div class="text-2xl">{{selected.username}} 角色表</div> <div class="text-2xl">{{selected.username}} 角色表</div>
<v-table :axios='$axios' :keys="au_keys" :api='`/api/user/${app.id}/user_role`'></v-table> <v-table :axios='$axios' :keys="au_keys" :api='`/api/user/${$G.app.id}/user_role`'></v-table>
</div> </div>
</v-dialog> </v-dialog>
</body> </body>

@ -8,6 +8,8 @@
</head> </head>
<style> <style>
body { body {
max-width: 600px;
margin: 0 auto;
border-radius: 8px; border-radius: 8px;
padding: 20px; padding: 20px;
display: flex; display: flex;
@ -69,7 +71,6 @@
<body> <body>
<h1 class="text-2xl text-center">个人信息修改</h1> <h1 class="text-2xl text-center">个人信息修改</h1>
<div class="form-group"> <div class="form-group">
<label>用户名</label> <label>用户名</label>
<input type="text" !value="user.username" @input="user.username = $event.target.value" placeholder="请输入用户名"> <input type="text" !value="user.username" @input="user.username = $event.target.value" placeholder="请输入用户名">
@ -104,11 +105,12 @@
<script setup> <script setup>
// 初始化用户数据 // 初始化用户数据
user = { user = {
id: Guser.uid, id: $G.token?.body()?.uid,
username: Guser.username, username: '',
nickname: "", nickname: "",
icon: Guser.icon || "", icon: "",
email: "", email: "",
phone: "", phone: "",
status: 0 status: 0

@ -6,11 +6,11 @@
*/ */
const routes = [ const routes = [
{ path: '/', component: '/page/index.html', name: 'home', meta: { auth: true } }, { path: '/', component: '/page/index.html', name: 'home', layout: 'default', meta: { auth: true } },
{ path: '/login', component: '/page/login.html', name: 'login', meta: { auth: false } }, { path: '/login', component: '/page/login.html', name: 'login', meta: { auth: false } },
{ path: '/profile', component: '/page/profile.html', name: 'profile', meta: { auth: true } }, { path: '/profile', component: '/page/profile.html', layout: 'default', meta: { auth: true } },
{ path: '/app', component: '/page/app.html', name: 'app', meta: { auth: true } }, { path: '/app', component: '/page/app.html', name: 'app', layout: 'default', meta: { auth: true } },
{ path: '/settings', component: '/page/settings.html', name: 'settings', meta: { auth: true } }, { path: '/settings', component: '/page/settings.html', layout: 'default', meta: { auth: true } },
{ {
path: '/app/:id', layout: 'app', meta: { auth: true }, path: '/app/:id', layout: 'app', meta: { auth: true },
children: [ children: [

@ -69,11 +69,16 @@ class TokenService {
} }
return this.__cache return this.__cache
} }
logout(to) { logout(to, querys) {
this.clearToken(); this.clearToken();
let url = this.#url + '/login'; let url = new URL(this.#url, window.location.origin)
url += '?redirect=' let redirect = to || window.location.pathname
location.href = url + (to || window.location.pathname); url.searchParams.set('redirect', redirect)
for (let key in querys) {
url.searchParams.set(key, querys[key]);
}
url.pathname += '/login'
location.href = url.toString()
} }
async refresh() { async refresh() {
@ -87,13 +92,13 @@ class TokenService {
method: 'post', method: 'post',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ refresh: refreshToken }) body: JSON.stringify({ refresh: refreshToken })
}).then(res => res.json()) }).then(res => res.text())
this.__cache = null; // 清除缓存 this.__cache = null; // 清除缓存
this.setToken(data); this.setToken(data);
} catch (e) { } catch (e) {
console.error('Token刷新失败:', e); console.error('Token刷新失败:', e);
this.clearToken() this.clearToken()
// logout(); logout();
} }
} }
isExpired() { isExpired() {
@ -163,7 +168,7 @@ class TokenService {
// 如果重试次数超过 3 次,则不再重试,直接跳转到登录页 // 如果重试次数超过 3 次,则不再重试,直接跳转到登录页
if (originalRequest.__retryCount >= 3) { if (originalRequest.__retryCount >= 3) {
that.logout() // that.logout()
// 拒绝原始请求的 Promise停止后续处理 // 拒绝原始请求的 Promise停止后续处理
return Promise.reject(error); return Promise.reject(error);
} }
@ -204,7 +209,7 @@ class TokenService {
// 拒绝等待队列中的所有请求 // 拒绝等待队列中的所有请求
processQueue(refreshError); processQueue(refreshError);
// that.clearToken(); // that.clearToken();
// that.logout(); that.logout();
// 拒绝原始请求的 Promise // 拒绝原始请求的 Promise
return Promise.reject(refreshError); return Promise.reject(refreshError);
} finally { } finally {

Loading…
Cancel
Save