Compare commits
No commits in common. 'fa896b66b6c9af181b3f8319a13476f9bf3d7246' and '59ae7e72f0e4295af763633e78bd1321f7bbd0c1' have entirely different histories.
fa896b66b6
...
59ae7e72f0
@ -1 +0,0 @@ |
|||||||
package main |
|
@ -0,0 +1,140 @@ |
|||||||
|
package middleware |
||||||
|
|
||||||
|
import ( |
||||||
|
"github.com/golang-jwt/jwt/v5" |
||||||
|
"github.com/labstack/echo-jwt/v4" |
||||||
|
"github.com/labstack/echo/v4" |
||||||
|
) |
||||||
|
|
||||||
|
// JWTConfig defines the config for JWT middleware.
|
||||||
|
type JWTConfig struct { |
||||||
|
// Skipper defines a function to skip middleware.
|
||||||
|
Skipper Skipper |
||||||
|
|
||||||
|
// BeforeFunc defines a function which is executed just before the middleware.
|
||||||
|
BeforeFunc BeforeFunc |
||||||
|
|
||||||
|
// SuccessHandler defines a function which is executed for a valid token.
|
||||||
|
SuccessHandler func(c echo.Context) |
||||||
|
|
||||||
|
// ErrorHandler defines a function which is executed when all lookups have been done and none of them passed Validator
|
||||||
|
// function. ErrorHandler is executed with last missing (ErrExtractionValueMissing) or an invalid key.
|
||||||
|
// It may be used to define a custom JWT error.
|
||||||
|
//
|
||||||
|
// Note: when error handler swallows the error (returns nil) middleware continues handler chain execution towards handler.
|
||||||
|
// This is useful in cases when portion of your site/api is publicly accessible and has extra features for authorized users
|
||||||
|
// In that case you can use ErrorHandler to set default public JWT token value to request and continue with handler chain.
|
||||||
|
ErrorHandler func(c echo.Context, err error) error |
||||||
|
|
||||||
|
// ContinueOnIgnoredError allows the next middleware/handler to be called when ErrorHandler decides to
|
||||||
|
// ignore the error (by returning `nil`).
|
||||||
|
// This is useful when parts of your site/api allow public access and some authorized routes provide extra functionality.
|
||||||
|
// In that case you can use ErrorHandler to set a default public JWT token value in the request context
|
||||||
|
// and continue. Some logic down the remaining execution chain needs to check that (public) token value then.
|
||||||
|
ContinueOnIgnoredError bool |
||||||
|
|
||||||
|
// Context key to store user information from the token into context.
|
||||||
|
// Optional. Default value "user".
|
||||||
|
ContextKey string |
||||||
|
|
||||||
|
// Signing key to validate token.
|
||||||
|
// This is one of the three options to provide a token validation key.
|
||||||
|
// The order of precedence is a user-defined KeyFunc, SigningKeys and SigningKey.
|
||||||
|
// Required if neither user-defined KeyFunc nor SigningKeys is provided.
|
||||||
|
SigningKey any |
||||||
|
|
||||||
|
// Map of signing keys to validate token with kid field usage.
|
||||||
|
// This is one of the three options to provide a token validation key.
|
||||||
|
// The order of precedence is a user-defined KeyFunc, SigningKeys and SigningKey.
|
||||||
|
// Required if neither user-defined KeyFunc nor SigningKey is provided.
|
||||||
|
SigningKeys map[string]any |
||||||
|
|
||||||
|
// Signing method used to check the token's signing algorithm.
|
||||||
|
// Optional. Default value HS256.
|
||||||
|
SigningMethod string |
||||||
|
|
||||||
|
// KeyFunc defines a user-defined function that supplies the public key for a token validation.
|
||||||
|
// The function shall take care of verifying the signing algorithm and selecting the proper key.
|
||||||
|
// A user-defined KeyFunc can be useful if tokens are issued by an external party.
|
||||||
|
// Used by default ParseTokenFunc implementation.
|
||||||
|
//
|
||||||
|
// When a user-defined KeyFunc is provided, SigningKey, SigningKeys, and SigningMethod are ignored.
|
||||||
|
// This is one of the three options to provide a token validation key.
|
||||||
|
// The order of precedence is a user-defined KeyFunc, SigningKeys and SigningKey.
|
||||||
|
// Required if neither SigningKeys nor SigningKey is provided.
|
||||||
|
// Not used if custom ParseTokenFunc is set.
|
||||||
|
// Default to an internal implementation verifying the signing algorithm and selecting the proper key.
|
||||||
|
KeyFunc jwt.Keyfunc |
||||||
|
|
||||||
|
// TokenLookup is a string in the form of "<source>:<name>" or "<source>:<name>,<source>:<name>" that is used
|
||||||
|
// to extract token from the request.
|
||||||
|
// Optional. Default value "header:Authorization".
|
||||||
|
// Possible values:
|
||||||
|
// - "header:<name>" or "header:<name>:<cut-prefix>"
|
||||||
|
// `<cut-prefix>` is argument value to cut/trim prefix of the extracted value. This is useful if header
|
||||||
|
// value has static prefix like `Authorization: <auth-scheme> <authorisation-parameters>` where part that we
|
||||||
|
// want to cut is `<auth-scheme> ` note the space at the end.
|
||||||
|
// In case of JWT tokens `Authorization: Bearer <token>` prefix we cut is `Bearer `.
|
||||||
|
// If prefix is left empty the whole value is returned.
|
||||||
|
// - "query:<name>"
|
||||||
|
// - "param:<name>"
|
||||||
|
// - "cookie:<name>"
|
||||||
|
// - "form:<name>"
|
||||||
|
// Multiple sources example:
|
||||||
|
// - "header:Authorization:Bearer ,cookie:myowncookie"
|
||||||
|
TokenLookup string |
||||||
|
|
||||||
|
// TokenLookupFuncs defines a list of user-defined functions that extract JWT token from the given context.
|
||||||
|
// This is one of the two options to provide a token extractor.
|
||||||
|
// The order of precedence is user-defined TokenLookupFuncs, and TokenLookup.
|
||||||
|
// You can also provide both if you want.
|
||||||
|
TokenLookupFuncs []ValuesExtractor |
||||||
|
|
||||||
|
// ParseTokenFunc defines a user-defined function that parses token from given auth. Returns an error when token
|
||||||
|
// parsing fails or parsed token is invalid.
|
||||||
|
// Defaults to implementation using `github.com/golang-jwt/jwt` as JWT implementation library
|
||||||
|
ParseTokenFunc func(c echo.Context, auth string) (any, error) |
||||||
|
|
||||||
|
// Claims are extendable claims data defining token content. Used by default ParseTokenFunc implementation.
|
||||||
|
// Not used if custom ParseTokenFunc is set.
|
||||||
|
// Optional. Defaults to function returning jwt.MapClaims
|
||||||
|
NewClaimsFunc func(c echo.Context) jwt.Claims |
||||||
|
} |
||||||
|
|
||||||
|
// Errors
|
||||||
|
var ( |
||||||
|
ErrJWTMissing = echojwt.ErrJWTMissing |
||||||
|
ErrJWTInvalid = echojwt.ErrJWTInvalid |
||||||
|
) |
||||||
|
|
||||||
|
func (config *JWTConfig) ToMiddleware() echo.MiddlewareFunc { |
||||||
|
return echojwt.WithConfig(echojwt.Config{ |
||||||
|
Skipper: config.Skipper, |
||||||
|
BeforeFunc: config.BeforeFunc, |
||||||
|
SuccessHandler: config.SuccessHandler, |
||||||
|
ErrorHandler: config.ErrorHandler, |
||||||
|
ContinueOnIgnoredError: config.ContinueOnIgnoredError, |
||||||
|
ContextKey: config.ContextKey, |
||||||
|
SigningKey: config.SigningKey, |
||||||
|
SigningKeys: config.SigningKeys, |
||||||
|
SigningMethod: config.SigningMethod, |
||||||
|
KeyFunc: config.KeyFunc, |
||||||
|
TokenLookup: config.TokenLookup, |
||||||
|
TokenLookupFuncs: config.TokenLookupFuncs, |
||||||
|
ParseTokenFunc: config.ParseTokenFunc, |
||||||
|
NewClaimsFunc: nil, |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
// JWT returns a JSON Web Token (JWT) auth middleware.
|
||||||
|
//
|
||||||
|
// For valid token, it sets the user in context and calls next handler.
|
||||||
|
// For invalid token, it returns "401 - Unauthorized" error.
|
||||||
|
// For missing token, it returns "400 - Bad Request" error.
|
||||||
|
//
|
||||||
|
// See: https://jwt.io/introduction
|
||||||
|
// See `JWTConfig.TokenLookup`
|
||||||
|
// See https://github.com/labstack/echo-jwt
|
||||||
|
func JWT(signingKey any) echo.MiddlewareFunc { |
||||||
|
return echojwt.JWT(signingKey) |
||||||
|
} |
@ -1,104 +0,0 @@ |
|||||||
package middleware |
|
||||||
|
|
||||||
import ( |
|
||||||
"crypto/rsa" |
|
||||||
"github.com/golang-jwt/jwt/v5" |
|
||||||
"github.com/labstack/echo/v4" |
|
||||||
"slices" |
|
||||||
"sorbet/pkg/env" |
|
||||||
"sorbet/pkg/ticket" |
|
||||||
) |
|
||||||
|
|
||||||
var ticketPublicKey *rsa.PublicKey |
|
||||||
|
|
||||||
type TicketConfig struct { |
|
||||||
Skipper Skipper |
|
||||||
Anonymously bool |
|
||||||
Audiences []string |
|
||||||
PublicKey *rsa.PublicKey |
|
||||||
TicketFinder ticket.Finder |
|
||||||
ClaimsLooker func(c echo.Context, claims *ticket.Claims) error |
|
||||||
ErrorHandler func(c echo.Context, err error) error |
|
||||||
SuccessHandler func(c echo.Context) |
|
||||||
} |
|
||||||
|
|
||||||
func Ticket(anonymously bool, roles ...string) echo.MiddlewareFunc { |
|
||||||
return TicketWithConfig(TicketConfig{ |
|
||||||
Anonymously: anonymously, |
|
||||||
TicketFinder: ticket.DefaultFinder, |
|
||||||
ClaimsLooker: func(c echo.Context, claims *ticket.Claims) error { |
|
||||||
if len(roles) > 0 && slices.Contains(roles, claims.Role) { |
|
||||||
return nil |
|
||||||
} |
|
||||||
return ticket.ErrUnauthorized |
|
||||||
}, |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
func TicketWithConfig(config TicketConfig) echo.MiddlewareFunc { |
|
||||||
return config.ToMiddleware() |
|
||||||
} |
|
||||||
|
|
||||||
func (t *TicketConfig) ToMiddleware() echo.MiddlewareFunc { |
|
||||||
if t.Skipper == nil { |
|
||||||
t.Skipper = DefaultSkipper |
|
||||||
} |
|
||||||
if len(t.Audiences) == 0 { |
|
||||||
t.Audiences = append(t.Audiences, "*") |
|
||||||
} |
|
||||||
if t.TicketFinder == nil { |
|
||||||
t.TicketFinder = ticket.DefaultFinder |
|
||||||
} |
|
||||||
if t.ErrorHandler == nil { |
|
||||||
t.ErrorHandler = func(c echo.Context, err error) error { |
|
||||||
return err |
|
||||||
} |
|
||||||
} |
|
||||||
if t.ClaimsLooker == nil { |
|
||||||
t.ClaimsLooker = func(c echo.Context, claims *ticket.Claims) error { |
|
||||||
return nil |
|
||||||
} |
|
||||||
} |
|
||||||
return func(next echo.HandlerFunc) echo.HandlerFunc { |
|
||||||
return func(c echo.Context) error { |
|
||||||
if t.Skipper(c) { |
|
||||||
return next(c) |
|
||||||
} |
|
||||||
ticketString := t.TicketFinder(c.Request()) |
|
||||||
if ticketString == "" { |
|
||||||
if t.Anonymously { |
|
||||||
return next(c) |
|
||||||
} |
|
||||||
return t.ErrorHandler(c, ticket.ErrNoTicketFound) |
|
||||||
} |
|
||||||
publicKey := t.PublicKey |
|
||||||
if publicKey == nil { |
|
||||||
key, err := getTicketPublicKey() |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
publicKey = key |
|
||||||
} |
|
||||||
claims, err := ticket.Verify(ticketString, publicKey, t.Audiences...) |
|
||||||
if err != nil { |
|
||||||
return t.ErrorHandler(c, err) |
|
||||||
} |
|
||||||
if err = t.ClaimsLooker(c, claims); err != nil { |
|
||||||
return t.ErrorHandler(c, err) |
|
||||||
} |
|
||||||
c.Set("ticket", ticketString) |
|
||||||
c.Set("ticket_claims", claims) |
|
||||||
return next(c) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func getTicketPublicKey() (*rsa.PublicKey, error) { |
|
||||||
if ticketPublicKey != nil { |
|
||||||
return ticketPublicKey, nil |
|
||||||
} |
|
||||||
var err error |
|
||||||
source := []byte(env.String("TICKET_PUBLIC_KEY")) |
|
||||||
ticketPublicKey, err = jwt.ParseRSAPublicKeyFromPEM(source) |
|
||||||
return ticketPublicKey, err |
|
||||||
} |
|
@ -1,16 +0,0 @@ |
|||||||
package controller |
|
||||||
|
|
||||||
import ( |
|
||||||
"github.com/labstack/echo/v4" |
|
||||||
"sorbet/internal/entities" |
|
||||||
"sorbet/internal/services/company/request" |
|
||||||
"sorbet/internal/util" |
|
||||||
) |
|
||||||
|
|
||||||
type CompanyController struct { |
|
||||||
util.Controller[entities.Company, request.CompanyUpsertRequest] |
|
||||||
} |
|
||||||
|
|
||||||
func (ctr *CompanyController) InitRoutes(r *echo.Group) { |
|
||||||
ctr.RegisterRoutes("/companies", r) |
|
||||||
} |
|
@ -1,16 +0,0 @@ |
|||||||
package controller |
|
||||||
|
|
||||||
import ( |
|
||||||
"github.com/labstack/echo/v4" |
|
||||||
"sorbet/internal/entities" |
|
||||||
"sorbet/internal/services/company/request" |
|
||||||
"sorbet/internal/util" |
|
||||||
) |
|
||||||
|
|
||||||
type CompanyDepartmentController struct { |
|
||||||
util.Controller[entities.CompanyDepartment, request.CompanyDepartmentUpsertRequest] |
|
||||||
} |
|
||||||
|
|
||||||
func (c *CompanyDepartmentController) InitRoutes(r *echo.Group) { |
|
||||||
c.RegisterRoutes("/company/departments", r) |
|
||||||
} |
|
@ -1,16 +0,0 @@ |
|||||||
package controller |
|
||||||
|
|
||||||
import ( |
|
||||||
"github.com/labstack/echo/v4" |
|
||||||
"sorbet/internal/entities" |
|
||||||
"sorbet/internal/services/company/request" |
|
||||||
"sorbet/internal/util" |
|
||||||
) |
|
||||||
|
|
||||||
type CompanyStaffController struct { |
|
||||||
util.Controller[entities.CompanyStaff, request.CompanyStaffUpsertRequest] |
|
||||||
} |
|
||||||
|
|
||||||
func (c *CompanyStaffController) InitRoutes(r *echo.Group) { |
|
||||||
c.RegisterRoutes("/company/staffs", r) |
|
||||||
} |
|
@ -1,42 +0,0 @@ |
|||||||
package request |
|
||||||
|
|
||||||
import ( |
|
||||||
"sorbet/internal/entities" |
|
||||||
"sorbet/pkg/db" |
|
||||||
) |
|
||||||
|
|
||||||
type CompanyDepartmentUpsertRequest struct { |
|
||||||
ID uint `json:"id" xml:"id" form:"id" path:"id"` |
|
||||||
PID uint `json:"pid" xml:"pid" form:"pid"` |
|
||||||
CompanyID uint `json:"company_id" xml:"company_id" form:"company_id"` |
|
||||||
PrincipalID uint `json:"principal_id" xml:"principal_id" form:"principal_id"` |
|
||||||
Name string `json:"name" xml:"name" form:"name"` |
|
||||||
Sort int32 `json:"sort" xml:"sort" form:"sort"` |
|
||||||
} |
|
||||||
|
|
||||||
func (c *CompanyDepartmentUpsertRequest) GetID() any { |
|
||||||
return c.ID |
|
||||||
} |
|
||||||
|
|
||||||
func (c *CompanyDepartmentUpsertRequest) ToEntity() any { |
|
||||||
return &entities.CompanyDepartment{ |
|
||||||
ID: c.ID, |
|
||||||
PID: &c.PID, |
|
||||||
CompanyID: c.CompanyID, |
|
||||||
PrincipalID: &c.PrincipalID, |
|
||||||
Name: c.Name, |
|
||||||
Sort: c.Sort, |
|
||||||
Version: db.Version{Int64: 1, Valid: true}, |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (c *CompanyDepartmentUpsertRequest) ToMap() map[string]any { |
|
||||||
return map[string]any{ |
|
||||||
"id": c.ID, |
|
||||||
"pid": c.PID, |
|
||||||
"company_id": c.CompanyID, |
|
||||||
"principal_id": c.PrincipalID, |
|
||||||
"name": c.Name, |
|
||||||
"sort": c.Sort, |
|
||||||
} |
|
||||||
} |
|
@ -1,49 +0,0 @@ |
|||||||
package request |
|
||||||
|
|
||||||
import ( |
|
||||||
"sorbet/internal/entities" |
|
||||||
) |
|
||||||
|
|
||||||
type CompanyStaffUpsertRequest struct { |
|
||||||
ID uint `json:"id" xml:"id" form:"id" path:"id"` |
|
||||||
CompanyID uint `json:"company_id" xml:"company_id" form:"company_id"` |
|
||||||
Name string `json:"name" xml:"name" form:"name"` |
|
||||||
Gender string `json:"gender" xml:"gender" form:"gender"` |
|
||||||
Position string `json:"position" xml:"position" form:"position"` |
|
||||||
PhoneNumber string `json:"phone_number" xml:"phone_number" form:"phone_number"` |
|
||||||
WechatOpenid string `json:"wechat_openid" xml:"wechat_openid" form:"wechat_openid"` |
|
||||||
WithoutStudy bool `json:"without_study" xml:"without_study" form:"without_study"` |
|
||||||
IsAdmin bool `json:"is_admin" xml:"is_admin" form:"is_admin"` |
|
||||||
} |
|
||||||
|
|
||||||
func (c *CompanyStaffUpsertRequest) GetID() any { |
|
||||||
return c.ID |
|
||||||
} |
|
||||||
|
|
||||||
func (c *CompanyStaffUpsertRequest) ToMap() map[string]any { |
|
||||||
return map[string]any{ |
|
||||||
"id": c.ID, |
|
||||||
"company_id": c.CompanyID, |
|
||||||
"name": c.Name, |
|
||||||
"gender": c.Gender, |
|
||||||
"position": c.Position, |
|
||||||
"phone_number": c.PhoneNumber, |
|
||||||
"wechat_openid": c.WechatOpenid, |
|
||||||
"without_study": c.WithoutStudy, |
|
||||||
"is_admin": c.IsAdmin, |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (c *CompanyStaffUpsertRequest) ToEntity() any { |
|
||||||
return &entities.CompanyStaff{ |
|
||||||
ID: c.ID, |
|
||||||
CompanyID: c.CompanyID, |
|
||||||
Name: c.Name, |
|
||||||
Gender: c.Gender, |
|
||||||
Position: c.Position, |
|
||||||
PhoneNumber: c.PhoneNumber, |
|
||||||
WechatOpenid: c.WechatOpenid, |
|
||||||
WithoutStudy: c.WithoutStudy, |
|
||||||
IsAdmin: c.IsAdmin, |
|
||||||
} |
|
||||||
} |
|
@ -1,39 +0,0 @@ |
|||||||
package request |
|
||||||
|
|
||||||
import ( |
|
||||||
"sorbet/internal/entities" |
|
||||||
"sorbet/pkg/db" |
|
||||||
) |
|
||||||
|
|
||||||
type CompanyUpsertRequest struct { |
|
||||||
ID uint `json:"id" xml:"id" form:"id" path:"id"` |
|
||||||
PrincipalID uint `json:"principal_id" xml:"principal_id" form:"principal_id"` |
|
||||||
Name string `json:"name" xml:"name" form:"name"` |
|
||||||
Logo string `json:"logo" xml:"logo" form:"logo"` |
|
||||||
Status bool `json:"status" xml:"status" form:"status"` |
|
||||||
} |
|
||||||
|
|
||||||
func (r *CompanyUpsertRequest) GetID() any { |
|
||||||
return r.ID |
|
||||||
} |
|
||||||
|
|
||||||
func (r *CompanyUpsertRequest) ToEntity() *entities.Company { |
|
||||||
return &entities.Company{ |
|
||||||
ID: r.ID, |
|
||||||
PrincipalID: &r.PrincipalID, |
|
||||||
Name: r.Name, |
|
||||||
Logo: r.Logo, |
|
||||||
Status: r.Status, |
|
||||||
Version: db.Version{Int64: 1, Valid: true}, |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (r *CompanyUpsertRequest) ToMap() map[string]any { |
|
||||||
return map[string]any{ |
|
||||||
"id": r.ID, |
|
||||||
"principal_id": r.PrincipalID, |
|
||||||
"name": r.Name, |
|
||||||
"logo": r.Logo, |
|
||||||
"status": r.Status, |
|
||||||
} |
|
||||||
} |
|
@ -1,23 +0,0 @@ |
|||||||
package company |
|
||||||
|
|
||||||
import ( |
|
||||||
"sorbet/internal/services/company/controller" |
|
||||||
"sorbet/pkg/app" |
|
||||||
) |
|
||||||
|
|
||||||
type Service struct{} |
|
||||||
|
|
||||||
func (s *Service) Init(ctx *app.Context) error { |
|
||||||
ctx.Routes(&controller.CompanyController{}) |
|
||||||
ctx.Routes(&controller.CompanyDepartmentController{}) |
|
||||||
ctx.Routes(&controller.CompanyStaffController{}) |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func (s *Service) Start() error { |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func (s *Service) Stop() error { |
|
||||||
return nil |
|
||||||
} |
|
@ -1,16 +0,0 @@ |
|||||||
package controller |
|
||||||
|
|
||||||
import ( |
|
||||||
"github.com/labstack/echo/v4" |
|
||||||
"sorbet/internal/entities" |
|
||||||
"sorbet/internal/services/config/request" |
|
||||||
"sorbet/internal/util" |
|
||||||
) |
|
||||||
|
|
||||||
type ConfigController struct { |
|
||||||
util.Controller[entities.Config, request.ConfigUpsertRequest] |
|
||||||
} |
|
||||||
|
|
||||||
func (ctr *ConfigController) InitRoutes(r *echo.Group) { |
|
||||||
ctr.RegisterRoutes("/config", r) |
|
||||||
} |
|
@ -1,17 +0,0 @@ |
|||||||
package controller |
|
||||||
|
|
||||||
import ( |
|
||||||
"github.com/labstack/echo/v4" |
|
||||||
"sorbet/internal/entities" |
|
||||||
"sorbet/internal/services/config/request" |
|
||||||
"sorbet/internal/util" |
|
||||||
) |
|
||||||
|
|
||||||
type ConfigGroupController struct { |
|
||||||
util.Controller[entities.ConfigGroup, request.ConfigGroupUpsertRequest] |
|
||||||
} |
|
||||||
|
|
||||||
// InitRoutes 实现路由注册接口
|
|
||||||
func (ctr *ConfigGroupController) InitRoutes(r *echo.Group) { |
|
||||||
ctr.RegisterRoutes("/config/groups", r) |
|
||||||
} |
|
@ -1,34 +0,0 @@ |
|||||||
package request |
|
||||||
|
|
||||||
import ( |
|
||||||
"sorbet/internal/entities" |
|
||||||
"sorbet/pkg/db" |
|
||||||
) |
|
||||||
|
|
||||||
type ConfigGroupUpsertRequest struct { |
|
||||||
ID uint `json:"id" xml:"id" path:"id"` |
|
||||||
Name string `json:"name" xml:"name" form:"name"` |
|
||||||
Description string `json:"description" xml:"description" form:"description"` |
|
||||||
Sort int32 `json:"sort" xml:"sort" form:"sort"` |
|
||||||
} |
|
||||||
|
|
||||||
func (c *ConfigGroupUpsertRequest) GetID() any { |
|
||||||
return c.ID |
|
||||||
} |
|
||||||
|
|
||||||
func (c *ConfigGroupUpsertRequest) ToEntity() *entities.ConfigGroup { |
|
||||||
return &entities.ConfigGroup{ |
|
||||||
Name: c.Name, |
|
||||||
Description: c.Description, |
|
||||||
Sort: c.Sort, |
|
||||||
Version: db.Version{Int64: 1, Valid: true}, |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (c *ConfigGroupUpsertRequest) ToMap() map[string]any { |
|
||||||
return map[string]any{ |
|
||||||
"name": c.Name, |
|
||||||
"description": c.Description, |
|
||||||
"sort": c.Sort, |
|
||||||
} |
|
||||||
} |
|
@ -1,50 +0,0 @@ |
|||||||
package request |
|
||||||
|
|
||||||
import ( |
|
||||||
"sorbet/internal/entities" |
|
||||||
"sorbet/pkg/db" |
|
||||||
) |
|
||||||
|
|
||||||
type ConfigUpsertRequest struct { |
|
||||||
ID uint `json:"id" xml:"id" form:"id" path:"id"` |
|
||||||
GroupID uint `json:"group_id" xml:"group_id" form:"group_id"` |
|
||||||
Name string `json:"name" xml:"name" form:"name"` |
|
||||||
Title string `json:"title" xml:"title" form:"title"` |
|
||||||
Description string `json:"description" xml:"description" form:"description"` |
|
||||||
DataType string `json:"data_type" xml:"data_type" form:"data_type"` |
|
||||||
Attributes map[string]any `json:"attributes" xml:"attributes" form:"attributes"` |
|
||||||
Value any `json:"value" xml:"value" form:"value"` |
|
||||||
Sort int32 `json:"sort" xml:"sort" form:"sort"` |
|
||||||
} |
|
||||||
|
|
||||||
func (c *ConfigUpsertRequest) GetID() any { |
|
||||||
return c.ID |
|
||||||
} |
|
||||||
|
|
||||||
func (c *ConfigUpsertRequest) ToEntity() *entities.Config { |
|
||||||
return &entities.Config{ |
|
||||||
ID: c.ID, |
|
||||||
GroupID: c.GroupID, |
|
||||||
Name: c.Name, |
|
||||||
Title: c.Title, |
|
||||||
Description: c.Description, |
|
||||||
DataType: c.DataType, |
|
||||||
Attributes: c.Attributes, |
|
||||||
Value: c.Value, |
|
||||||
Sort: c.Sort, |
|
||||||
Version: db.Version{Int64: 1, Valid: true}, |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (c *ConfigUpsertRequest) ToMap() map[string]any { |
|
||||||
return map[string]any{ |
|
||||||
"group_id": c.GroupID, |
|
||||||
"name": c.Name, |
|
||||||
"title": c.Title, |
|
||||||
"description": c.Description, |
|
||||||
"data_type": c.DataType, |
|
||||||
"attributes": c.Attributes, |
|
||||||
"value": c.Value, |
|
||||||
"sort": c.Sort, |
|
||||||
} |
|
||||||
} |
|
@ -1,23 +0,0 @@ |
|||||||
package config |
|
||||||
|
|
||||||
import ( |
|
||||||
"sorbet/internal/services/config/controller" |
|
||||||
"sorbet/pkg/app" |
|
||||||
) |
|
||||||
|
|
||||||
type Service struct { |
|
||||||
} |
|
||||||
|
|
||||||
func (s *Service) Init(ctx *app.Context) error { |
|
||||||
ctx.Routes(&controller.ConfigGroupController{}) |
|
||||||
ctx.Routes(&controller.ConfigController{}) |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func (s *Service) Start() error { |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func (s *Service) Stop() error { |
|
||||||
return nil |
|
||||||
} |
|
@ -1,16 +0,0 @@ |
|||||||
package controller |
|
||||||
|
|
||||||
import ( |
|
||||||
"github.com/labstack/echo/v4" |
|
||||||
"sorbet/internal/entities" |
|
||||||
"sorbet/internal/services/feature/request" |
|
||||||
"sorbet/internal/util" |
|
||||||
) |
|
||||||
|
|
||||||
type FeatureCategoryController struct { |
|
||||||
util.Controller[entities.FeatureCategory, request.FeatureCategoryUpsertRequest] |
|
||||||
} |
|
||||||
|
|
||||||
func (f *FeatureCategoryController) InitRoutes(r *echo.Group) { |
|
||||||
f.RegisterRoutes("/feature/categories", r) |
|
||||||
} |
|
@ -1,16 +0,0 @@ |
|||||||
package controller |
|
||||||
|
|
||||||
import ( |
|
||||||
"github.com/labstack/echo/v4" |
|
||||||
"sorbet/internal/entities" |
|
||||||
"sorbet/internal/services/feature/request" |
|
||||||
"sorbet/internal/util" |
|
||||||
) |
|
||||||
|
|
||||||
type FeatureConfigController struct { |
|
||||||
util.Controller[entities.FeatureConfig, request.FeatureConfigUpsertRequest] |
|
||||||
} |
|
||||||
|
|
||||||
func (f *FeatureConfigController) InitRoutes(r *echo.Group) { |
|
||||||
f.RegisterRoutes("/feature/configs", r) |
|
||||||
} |
|
@ -1,16 +0,0 @@ |
|||||||
package controller |
|
||||||
|
|
||||||
import ( |
|
||||||
"github.com/labstack/echo/v4" |
|
||||||
"sorbet/internal/entities" |
|
||||||
"sorbet/internal/services/feature/request" |
|
||||||
"sorbet/internal/util" |
|
||||||
) |
|
||||||
|
|
||||||
type FeatureContentChapterController struct { |
|
||||||
util.Controller[entities.FeatureContentChapter, request.FeatureContentChapterUpsertRequest] |
|
||||||
} |
|
||||||
|
|
||||||
func (f *FeatureContentChapterController) InitRoutes(r *echo.Group) { |
|
||||||
f.RegisterRoutes("/feature/content/chapters", r) |
|
||||||
} |
|
@ -1,16 +0,0 @@ |
|||||||
package controller |
|
||||||
|
|
||||||
import ( |
|
||||||
"github.com/labstack/echo/v4" |
|
||||||
"sorbet/internal/entities" |
|
||||||
"sorbet/internal/services/feature/request" |
|
||||||
"sorbet/internal/util" |
|
||||||
) |
|
||||||
|
|
||||||
type FeatureContentController struct { |
|
||||||
util.Controller[entities.FeatureContent, request.FeatureContentUpsertRequest] |
|
||||||
} |
|
||||||
|
|
||||||
func (f *FeatureContentController) InitRoutes(r *echo.Group) { |
|
||||||
f.RegisterRoutes("/feature/contents", r) |
|
||||||
} |
|
@ -1,16 +0,0 @@ |
|||||||
package controller |
|
||||||
|
|
||||||
import ( |
|
||||||
"github.com/labstack/echo/v4" |
|
||||||
"sorbet/internal/entities" |
|
||||||
"sorbet/internal/services/feature/request" |
|
||||||
"sorbet/internal/util" |
|
||||||
) |
|
||||||
|
|
||||||
type FeatureContentDetailController struct { |
|
||||||
util.Controller[entities.FeatureContentDetail, request.FeatureContentDetailUpsertRequest] |
|
||||||
} |
|
||||||
|
|
||||||
func (f *FeatureContentDetailController) InitRoutes(r *echo.Group) { |
|
||||||
f.RegisterRoutes("/feature/content/detail", r) |
|
||||||
} |
|
@ -1,16 +0,0 @@ |
|||||||
package controller |
|
||||||
|
|
||||||
import ( |
|
||||||
"github.com/labstack/echo/v4" |
|
||||||
"sorbet/internal/entities" |
|
||||||
"sorbet/internal/services/feature/request" |
|
||||||
"sorbet/internal/util" |
|
||||||
) |
|
||||||
|
|
||||||
type FeatureController struct { |
|
||||||
util.Controller[entities.Feature, request.FeatureUpsertRequest] |
|
||||||
} |
|
||||||
|
|
||||||
func (f *FeatureController) InitRoutes(r *echo.Group) { |
|
||||||
f.RegisterRoutes("/features", r) |
|
||||||
} |
|
@ -1,43 +0,0 @@ |
|||||||
package request |
|
||||||
|
|
||||||
import ( |
|
||||||
"sorbet/internal/entities" |
|
||||||
) |
|
||||||
|
|
||||||
type FeatureCategoryUpsertRequest struct { |
|
||||||
ID uint `json:"id" xml:"id" form:"id" path:"id"` |
|
||||||
PID uint `json:"pid" xml:"pid" form:"pid"` |
|
||||||
FeatureID uint `json:"feature_id" xml:"feature_id" form:"feature_id"` |
|
||||||
Title string `json:"title" xml:"title" form:"title"` |
|
||||||
Description string `json:"description" xml:"description" form:"description"` |
|
||||||
Sort int32 `json:"sort" xml:"sort" form:"sort"` |
|
||||||
Status bool `json:"status" xml:"status" form:"status"` |
|
||||||
} |
|
||||||
|
|
||||||
func (f *FeatureCategoryUpsertRequest) GetID() any { |
|
||||||
return f.ID |
|
||||||
} |
|
||||||
|
|
||||||
func (f *FeatureCategoryUpsertRequest) ToMap() map[string]any { |
|
||||||
return map[string]any{ |
|
||||||
"id": f.ID, |
|
||||||
"pid": f.PID, |
|
||||||
"feature_id": f.FeatureID, |
|
||||||
"title": f.Title, |
|
||||||
"description": f.Description, |
|
||||||
"sort": f.Sort, |
|
||||||
"status": f.Status, |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (f *FeatureCategoryUpsertRequest) ToEntity() any { |
|
||||||
return &entities.FeatureCategory{ |
|
||||||
ID: f.ID, |
|
||||||
PID: &f.PID, |
|
||||||
FeatureID: f.FeatureID, |
|
||||||
Title: f.Title, |
|
||||||
Description: f.Description, |
|
||||||
Sort: f.Sort, |
|
||||||
Status: f.Status, |
|
||||||
} |
|
||||||
} |
|
@ -1,40 +0,0 @@ |
|||||||
package request |
|
||||||
|
|
||||||
import ( |
|
||||||
"sorbet/internal/entities" |
|
||||||
) |
|
||||||
|
|
||||||
type FeatureConfigUpsertRequest struct { |
|
||||||
ID uint `json:"id" xml:"id" form:"id" path:"id"` |
|
||||||
FeatureID uint `json:"feature_id" xml:"feature_id" form:"feature_id"` |
|
||||||
Status bool `json:"status" xml:"status" form:"status"` |
|
||||||
Categorizable bool `json:"categorizable" xml:"categorizable" form:"categorizable"` |
|
||||||
CategoryDepth int64 `json:"category_depth" xml:"category_depth" form:"category_depth"` |
|
||||||
ContentTypes []string `json:"content_types" xml:"content_types" form:"content_types"` |
|
||||||
} |
|
||||||
|
|
||||||
func (f *FeatureConfigUpsertRequest) GetID() any { |
|
||||||
return f.ID |
|
||||||
} |
|
||||||
|
|
||||||
func (f *FeatureConfigUpsertRequest) ToMap() map[string]any { |
|
||||||
return map[string]any{ |
|
||||||
"id": f.ID, |
|
||||||
"feature_id": f.FeatureID, |
|
||||||
"status": f.Status, |
|
||||||
"categorizable": f.Categorizable, |
|
||||||
"category_depth": f.CategoryDepth, |
|
||||||
"content_types": f.ContentTypes, |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (f *FeatureConfigUpsertRequest) ToEntity() any { |
|
||||||
return &entities.FeatureConfig{ |
|
||||||
ID: f.ID, |
|
||||||
FeatureID: f.FeatureID, |
|
||||||
Status: f.Status, |
|
||||||
Categorizable: f.Categorizable, |
|
||||||
CategoryDepth: f.CategoryDepth, |
|
||||||
ContentTypes: f.ContentTypes, |
|
||||||
} |
|
||||||
} |
|
@ -1,43 +0,0 @@ |
|||||||
package request |
|
||||||
|
|
||||||
import ( |
|
||||||
"sorbet/internal/entities" |
|
||||||
) |
|
||||||
|
|
||||||
type FeatureContentChapterUpsertRequest struct { |
|
||||||
ID uint `json:"id" xml:"id" form:"id" path:"id"` |
|
||||||
PID uint `json:"pid" xml:"pid" form:"pid"` |
|
||||||
FeatureID uint `json:"feature_id" xml:"feature_id" form:"feature_id"` |
|
||||||
ContentID uint `json:"content_id" xml:"content_id" form:"content_id"` |
|
||||||
Title string `json:"title" xml:"title" form:"title"` |
|
||||||
Intro string `json:"intro" xml:"intro" form:"intro"` |
|
||||||
Sort int32 `json:"sort" xml:"sort" form:"sort"` |
|
||||||
} |
|
||||||
|
|
||||||
func (f *FeatureContentChapterUpsertRequest) GetID() any { |
|
||||||
return f.ID |
|
||||||
} |
|
||||||
|
|
||||||
func (f *FeatureContentChapterUpsertRequest) ToMap() map[string]any { |
|
||||||
return map[string]any{ |
|
||||||
"id": f.ID, |
|
||||||
"pid": f.PID, |
|
||||||
"feature_id": f.FeatureID, |
|
||||||
"content_id": f.ContentID, |
|
||||||
"title": f.Title, |
|
||||||
"intro": f.Intro, |
|
||||||
"sort": f.Sort, |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (f *FeatureContentChapterUpsertRequest) ToEntity() any { |
|
||||||
return &entities.FeatureContentChapter{ |
|
||||||
ID: f.ID, |
|
||||||
PID: &f.PID, |
|
||||||
FeatureID: f.FeatureID, |
|
||||||
ContentID: f.ContentID, |
|
||||||
Title: f.Title, |
|
||||||
Intro: f.Intro, |
|
||||||
Sort: f.Sort, |
|
||||||
} |
|
||||||
} |
|
@ -1,55 +0,0 @@ |
|||||||
package request |
|
||||||
|
|
||||||
import ( |
|
||||||
"sorbet/internal/entities" |
|
||||||
) |
|
||||||
|
|
||||||
type FeatureContentDetailUpsertRequest struct { |
|
||||||
ID uint `json:"id" xml:"id" form:"id" path:"id"` |
|
||||||
FeatureID uint `json:"feature_id" xml:"feature_id" form:"feature_id"` |
|
||||||
ChapterID uint `json:"chapter_id" xml:"chapter_id" form:"chapter_id"` |
|
||||||
ContentID uint `json:"content_id" xml:"content_id" form:"content_id"` |
|
||||||
Type string `json:"type" xml:"type" form:"type"` |
|
||||||
Title string `json:"title" xml:"title" form:"title"` |
|
||||||
Intro string `json:"intro" xml:"intro" form:"intro"` |
|
||||||
PosterUrl string `json:"poster_url" xml:"poster_url" form:"poster_url"` |
|
||||||
VideoUrl string `json:"video_url" xml:"video_url" form:"video_url"` |
|
||||||
Text string `json:"text" xml:"text" form:"text"` |
|
||||||
Attributes map[string]any `json:"attributes" xml:"attributes" form:"attributes"` |
|
||||||
} |
|
||||||
|
|
||||||
func (f *FeatureContentDetailUpsertRequest) GetID() any { |
|
||||||
return f.ID |
|
||||||
} |
|
||||||
|
|
||||||
func (f *FeatureContentDetailUpsertRequest) ToMap() map[string]any { |
|
||||||
return map[string]any{ |
|
||||||
"id": f.ID, |
|
||||||
"feature_id": f.FeatureID, |
|
||||||
"chapter_id": f.ChapterID, |
|
||||||
"content_id": f.ContentID, |
|
||||||
"type": f.Type, |
|
||||||
"title": f.Title, |
|
||||||
"intro": f.Intro, |
|
||||||
"poster_url": f.PosterUrl, |
|
||||||
"video_url": f.VideoUrl, |
|
||||||
"text": f.Text, |
|
||||||
"attributes": f.Attributes, |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (f *FeatureContentDetailUpsertRequest) ToEntity() any { |
|
||||||
return &entities.FeatureContentDetail{ |
|
||||||
ID: f.ID, |
|
||||||
FeatureID: f.FeatureID, |
|
||||||
ChapterID: &f.ChapterID, |
|
||||||
ContentID: f.ContentID, |
|
||||||
Type: f.Type, |
|
||||||
Title: f.Title, |
|
||||||
Intro: f.Intro, |
|
||||||
PosterUrl: f.PosterUrl, |
|
||||||
VideoUrl: f.VideoUrl, |
|
||||||
Text: f.Text, |
|
||||||
Attributes: f.Attributes, |
|
||||||
} |
|
||||||
} |
|
@ -1,40 +0,0 @@ |
|||||||
package request |
|
||||||
|
|
||||||
import ( |
|
||||||
"sorbet/internal/entities" |
|
||||||
) |
|
||||||
|
|
||||||
type FeatureContentUpsertRequest struct { |
|
||||||
ID uint `json:"id" xml:"id" form:"id" path:"id"` |
|
||||||
FeatureID uint `json:"feature_id" xml:"feature_id" form:"feature_id"` |
|
||||||
CategoryID *uint `json:"category_id" xml:"category_id" form:"category_id"` |
|
||||||
Type string `json:"type" xml:"type" form:"type"` |
|
||||||
Title string `json:"title" xml:"title" form:"title"` |
|
||||||
Intro string `json:"intro" xml:"intro" form:"intro"` |
|
||||||
} |
|
||||||
|
|
||||||
func (f *FeatureContentUpsertRequest) GetID() any { |
|
||||||
return f.ID |
|
||||||
} |
|
||||||
|
|
||||||
func (f *FeatureContentUpsertRequest) ToMap() map[string]any { |
|
||||||
return map[string]any{ |
|
||||||
"id": f.ID, |
|
||||||
"feature_id": f.FeatureID, |
|
||||||
"category_id": f.CategoryID, |
|
||||||
"type": f.Type, |
|
||||||
"title": f.Title, |
|
||||||
"intro": f.Intro, |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (f *FeatureContentUpsertRequest) ToEntity() any { |
|
||||||
return &entities.FeatureContent{ |
|
||||||
ID: f.ID, |
|
||||||
FeatureID: f.FeatureID, |
|
||||||
CategoryID: f.CategoryID, |
|
||||||
Type: f.Type, |
|
||||||
Title: f.Title, |
|
||||||
Intro: f.Intro, |
|
||||||
} |
|
||||||
} |
|
@ -1,37 +0,0 @@ |
|||||||
package request |
|
||||||
|
|
||||||
import ( |
|
||||||
"sorbet/internal/entities" |
|
||||||
) |
|
||||||
|
|
||||||
type FeatureUpsertRequest struct { |
|
||||||
ID uint `json:"id" xml:"id" form:"id" path:"id"` |
|
||||||
Title string `json:"title" xml:"title" form:"title"` |
|
||||||
Intro string `json:"intro" xml:"intro" form:"intro"` |
|
||||||
Icon string `json:"icon" xml:"icon" form:"icon"` |
|
||||||
Sort int32 `json:"sort" xml:"sort" form:"sort"` |
|
||||||
} |
|
||||||
|
|
||||||
func (f *FeatureUpsertRequest) GetID() any { |
|
||||||
return f.ID |
|
||||||
} |
|
||||||
|
|
||||||
func (f *FeatureUpsertRequest) ToMap() map[string]any { |
|
||||||
return map[string]any{ |
|
||||||
"id": f.ID, |
|
||||||
"title": f.Title, |
|
||||||
"intro": f.Intro, |
|
||||||
"icon": f.Icon, |
|
||||||
"sort": f.Sort, |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (f *FeatureUpsertRequest) ToEntity() any { |
|
||||||
return &entities.Feature{ |
|
||||||
ID: f.ID, |
|
||||||
Title: f.Title, |
|
||||||
Intro: f.Intro, |
|
||||||
Icon: f.Icon, |
|
||||||
Sort: f.Sort, |
|
||||||
} |
|
||||||
} |
|
@ -1,25 +0,0 @@ |
|||||||
package feature |
|
||||||
|
|
||||||
import ( |
|
||||||
"sorbet/internal/services/feature/controller" |
|
||||||
"sorbet/pkg/app" |
|
||||||
) |
|
||||||
|
|
||||||
type Service struct{} |
|
||||||
|
|
||||||
func (s *Service) Init(ctx *app.Context) error { |
|
||||||
ctx.Routes(&controller.FeatureCategoryController{}) |
|
||||||
ctx.Routes(&controller.FeatureConfigController{}) |
|
||||||
ctx.Routes(&controller.FeatureContentChapterController{}) |
|
||||||
ctx.Routes(&controller.FeatureContentDetailController{}) |
|
||||||
ctx.Routes(&controller.FeatureController{}) |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func (s *Service) Start() error { |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func (s *Service) Stop() error { |
|
||||||
return nil |
|
||||||
} |
|
@ -1,16 +0,0 @@ |
|||||||
package controller |
|
||||||
|
|
||||||
import ( |
|
||||||
"github.com/labstack/echo/v4" |
|
||||||
"sorbet/internal/entities" |
|
||||||
"sorbet/internal/services/resource/request" |
|
||||||
"sorbet/internal/util" |
|
||||||
) |
|
||||||
|
|
||||||
type ResourceCategoryController struct { |
|
||||||
util.Controller[entities.Resource, request.ResourceCategoryUpsertRequest] |
|
||||||
} |
|
||||||
|
|
||||||
func (ctr *ResourceCategoryController) InitRoutes(r *echo.Group) { |
|
||||||
ctr.RegisterRoutes("/resource/categories", r) |
|
||||||
} |
|
@ -1,16 +0,0 @@ |
|||||||
package controller |
|
||||||
|
|
||||||
import ( |
|
||||||
"github.com/labstack/echo/v4" |
|
||||||
"sorbet/internal/entities" |
|
||||||
"sorbet/internal/services/resource/request" |
|
||||||
"sorbet/internal/util" |
|
||||||
) |
|
||||||
|
|
||||||
type ResourceController struct { |
|
||||||
util.Controller[entities.Resource, request.ResourceUpsertRequest] |
|
||||||
} |
|
||||||
|
|
||||||
func (ctr *ResourceController) InitRoutes(r *echo.Group) { |
|
||||||
ctr.RegisterRoutes("/resources", r) |
|
||||||
} |
|
@ -1,37 +0,0 @@ |
|||||||
package request |
|
||||||
|
|
||||||
import ( |
|
||||||
"sorbet/internal/entities" |
|
||||||
) |
|
||||||
|
|
||||||
type ResourceCategoryUpsertRequest struct { |
|
||||||
ID uint `json:"id" xml:"id" form:"id" path:"id"` |
|
||||||
PID uint `json:"pid" xml:"pid" form:"pid"` |
|
||||||
Title string `json:"title" xml:"title" form:"title"` |
|
||||||
Sort int32 `json:"sort" xml:"sort" form:"sort"` |
|
||||||
Status bool `json:"status" xml:"status" form:"status"` |
|
||||||
} |
|
||||||
|
|
||||||
func (r *ResourceCategoryUpsertRequest) GetID() any { |
|
||||||
return r.ID |
|
||||||
} |
|
||||||
|
|
||||||
func (r *ResourceCategoryUpsertRequest) ToMap() map[string]any { |
|
||||||
return map[string]any{ |
|
||||||
"id": r.ID, |
|
||||||
"pid": r.PID, |
|
||||||
"title": r.Title, |
|
||||||
"sort": r.Sort, |
|
||||||
"status": r.Status, |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (r *ResourceCategoryUpsertRequest) ToEntity() any { |
|
||||||
return &entities.ResourceCategory{ |
|
||||||
ID: r.ID, |
|
||||||
PID: &r.PID, |
|
||||||
Title: r.Title, |
|
||||||
Sort: r.Sort, |
|
||||||
Status: r.Status, |
|
||||||
} |
|
||||||
} |
|
@ -1,52 +0,0 @@ |
|||||||
package request |
|
||||||
|
|
||||||
import ( |
|
||||||
"sorbet/internal/entities" |
|
||||||
) |
|
||||||
|
|
||||||
type ResourceUpsertRequest struct { |
|
||||||
ID uint `json:"id" xml:"id" form:"id" path:"id"` |
|
||||||
CategoryID uint `json:"category_id" xml:"category_id" form:"category_id"` |
|
||||||
Title string `json:"title" xml:"title" form:"title"` |
|
||||||
Path string `json:"path" xml:"path" form:"path"` |
|
||||||
Width int32 `json:"width" xml:"width" form:"width"` |
|
||||||
Height int32 `json:"height" xml:"height" form:"height"` |
|
||||||
Duration int32 `json:"duration" xml:"duration" form:"duration"` |
|
||||||
MimeType string `json:"mime_type" xml:"mime_type" form:"mime_type"` |
|
||||||
Extension string `json:"extension" xml:"extension" form:"extension"` |
|
||||||
Size int64 `json:"size" xml:"size" form:"size"` |
|
||||||
} |
|
||||||
|
|
||||||
func (r *ResourceUpsertRequest) GetID() any { |
|
||||||
return r.ID |
|
||||||
} |
|
||||||
|
|
||||||
func (r *ResourceUpsertRequest) ToMap() map[string]any { |
|
||||||
return map[string]any{ |
|
||||||
"id": r.ID, |
|
||||||
"category_id": r.CategoryID, |
|
||||||
"title": r.Title, |
|
||||||
"path": r.Path, |
|
||||||
"width": r.Width, |
|
||||||
"height": r.Height, |
|
||||||
"duration": r.Duration, |
|
||||||
"mime_type": r.MimeType, |
|
||||||
"extension": r.Extension, |
|
||||||
"size": r.Size, |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (r *ResourceUpsertRequest) ToEntity() any { |
|
||||||
return &entities.Resource{ |
|
||||||
ID: r.ID, |
|
||||||
CategoryID: r.CategoryID, |
|
||||||
Title: r.Title, |
|
||||||
Path: r.Path, |
|
||||||
Width: r.Width, |
|
||||||
Height: r.Height, |
|
||||||
Duration: r.Duration, |
|
||||||
MimeType: r.MimeType, |
|
||||||
Extension: r.Extension, |
|
||||||
Size: r.Size, |
|
||||||
} |
|
||||||
} |
|
@ -1,22 +0,0 @@ |
|||||||
package resource |
|
||||||
|
|
||||||
import ( |
|
||||||
"sorbet/internal/services/resource/controller" |
|
||||||
"sorbet/pkg/app" |
|
||||||
) |
|
||||||
|
|
||||||
type Service struct{} |
|
||||||
|
|
||||||
func (s *Service) Init(ctx *app.Context) error { |
|
||||||
ctx.Routes(&controller.ResourceController{}) |
|
||||||
ctx.Routes(&controller.ResourceCategoryController{}) |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func (s *Service) Start() error { |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func (s *Service) Stop() error { |
|
||||||
return nil |
|
||||||
} |
|
@ -1,24 +0,0 @@ |
|||||||
package services |
|
||||||
|
|
||||||
import ( |
|
||||||
"sorbet/pkg/app" |
|
||||||
) |
|
||||||
|
|
||||||
type Service struct { |
|
||||||
inners []Service |
|
||||||
} |
|
||||||
|
|
||||||
func (s Service) Init(ctx *app.Context) error { |
|
||||||
//TODO implement me
|
|
||||||
panic("implement me") |
|
||||||
} |
|
||||||
|
|
||||||
func (s Service) Start() error { |
|
||||||
//TODO implement me
|
|
||||||
panic("implement me") |
|
||||||
} |
|
||||||
|
|
||||||
func (s Service) Stop() error { |
|
||||||
//TODO implement me
|
|
||||||
panic("implement me") |
|
||||||
} |
|
@ -1,16 +0,0 @@ |
|||||||
package controller |
|
||||||
|
|
||||||
import ( |
|
||||||
"github.com/labstack/echo/v4" |
|
||||||
"sorbet/internal/entities" |
|
||||||
"sorbet/internal/services/system/request" |
|
||||||
"sorbet/internal/util" |
|
||||||
) |
|
||||||
|
|
||||||
type SystemLogController struct { |
|
||||||
util.Controller[entities.SystemLog, request.SystemLogUpsertRequest] |
|
||||||
} |
|
||||||
|
|
||||||
func (s *SystemLogController) InitRoutes(r *echo.Group) { |
|
||||||
s.RegisterRoutes("/system/logs", r) |
|
||||||
} |
|
@ -1,16 +0,0 @@ |
|||||||
package controller |
|
||||||
|
|
||||||
import ( |
|
||||||
"github.com/labstack/echo/v4" |
|
||||||
"sorbet/internal/entities" |
|
||||||
"sorbet/internal/services/system/request" |
|
||||||
"sorbet/internal/util" |
|
||||||
) |
|
||||||
|
|
||||||
type SystemMenuController struct { |
|
||||||
util.Controller[entities.SystemMenu, request.SystemMenuUpsertRequest] |
|
||||||
} |
|
||||||
|
|
||||||
func (s *SystemMenuController) InitRoutes(r *echo.Group) { |
|
||||||
s.RegisterRoutes("/system/menus", r) |
|
||||||
} |
|
@ -1,16 +0,0 @@ |
|||||||
package controller |
|
||||||
|
|
||||||
import ( |
|
||||||
"github.com/labstack/echo/v4" |
|
||||||
"sorbet/internal/entities" |
|
||||||
"sorbet/internal/services/system/request" |
|
||||||
"sorbet/internal/util" |
|
||||||
) |
|
||||||
|
|
||||||
type SystemPermissionController struct { |
|
||||||
util.Controller[entities.SystemPermission, request.SystemPermissionUpsertRequest] |
|
||||||
} |
|
||||||
|
|
||||||
func (s *SystemPermissionController) InitRoutes(r *echo.Group) { |
|
||||||
s.RegisterRoutes("/system/permissions", r) |
|
||||||
} |
|
@ -1,16 +0,0 @@ |
|||||||
package controller |
|
||||||
|
|
||||||
import ( |
|
||||||
"github.com/labstack/echo/v4" |
|
||||||
"sorbet/internal/entities" |
|
||||||
"sorbet/internal/services/system/request" |
|
||||||
"sorbet/internal/util" |
|
||||||
) |
|
||||||
|
|
||||||
type SystemRoleController struct { |
|
||||||
util.Controller[entities.SystemRole, request.SystemRoleUpsertRequest] |
|
||||||
} |
|
||||||
|
|
||||||
func (s *SystemRoleController) InitRoutes(r *echo.Group) { |
|
||||||
s.RegisterRoutes("/system/roles", r) |
|
||||||
} |
|
@ -1,16 +0,0 @@ |
|||||||
package controller |
|
||||||
|
|
||||||
import ( |
|
||||||
"github.com/labstack/echo/v4" |
|
||||||
"sorbet/internal/entities" |
|
||||||
"sorbet/internal/services/system/request" |
|
||||||
"sorbet/internal/util" |
|
||||||
) |
|
||||||
|
|
||||||
type SystemRolePowerController struct { |
|
||||||
util.Controller[entities.SystemRolePower, request.SystemRolePowerUpsertRequest] |
|
||||||
} |
|
||||||
|
|
||||||
func (s *SystemRolePowerController) InitRoutes(r *echo.Group) { |
|
||||||
s.RegisterRoutes("/system/role/powers", r) |
|
||||||
} |
|
@ -1,16 +0,0 @@ |
|||||||
package controller |
|
||||||
|
|
||||||
import ( |
|
||||||
"github.com/labstack/echo/v4" |
|
||||||
"sorbet/internal/entities" |
|
||||||
"sorbet/internal/services/system/request" |
|
||||||
"sorbet/internal/util" |
|
||||||
) |
|
||||||
|
|
||||||
type SystemUserController struct { |
|
||||||
util.Controller[entities.SystemUser, request.SystemUserUpsertRequest] |
|
||||||
} |
|
||||||
|
|
||||||
func (s *SystemUserController) InitRoutes(r *echo.Group) { |
|
||||||
s.RegisterRoutes("/system/users", r) |
|
||||||
} |
|
@ -1,55 +0,0 @@ |
|||||||
package request |
|
||||||
|
|
||||||
import ( |
|
||||||
"sorbet/internal/entities" |
|
||||||
) |
|
||||||
|
|
||||||
type SystemLogUpsertRequest struct { |
|
||||||
ID uint `json:"id" xml:"id" form:"id" path:"id"` |
|
||||||
Table string `json:"table" xml:"table" form:"table"` |
|
||||||
RowID uint `json:"row_id" xml:"row_id" form:"row_id"` |
|
||||||
Operation string `json:"operation" xml:"operation" form:"operation"` |
|
||||||
IP string `json:"ip" xml:"ip" form:"ip"` |
|
||||||
Comment string `json:"comment" xml:"comment" form:"comment"` |
|
||||||
RequestID string `json:"request_id" xml:"request_id" form:"request_id"` |
|
||||||
RequestInfo string `json:"request_info" xml:"request_info" form:"request_info"` |
|
||||||
ColumnInfo string `json:"column_info" xml:"column_info" form:"column_info"` |
|
||||||
UserID int64 `json:"user_id" xml:"user_id" form:"user_id"` |
|
||||||
UserType int64 `json:"user_type" xml:"user_type" form:"user_type"` |
|
||||||
} |
|
||||||
|
|
||||||
func (s *SystemLogUpsertRequest) GetID() any { |
|
||||||
return s.ID |
|
||||||
} |
|
||||||
|
|
||||||
func (s *SystemLogUpsertRequest) ToMap() map[string]any { |
|
||||||
return map[string]any{ |
|
||||||
"id": s.ID, |
|
||||||
"table": s.Table, |
|
||||||
"row_id": s.RowID, |
|
||||||
"operation": s.Operation, |
|
||||||
"ip": s.IP, |
|
||||||
"comment": s.Comment, |
|
||||||
"request_id": s.RequestID, |
|
||||||
"request_info": s.RequestInfo, |
|
||||||
"column_info": s.ColumnInfo, |
|
||||||
"user_id": s.UserID, |
|
||||||
"user_type": s.UserType, |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (s *SystemLogUpsertRequest) ToEntity() any { |
|
||||||
return &entities.SystemLog{ |
|
||||||
ID: s.ID, |
|
||||||
Table: s.Table, |
|
||||||
RowID: s.RowID, |
|
||||||
Operation: s.Operation, |
|
||||||
IP: s.IP, |
|
||||||
Comment: s.Comment, |
|
||||||
RequestID: s.RequestID, |
|
||||||
RequestInfo: s.RequestInfo, |
|
||||||
ColumnInfo: s.ColumnInfo, |
|
||||||
UserID: s.UserID, |
|
||||||
UserType: s.UserType, |
|
||||||
} |
|
||||||
} |
|
@ -1,40 +0,0 @@ |
|||||||
package request |
|
||||||
|
|
||||||
import ( |
|
||||||
"sorbet/internal/entities" |
|
||||||
) |
|
||||||
|
|
||||||
type SystemMenuUpsertRequest struct { |
|
||||||
ID uint `json:"id" xml:"id" form:"id" path:"id"` |
|
||||||
PID uint `json:"pid" xml:"pid" form:"pid"` |
|
||||||
Title string `json:"title" xml:"title" form:"title"` |
|
||||||
Icon string `json:"icon" xml:"icon" form:"icon"` |
|
||||||
Sort int32 `json:"sort" xml:"sort" form:"sort"` |
|
||||||
Path string `json:"path" xml:"path" form:"path"` |
|
||||||
} |
|
||||||
|
|
||||||
func (s *SystemMenuUpsertRequest) GetID() any { |
|
||||||
return s.ID |
|
||||||
} |
|
||||||
|
|
||||||
func (s *SystemMenuUpsertRequest) ToMap() map[string]any { |
|
||||||
return map[string]any{ |
|
||||||
"id": s.ID, |
|
||||||
"pid": s.PID, |
|
||||||
"title": s.Title, |
|
||||||
"icon": s.Icon, |
|
||||||
"sort": s.Sort, |
|
||||||
"path": s.Path, |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (s *SystemMenuUpsertRequest) ToEntity() any { |
|
||||||
return &entities.SystemMenu{ |
|
||||||
ID: s.ID, |
|
||||||
PID: &s.PID, |
|
||||||
Title: s.Title, |
|
||||||
Icon: s.Icon, |
|
||||||
Sort: s.Sort, |
|
||||||
Path: s.Path, |
|
||||||
} |
|
||||||
} |
|
@ -1,37 +0,0 @@ |
|||||||
package request |
|
||||||
|
|
||||||
import ( |
|
||||||
"sorbet/internal/entities" |
|
||||||
) |
|
||||||
|
|
||||||
type SystemPermissionUpsertRequest struct { |
|
||||||
ID uint `json:"id" xml:"id" form:"id" path:"id"` |
|
||||||
PID uint `json:"pid" xml:"pid" form:"pid"` |
|
||||||
Name string `json:"name" xml:"name" form:"name"` |
|
||||||
Type string `json:"type" xml:"type" form:"type"` |
|
||||||
Identifier string `json:"identifier" xml:"identifier" form:"identifier"` |
|
||||||
} |
|
||||||
|
|
||||||
func (s *SystemPermissionUpsertRequest) GetID() any { |
|
||||||
return s.ID |
|
||||||
} |
|
||||||
|
|
||||||
func (s *SystemPermissionUpsertRequest) ToMap() map[string]any { |
|
||||||
return map[string]any{ |
|
||||||
"id": s.ID, |
|
||||||
"pid": s.PID, |
|
||||||
"name": s.Name, |
|
||||||
"type": s.Type, |
|
||||||
"identifier": s.Identifier, |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (s *SystemPermissionUpsertRequest) ToEntity() any { |
|
||||||
return &entities.SystemPermission{ |
|
||||||
ID: s.ID, |
|
||||||
PID: &s.PID, |
|
||||||
Name: s.Name, |
|
||||||
Type: s.Type, |
|
||||||
Identifier: s.Identifier, |
|
||||||
} |
|
||||||
} |
|
@ -1,34 +0,0 @@ |
|||||||
package request |
|
||||||
|
|
||||||
import ( |
|
||||||
"sorbet/internal/entities" |
|
||||||
) |
|
||||||
|
|
||||||
type SystemRolePowerUpsertRequest struct { |
|
||||||
ID uint `json:"id" xml:"id" form:"id" path:"id"` |
|
||||||
RoleID uint `json:"role_id" xml:"role_id" form:"role_id"` |
|
||||||
WithType string `json:"with_type" xml:"with_type" form:"with_type"` |
|
||||||
WithID uint `json:"with_id" xml:"with_id" form:"with_id"` |
|
||||||
} |
|
||||||
|
|
||||||
func (s *SystemRolePowerUpsertRequest) GetID() any { |
|
||||||
return s.ID |
|
||||||
} |
|
||||||
|
|
||||||
func (s *SystemRolePowerUpsertRequest) ToMap() map[string]any { |
|
||||||
return map[string]any{ |
|
||||||
"id": s.ID, |
|
||||||
"role_id": s.RoleID, |
|
||||||
"with_type": s.WithType, |
|
||||||
"with_id": s.WithID, |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (s *SystemRolePowerUpsertRequest) ToEntity() any { |
|
||||||
return &entities.SystemRolePower{ |
|
||||||
ID: s.ID, |
|
||||||
RoleID: s.RoleID, |
|
||||||
WithType: s.WithType, |
|
||||||
WithID: s.WithID, |
|
||||||
} |
|
||||||
} |
|
@ -1,28 +0,0 @@ |
|||||||
package request |
|
||||||
|
|
||||||
import ( |
|
||||||
"sorbet/internal/entities" |
|
||||||
) |
|
||||||
|
|
||||||
type SystemRoleUpsertRequest struct { |
|
||||||
ID uint `json:"id" xml:"id" form:"id" path:"id"` |
|
||||||
Name string `json:"name" xml:"name" form:"name"` |
|
||||||
} |
|
||||||
|
|
||||||
func (s *SystemRoleUpsertRequest) GetID() any { |
|
||||||
return s.ID |
|
||||||
} |
|
||||||
|
|
||||||
func (s *SystemRoleUpsertRequest) ToMap() map[string]any { |
|
||||||
return map[string]any{ |
|
||||||
"id": s.ID, |
|
||||||
"name": s.Name, |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (s *SystemRoleUpsertRequest) ToEntity() any { |
|
||||||
return &entities.SystemRole{ |
|
||||||
ID: s.ID, |
|
||||||
Name: s.Name, |
|
||||||
} |
|
||||||
} |
|
@ -1,34 +0,0 @@ |
|||||||
package request |
|
||||||
|
|
||||||
import ( |
|
||||||
"sorbet/internal/entities" |
|
||||||
) |
|
||||||
|
|
||||||
type SystemUserUpsertRequest struct { |
|
||||||
ID int64 `json:"id" xml:"id" form:"id" path:"id"` |
|
||||||
Username string `json:"username" xml:"username" form:"username"` |
|
||||||
Password string `json:"password" xml:"password" form:"password"` |
|
||||||
Status bool `json:"status" xml:"status" form:"status"` |
|
||||||
} |
|
||||||
|
|
||||||
func (s *SystemUserUpsertRequest) GetID() any { |
|
||||||
return s.ID |
|
||||||
} |
|
||||||
|
|
||||||
func (s *SystemUserUpsertRequest) ToMap() map[string]any { |
|
||||||
return map[string]any{ |
|
||||||
"id": s.ID, |
|
||||||
"username": s.Username, |
|
||||||
"password": s.Password, |
|
||||||
"status": s.Status, |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func (s *SystemUserUpsertRequest) ToEntity() any { |
|
||||||
return &entities.SystemUser{ |
|
||||||
ID: s.ID, |
|
||||||
Username: s.Username, |
|
||||||
Password: s.Password, |
|
||||||
Status: s.Status, |
|
||||||
} |
|
||||||
} |
|
@ -1,26 +0,0 @@ |
|||||||
package system |
|
||||||
|
|
||||||
import ( |
|
||||||
"sorbet/internal/services/system/controller" |
|
||||||
"sorbet/pkg/app" |
|
||||||
) |
|
||||||
|
|
||||||
type Service struct{} |
|
||||||
|
|
||||||
func (s *Service) Init(ctx *app.Context) error { |
|
||||||
ctx.Routes(&controller.SystemLogController{}) |
|
||||||
ctx.Routes(&controller.SystemMenuController{}) |
|
||||||
ctx.Routes(&controller.SystemPermissionController{}) |
|
||||||
ctx.Routes(&controller.SystemRoleController{}) |
|
||||||
ctx.Routes(&controller.SystemRolePowerController{}) |
|
||||||
ctx.Routes(&controller.SystemUserController{}) |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func (s *Service) Start() error { |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func (s *Service) Stop() error { |
|
||||||
return nil |
|
||||||
} |
|
@ -1,312 +0,0 @@ |
|||||||
package util |
|
||||||
|
|
||||||
import ( |
|
||||||
"github.com/labstack/echo/v4" |
|
||||||
"gorm.io/gorm" |
|
||||||
"net/url" |
|
||||||
"reflect" |
|
||||||
"sorbet/pkg/db" |
|
||||||
"sorbet/pkg/ioc" |
|
||||||
"sorbet/pkg/rsp" |
|
||||||
"strconv" |
|
||||||
"strings" |
|
||||||
) |
|
||||||
|
|
||||||
type GetID interface { |
|
||||||
GetID() any |
|
||||||
} |
|
||||||
|
|
||||||
type ToMap interface { |
|
||||||
ToMap() map[string]any |
|
||||||
} |
|
||||||
|
|
||||||
type ToEntity interface { |
|
||||||
ToEntity() any |
|
||||||
} |
|
||||||
|
|
||||||
type ControllerRequest interface { |
|
||||||
GetID |
|
||||||
ToMap |
|
||||||
ToEntity |
|
||||||
} |
|
||||||
|
|
||||||
func ParseQuery[T any](query url.Values, qb *db.QueryBuilder[T]) (page, limit int, err error) { |
|
||||||
var paginating bool |
|
||||||
for key, values := range query { |
|
||||||
switch key { |
|
||||||
case "sortby": |
|
||||||
for _, s := range values { |
|
||||||
if s[0] == '+' { |
|
||||||
qb.AscentBy(s[1:]) |
|
||||||
} else if s[0] == '-' { |
|
||||||
qb.DescentBy(s[1:]) |
|
||||||
} else { |
|
||||||
qb.AscentBy(s) |
|
||||||
} |
|
||||||
} |
|
||||||
case "limit", "page": |
|
||||||
var v int |
|
||||||
if values[0] != "" { |
|
||||||
v, err = strconv.Atoi(values[0]) |
|
||||||
if err != nil { |
|
||||||
return |
|
||||||
} |
|
||||||
} |
|
||||||
if v <= 0 { |
|
||||||
return 0, 0, rsp.ErrInternal |
|
||||||
} |
|
||||||
if key == "limit" { |
|
||||||
qb.Limit(v) |
|
||||||
limit = v |
|
||||||
} else { |
|
||||||
paginating = true |
|
||||||
page = max(v, 1) |
|
||||||
} |
|
||||||
default: |
|
||||||
v := values[0] |
|
||||||
i := strings.IndexByte(key, '#') |
|
||||||
if i == -1 { |
|
||||||
qb.Eq(key, v) |
|
||||||
continue |
|
||||||
} |
|
||||||
switch k, op := key[:i], key[i+1:]; op { |
|
||||||
case "=": |
|
||||||
qb.Eq(k, v) |
|
||||||
case "!=": |
|
||||||
qb.Neq(k, v) |
|
||||||
case "<": |
|
||||||
qb.Lt(k, v) |
|
||||||
case "<=": |
|
||||||
qb.Lte(k, v) |
|
||||||
case ">": |
|
||||||
qb.Gt(k, v) |
|
||||||
case ">=": |
|
||||||
qb.Gte(k, v) |
|
||||||
case "<>", "><": |
|
||||||
var less, more any |
|
||||||
switch len(values) { |
|
||||||
case 2: |
|
||||||
less, more = values[0], values[1] |
|
||||||
case 1: |
|
||||||
vs := strings.Split(v, ",") |
|
||||||
if len(vs) != 2 || vs[0] == "" || vs[1] == "" { |
|
||||||
return 0, 0, rsp.ErrBadParams |
|
||||||
} |
|
||||||
less, more = vs[0], vs[1] |
|
||||||
} |
|
||||||
if op == "<>" { |
|
||||||
qb.Between(k, less, more) |
|
||||||
} else { |
|
||||||
qb.NotBetween(k, key, more) |
|
||||||
} |
|
||||||
case "nil": |
|
||||||
qb.IsNull(k) |
|
||||||
case "!nil": |
|
||||||
qb.NotNull(k) |
|
||||||
case "~": |
|
||||||
qb.Like(k, v) |
|
||||||
case "!~": |
|
||||||
qb.NotLike(k, v) |
|
||||||
case "in", "!in": |
|
||||||
if len(values) == 1 { |
|
||||||
values = strings.Split(v, ",") |
|
||||||
} |
|
||||||
vs := make([]any, len(values)) |
|
||||||
for i, value := range values { |
|
||||||
vs[i] = value |
|
||||||
} |
|
||||||
if op == "in" { |
|
||||||
qb.In(k, vs...) |
|
||||||
} else { |
|
||||||
qb.NotIn(k, vs...) |
|
||||||
} |
|
||||||
default: |
|
||||||
qb.Eq(key, v) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
if paginating { |
|
||||||
return |
|
||||||
} |
|
||||||
if limit == 0 { |
|
||||||
limit = 30 |
|
||||||
qb.Limit(limit) |
|
||||||
} |
|
||||||
qb.Offset((page - 1) * limit) |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
func getID(req any) (val any) { |
|
||||||
defer func() { |
|
||||||
if recover() != nil { |
|
||||||
val = nil |
|
||||||
} |
|
||||||
}() |
|
||||||
val = req.(GetID).GetID() |
|
||||||
rv := reflect.ValueOf(val) |
|
||||||
if rv.IsZero() || rv.IsNil() { |
|
||||||
val = nil |
|
||||||
} |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
func getValues(req any) map[string]any { |
|
||||||
if v, ok := req.(ToMap); ok { |
|
||||||
return v.ToMap() |
|
||||||
} |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func getEntity[T any](request any) *T { |
|
||||||
v, ok := request.(ToEntity) |
|
||||||
if !ok { |
|
||||||
return nil |
|
||||||
} |
|
||||||
ent, ok := v.ToEntity().(*T) |
|
||||||
if ok { |
|
||||||
return ent |
|
||||||
} |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
// Controller 控制器基类
|
|
||||||
//
|
|
||||||
// 泛型 [Entity] 表示操作的具体数据;
|
|
||||||
// 泛型 [Upsert] 表示创建或更新时需要的数据。
|
|
||||||
type Controller[Entity any, Upsert any] struct{} |
|
||||||
|
|
||||||
func (ctr *Controller[Entity, Upsert]) RegisterRoutes(path string, r *echo.Group) { |
|
||||||
r.PUT(path, ctr.Create) |
|
||||||
r.DELETE(path+"/:id", ctr.Delete) |
|
||||||
r.POST(path+"/:id", ctr.Update) |
|
||||||
r.GET(path+"/:id", ctr.Get) |
|
||||||
r.GET(path, ctr.List) |
|
||||||
} |
|
||||||
|
|
||||||
func (ctr *Controller[Entity, Upsert]) ORM(c echo.Context) (*gorm.DB, error) { |
|
||||||
orm, err := ioc.Get[gorm.DB]() |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
return orm.WithContext(c.Request().Context()), nil |
|
||||||
} |
|
||||||
|
|
||||||
func (ctr *Controller[Entity, Upsert]) Repository() (*db.Repository[Entity], error) { |
|
||||||
orm, err := ioc.Get[gorm.DB]() |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
return db.NewRepository[Entity](orm), nil |
|
||||||
} |
|
||||||
|
|
||||||
// Create 创建数据
|
|
||||||
func (ctr *Controller[Entity, Upsert]) Create(c echo.Context) error { |
|
||||||
return ctr.upsert(c, true) |
|
||||||
} |
|
||||||
|
|
||||||
// Update 更新数据
|
|
||||||
func (ctr *Controller[Entity, Upsert]) Update(c echo.Context) error { |
|
||||||
return ctr.upsert(c, false) |
|
||||||
} |
|
||||||
|
|
||||||
func (ctr *Controller[Entity, Upsert]) upsert(c echo.Context, isCreate bool) error { |
|
||||||
request, err := Bind[Upsert](c) |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
repo, err := ctr.Repository() |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
id := getID(request) |
|
||||||
if isCreate != reflect.ValueOf(id).IsZero() { |
|
||||||
return rsp.BadParams(c, "参数错误") |
|
||||||
} |
|
||||||
// 更新数据
|
|
||||||
if !isCreate { |
|
||||||
values := getValues(request) |
|
||||||
if values == nil { |
|
||||||
return rsp.ErrInternal |
|
||||||
} |
|
||||||
err = repo.UpdateByID(c.Request().Context(), id, values) |
|
||||||
if err == nil { |
|
||||||
// TODO(hupeh): 返回更新后的实体数据
|
|
||||||
return rsp.Ok(c, nil) |
|
||||||
} |
|
||||||
return err |
|
||||||
} |
|
||||||
// 创建数据
|
|
||||||
group := getEntity[Entity](request) |
|
||||||
if group == nil { |
|
||||||
return rsp.ErrInternal |
|
||||||
} |
|
||||||
err = repo.Create(c.Request().Context(), group) |
|
||||||
if err != nil { |
|
||||||
return rsp.Created(c, group) |
|
||||||
} |
|
||||||
return err |
|
||||||
} |
|
||||||
|
|
||||||
// Delete 通过ID删除数据
|
|
||||||
func (ctr *Controller[Entity, Upsert]) Delete(c echo.Context) error { |
|
||||||
id, err := BindId(c, true) |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
repo, err := ctr.Repository() |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
err = repo.DeleteByID(c.Request().Context(), id) |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
return rsp.Ok(c, nil) |
|
||||||
} |
|
||||||
|
|
||||||
// Get 通过ID获取数据
|
|
||||||
func (ctr *Controller[Entity, Upsert]) Get(c echo.Context) error { |
|
||||||
id, err := BindId(c, true) |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
repo, err := ctr.Repository() |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
entity, err := repo.GetByID(c.Request().Context(), id) |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
return rsp.Ok(c, entity) |
|
||||||
} |
|
||||||
|
|
||||||
// List 获取数据列表
|
|
||||||
func (ctr *Controller[Entity, Upsert]) List(c echo.Context) error { |
|
||||||
repo, err := ctr.Repository() |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
qb := repo.NewQueryBuilder(c.Request().Context()) |
|
||||||
_, _, err = ParseQuery[Entity](c.QueryParams(), qb) |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
|
|
||||||
// 不是分页查询
|
|
||||||
if !c.QueryParams().Has("page") { |
|
||||||
var result []*Entity |
|
||||||
err = qb.Find(&result) |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
return rsp.Ok(c, result) |
|
||||||
} |
|
||||||
|
|
||||||
// 分页查询
|
|
||||||
pager, err := qb.Paginate() |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
return rsp.Ok(c, pager) |
|
||||||
} |
|
@ -1,43 +0,0 @@ |
|||||||
package util |
|
||||||
|
|
||||||
import ( |
|
||||||
"github.com/labstack/echo/v4" |
|
||||||
"sorbet/pkg/rsp" |
|
||||||
) |
|
||||||
|
|
||||||
// RequestGuarder 参数守卫函数签名
|
|
||||||
type RequestGuarder[T any] func(c echo.Context, req *T) error |
|
||||||
|
|
||||||
// Bind 将提交的参数绑定到泛型 T 的实例上
|
|
||||||
func Bind[T any](c echo.Context, guards ...RequestGuarder[T]) (*T, error) { |
|
||||||
var req T |
|
||||||
if err := c.Bind(&req); err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
if err := c.Validate(&req); err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
for _, guard := range guards { |
|
||||||
if err := guard(c, &req); err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
} |
|
||||||
return &req, nil |
|
||||||
} |
|
||||||
|
|
||||||
func BindId(c echo.Context, must bool) (uint, error) { |
|
||||||
request, err := Bind[struct { |
|
||||||
ID uint `json:"id" xml:"id" path:"id"` |
|
||||||
}](c) |
|
||||||
if err != nil { |
|
||||||
return 0, err |
|
||||||
} |
|
||||||
if must { |
|
||||||
if request.ID <= 0 { |
|
||||||
return 0, rsp.ErrBadParams |
|
||||||
} |
|
||||||
} else if request.ID < 0 { |
|
||||||
return 0, rsp.ErrBadParams |
|
||||||
} |
|
||||||
return request.ID, err |
|
||||||
} |
|
@ -0,0 +1,7 @@ |
|||||||
|
package util |
||||||
|
|
||||||
|
import "github.com/labstack/echo/v4" |
||||||
|
|
||||||
|
type EchoContext struct { |
||||||
|
echo.Context |
||||||
|
} |
@ -1,38 +0,0 @@ |
|||||||
package util |
|
||||||
|
|
||||||
import ( |
|
||||||
"github.com/golang-jwt/jwt/v5" |
|
||||||
"github.com/rs/xid" |
|
||||||
"sorbet/pkg/env" |
|
||||||
"sorbet/pkg/ticket" |
|
||||||
"time" |
|
||||||
) |
|
||||||
|
|
||||||
func CreateTicket(claims *ticket.Claims) (string, error) { |
|
||||||
if claims.ID == "" { |
|
||||||
claims.ID = xid.New().String() |
|
||||||
} |
|
||||||
if claims.Issuer == "" { |
|
||||||
claims.Issuer = env.String("TICKET_ISSUER") |
|
||||||
} |
|
||||||
if claims.Subject == "" { |
|
||||||
claims.Issuer = env.String("TICKET_SUBJECT") |
|
||||||
} |
|
||||||
if claims.Audience == nil { |
|
||||||
claims.Audience = env.List("TICKET_AUDIENCE") |
|
||||||
} |
|
||||||
if claims.ExpiresAt == nil { |
|
||||||
ttl := env.Duration("TICKET_TTL", time.Hour) |
|
||||||
claims.ExpiresAt = jwt.NewNumericDate(time.Now().Add(ttl)) |
|
||||||
} |
|
||||||
source := []byte(env.String("TICKET_PRIVATE_KEY")) |
|
||||||
privateKey, err := jwt.ParseRSAPrivateKeyFromPEM(source) |
|
||||||
if err != nil { |
|
||||||
return "", err |
|
||||||
} |
|
||||||
signedString, err := ticket.Create(claims, privateKey) |
|
||||||
if err != nil { |
|
||||||
return "", err |
|
||||||
} |
|
||||||
return signedString, nil |
|
||||||
} |
|
@ -1,19 +1,88 @@ |
|||||||
package main |
package main |
||||||
|
|
||||||
import ( |
import ( |
||||||
"log" |
"github.com/labstack/echo/v4" |
||||||
|
"github.com/swaggo/echo-swagger" |
||||||
|
"gorm.io/gorm" |
||||||
|
"net/http" |
||||||
|
_ "sorbet/docs" // 开发文档
|
||||||
"sorbet/internal" |
"sorbet/internal" |
||||||
|
"sorbet/internal/entities" |
||||||
|
"sorbet/internal/middleware" |
||||||
|
"sorbet/internal/repositories" |
||||||
|
"sorbet/internal/util" |
||||||
"sorbet/pkg/env" |
"sorbet/pkg/env" |
||||||
|
"sorbet/pkg/ioc" |
||||||
|
"sorbet/pkg/rsp" |
||||||
) |
) |
||||||
|
|
||||||
|
// @title 博客系统
|
||||||
|
// @version 1.0
|
||||||
|
// @description 基于 Echo 框架的基本库
|
||||||
|
//
|
||||||
|
// @contact.name API Support
|
||||||
|
// @contact.url http://www.swagger.io/support
|
||||||
|
// @contact.email support@swagger.io
|
||||||
|
//
|
||||||
|
// @license.name Apache 2.0
|
||||||
|
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
// @accept json
|
||||||
func main() { |
func main() { |
||||||
if err := env.Init(); err != nil { |
if err := env.Init(); err != nil { |
||||||
panic(err) |
panic(err) |
||||||
} |
} |
||||||
|
|
||||||
if err := internal.Init(); err != nil { |
if err := internal.Init(); err != nil { |
||||||
panic(err) |
panic(err) |
||||||
} |
} |
||||||
if err := internal.Start(); err != nil { |
|
||||||
log.Panicln(err) |
repositories.Init() |
||||||
|
|
||||||
|
e := echo.New() |
||||||
|
e.HideBanner = true |
||||||
|
e.HidePort = true |
||||||
|
e.HTTPErrorHandler = func(err error, c echo.Context) { |
||||||
|
if !c.Response().Committed { |
||||||
|
http.Error(c.Response(), err.Error(), 500) |
||||||
|
} |
||||||
|
} |
||||||
|
e.Logger = util.NewLogger() |
||||||
|
e.Use(middleware.Recover()) |
||||||
|
e.Use(middleware.CORS()) |
||||||
|
e.Use(middleware.Logger) |
||||||
|
e.Use(func(next echo.HandlerFunc) echo.HandlerFunc { |
||||||
|
return func(c echo.Context) error { |
||||||
|
db := ioc.MustGet[gorm.DB]().WithContext(c.Request().Context()) |
||||||
|
ci := ioc.Fork() |
||||||
|
ci.Bind(db) |
||||||
|
c.Set("db", db) |
||||||
|
c.Set("ioc", ci) |
||||||
|
return next(c) |
||||||
|
} |
||||||
|
}) |
||||||
|
e.GET("/swagger/*", echoSwagger.WrapHandler) |
||||||
|
e.GET("/", func(c echo.Context) error { |
||||||
|
repo := repositories.NewCompanyRepository(c.Get("db").(*gorm.DB)) |
||||||
|
//err := c.Get("ioc").(*ioc.Container).Resolve(&repo)
|
||||||
|
//if err != nil {
|
||||||
|
// return err
|
||||||
|
//}
|
||||||
|
//db := ioc.MustGet[gorm.DB]().WithContext(c.Request().Context())
|
||||||
|
//ioc.Fork().Bind(db)
|
||||||
|
//repo := ioc.MustGet[repositories.CompanyRepository]()
|
||||||
|
repo.Create(c.Request().Context(), &entities.Company{Name: "海苔一诺"}) |
||||||
|
pager, err := repo.Paginate(c.Request().Context()) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
return rsp.Ok(c, pager) |
||||||
|
}) |
||||||
|
e.Logger.Fatal(e.Start(":1323")) |
||||||
|
} |
||||||
|
|
||||||
|
func panicIf(e error) { |
||||||
|
if e != nil { |
||||||
|
panic(e) |
||||||
} |
} |
||||||
} |
} |
||||||
|
@ -1,55 +0,0 @@ |
|||||||
package app |
|
||||||
|
|
||||||
import ( |
|
||||||
"context" |
|
||||||
"github.com/labstack/echo/v4" |
|
||||||
"sync" |
|
||||||
) |
|
||||||
|
|
||||||
type Context struct { |
|
||||||
context.Context |
|
||||||
prefix string |
|
||||||
store map[any]any |
|
||||||
router *echo.Group |
|
||||||
mu sync.RWMutex |
|
||||||
} |
|
||||||
|
|
||||||
// Prefix 设置路由前缀
|
|
||||||
func (c *Context) Prefix(prefix string) { |
|
||||||
c.mu.Lock() |
|
||||||
defer c.mu.Unlock() |
|
||||||
if c.prefix != "" { |
|
||||||
panic("already prefixed") |
|
||||||
} |
|
||||||
c.prefix = prefix |
|
||||||
} |
|
||||||
|
|
||||||
// Routes 注册路由
|
|
||||||
func (c *Context) Routes(routes Routable) { |
|
||||||
c.mu.Lock() |
|
||||||
defer c.mu.Unlock() |
|
||||||
routes.InitRoutes(c.router) |
|
||||||
} |
|
||||||
|
|
||||||
// Set 设置值
|
|
||||||
func (c *Context) Set(key, val any) { |
|
||||||
c.mu.Lock() |
|
||||||
defer c.mu.Unlock() |
|
||||||
c.store[key] = val |
|
||||||
} |
|
||||||
|
|
||||||
// Get 获取值,只会获取通过 Set 方法设置的值
|
|
||||||
func (c *Context) Get(key any) (any, bool) { |
|
||||||
c.mu.RLock() |
|
||||||
defer c.mu.RUnlock() |
|
||||||
val, ok := c.store[key] |
|
||||||
return val, ok |
|
||||||
} |
|
||||||
|
|
||||||
// Value 获取值
|
|
||||||
func (c *Context) Value(key any) any { |
|
||||||
if val, ok := c.Get(key); ok { |
|
||||||
return val |
|
||||||
} |
|
||||||
return c.Value(key) |
|
||||||
} |
|
@ -1,15 +0,0 @@ |
|||||||
package app |
|
||||||
|
|
||||||
import "github.com/labstack/echo/v4" |
|
||||||
|
|
||||||
type echoContext struct { |
|
||||||
echo.Context |
|
||||||
ctx *Context |
|
||||||
} |
|
||||||
|
|
||||||
func (e *echoContext) Get(key string) any { |
|
||||||
if val, ok := e.ctx.Get(key); ok && val != nil { |
|
||||||
return val |
|
||||||
} |
|
||||||
return e.Context.Get(key) |
|
||||||
} |
|
@ -1,16 +0,0 @@ |
|||||||
package app |
|
||||||
|
|
||||||
import "github.com/labstack/echo/v4" |
|
||||||
|
|
||||||
type Service interface { |
|
||||||
// Init 初始化服务
|
|
||||||
Init(ctx *Context) error |
|
||||||
// Start 启动服务
|
|
||||||
Start() error |
|
||||||
// Stop 停止服务
|
|
||||||
Stop() error |
|
||||||
} |
|
||||||
|
|
||||||
type Routable interface { |
|
||||||
InitRoutes(r *echo.Group) |
|
||||||
} |
|
@ -0,0 +1,67 @@ |
|||||||
|
package logs |
||||||
|
|
||||||
|
import ( |
||||||
|
"log/slog" |
||||||
|
"time" |
||||||
|
) |
||||||
|
|
||||||
|
type Attr = slog.Attr |
||||||
|
|
||||||
|
// String returns an Attr for a string value.
|
||||||
|
func String(key, value string) Attr { |
||||||
|
return slog.String(key, value) |
||||||
|
} |
||||||
|
|
||||||
|
// Int64 returns an Attr for an int64.
|
||||||
|
func Int64(key string, value int64) Attr { |
||||||
|
return slog.Int64(key, value) |
||||||
|
} |
||||||
|
|
||||||
|
// Int converts an int to an int64 and returns
|
||||||
|
// an Attr with that value.
|
||||||
|
func Int(key string, value int) Attr { |
||||||
|
return slog.Int(key, value) |
||||||
|
} |
||||||
|
|
||||||
|
// Uint64 returns an Attr for a uint64.
|
||||||
|
func Uint64(key string, v uint64) Attr { |
||||||
|
return slog.Uint64(key, v) |
||||||
|
} |
||||||
|
|
||||||
|
// Float64 returns an Attr for a floating-point number.
|
||||||
|
func Float64(key string, v float64) Attr { |
||||||
|
return slog.Float64(key, v) |
||||||
|
} |
||||||
|
|
||||||
|
// Bool returns an Attr for a bool.
|
||||||
|
func Bool(key string, v bool) Attr { |
||||||
|
return slog.Bool(key, v) |
||||||
|
} |
||||||
|
|
||||||
|
// Time returns an Attr for a time.Time.
|
||||||
|
// It discards the monotonic portion.
|
||||||
|
func Time(key string, v time.Time) Attr { |
||||||
|
return slog.Time(key, v) |
||||||
|
} |
||||||
|
|
||||||
|
// Duration returns an Attr for a time.Duration.
|
||||||
|
func Duration(key string, v time.Duration) Attr { |
||||||
|
return slog.Duration(key, v) |
||||||
|
} |
||||||
|
|
||||||
|
// Group returns an Attr for a Group Instance.
|
||||||
|
// The first argument is the key; the remaining arguments
|
||||||
|
// are converted to Attrs as in [Logger.Log].
|
||||||
|
//
|
||||||
|
// Use Group to collect several key-value pairs under a single
|
||||||
|
// key on a log line, or as the result of LogValue
|
||||||
|
// in order to log a single value as multiple Attrs.
|
||||||
|
func Group(key string, args ...any) Attr { |
||||||
|
return slog.Group(key, args...) |
||||||
|
} |
||||||
|
|
||||||
|
// Any returns an Attr for the supplied value.
|
||||||
|
// See [AnyValue] for how values are treated.
|
||||||
|
func Any(key string, value any) Attr { |
||||||
|
return slog.Any(key, value) |
||||||
|
} |
@ -0,0 +1,44 @@ |
|||||||
|
package logs |
||||||
|
|
||||||
|
import ( |
||||||
|
"github.com/mattn/go-isatty" |
||||||
|
"os" |
||||||
|
) |
||||||
|
|
||||||
|
type Color string |
||||||
|
|
||||||
|
var ( |
||||||
|
//fgBlack Color = "\x1b[30m"
|
||||||
|
//fgWhiteItalic Color = "\x1b[37;3m"
|
||||||
|
|
||||||
|
FgRed Color = "\x1b[31m" |
||||||
|
FgGreen Color = "\x1b[32m" |
||||||
|
FgYellow Color = "\x1b[33m" |
||||||
|
FgBlue Color = "\x1b[34m" |
||||||
|
FgMagenta Color = "\x1b[35m" |
||||||
|
FgCyan Color = "\x1b[36m" |
||||||
|
FgWhite Color = "\x1b[37m" |
||||||
|
FgHiBlack Color = "\x1b[90m" |
||||||
|
fgGreenItalic Color = "\x1b[32;3m" |
||||||
|
|
||||||
|
// NoColor defines if the output is colorized or not. It's dynamically set to
|
||||||
|
// false or true based on the stdout's file descriptor referring to a terminal
|
||||||
|
// or not. It's also set to true if the NO_COLOR environment variable is
|
||||||
|
// set (regardless of its value). This is a global option and affects all
|
||||||
|
// colors. For more control over each Color block use the methods
|
||||||
|
// DisableColor() individually.
|
||||||
|
noColor = noColorIsSet() || os.Getenv("TERM") == "dumb" || |
||||||
|
(!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd())) |
||||||
|
) |
||||||
|
|
||||||
|
// noColorIsSet returns true if the environment variable NO_COLOR is set to a non-empty string.
|
||||||
|
func noColorIsSet() bool { |
||||||
|
return os.Getenv("NO_COLOR") != "" |
||||||
|
} |
||||||
|
|
||||||
|
func (c Color) Wrap(msg string) string { |
||||||
|
if noColorIsSet() || noColor { |
||||||
|
return msg |
||||||
|
} |
||||||
|
return string(c) + msg + "\x1b[0m" |
||||||
|
} |
@ -0,0 +1 @@ |
|||||||
|
package logs |
@ -0,0 +1,59 @@ |
|||||||
|
package logs |
||||||
|
|
||||||
|
import ( |
||||||
|
"log/slog" |
||||||
|
) |
||||||
|
|
||||||
|
type Level int |
||||||
|
|
||||||
|
const ( |
||||||
|
LevelTrace Level = iota |
||||||
|
LevelDebug // 用于程序调试
|
||||||
|
LevelInfo // 用于程序运行
|
||||||
|
LevelWarn // 潜在错误或非预期结果
|
||||||
|
LevelError // 发生错误,但不影响系统的继续运行
|
||||||
|
LevelFatal |
||||||
|
LevelSilent |
||||||
|
) |
||||||
|
|
||||||
|
// 越界取近值
|
||||||
|
func (l Level) real() Level { |
||||||
|
return min(LevelSilent, max(l, LevelTrace)) |
||||||
|
} |
||||||
|
|
||||||
|
// Level 实现 slog.Leveler 接口
|
||||||
|
func (l Level) Level() slog.Level { |
||||||
|
return slog.Level(16 - int(LevelSilent-l.real())*4) |
||||||
|
} |
||||||
|
|
||||||
|
func (l Level) slog() slog.Leveler { |
||||||
|
return l.Level() |
||||||
|
} |
||||||
|
|
||||||
|
func (l Level) String() string { |
||||||
|
switch l { |
||||||
|
case LevelTrace: |
||||||
|
return "TRACE" |
||||||
|
case LevelDebug: |
||||||
|
return "DEBUG" |
||||||
|
case LevelInfo: |
||||||
|
return "INFO" |
||||||
|
case LevelWarn: |
||||||
|
return "WARN" |
||||||
|
case LevelError: |
||||||
|
return "ERROR" |
||||||
|
case LevelFatal: |
||||||
|
return "FATAL" |
||||||
|
case LevelSilent: |
||||||
|
return "OFF" |
||||||
|
} |
||||||
|
if l < LevelTrace { |
||||||
|
return "TRACE" |
||||||
|
} else { |
||||||
|
return "OFF" |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func parseSlogLevel(level slog.Level) Level { |
||||||
|
return Level(level/4 + 2).real() |
||||||
|
} |
@ -0,0 +1,38 @@ |
|||||||
|
package logs |
||||||
|
|
||||||
|
import ( |
||||||
|
"io" |
||||||
|
"time" |
||||||
|
) |
||||||
|
|
||||||
|
var std = New(&Options{Level: LevelInfo}) |
||||||
|
|
||||||
|
func Default() Logger { return std } |
||||||
|
|
||||||
|
func SetFlags(flags int) { Default().SetFlags(flags) } |
||||||
|
func Flags() int { return Default().Flags() } |
||||||
|
func SetTimezone(loc *time.Location) { Default().SetTimezone(loc) } |
||||||
|
func Timezone() *time.Location { return Default().Timezone() } |
||||||
|
func SetLevel(level Level) { Default().SetLevel(level) } |
||||||
|
func GetLevel() Level { return Default().Level() } |
||||||
|
func SetPersistWriter(w io.Writer) { Default().SetPersistWriter(w) } |
||||||
|
func SetWriter(w io.Writer) { Default().SetWriter(w) } |
||||||
|
func With(args ...Attr) Logger { return Default().With(args...) } |
||||||
|
func WithGroup(name string) Logger { return Default().WithGroup(name) } |
||||||
|
func Enabled(level Level) bool { return Default().Enabled(level) } |
||||||
|
func Log(level Level, msg string, args ...any) { Default().Log(level, msg, args...) } |
||||||
|
func ForkLevel(level Level, msg string, args ...any) ChildLogger { |
||||||
|
return Default().ForkLevel(level, msg, args...) |
||||||
|
} |
||||||
|
func Trace(msg string, args ...any) { Default().Trace(msg, args...) } |
||||||
|
func ForkTrace(msg string, args ...any) ChildLogger { return Default().ForkTrace(msg, args...) } |
||||||
|
func Debug(msg string, args ...any) { Default().Debug(msg, args...) } |
||||||
|
func ForkDebug(msg string, args ...any) ChildLogger { return Default().ForkDebug(msg, args...) } |
||||||
|
func Info(msg string, args ...any) { Default().Info(msg) } |
||||||
|
func ForkInfo(msg string, args ...any) ChildLogger { return Default().ForkInfo(msg, args...) } |
||||||
|
func Warn(msg string, args ...any) { Default().Warn(msg, args...) } |
||||||
|
func ForkWarn(msg string, args ...any) ChildLogger { return Default().ForkWarn(msg, args...) } |
||||||
|
func Error(msg string, args ...any) { Default().Error(msg, args...) } |
||||||
|
func ForkError(msg string, args ...any) ChildLogger { return Default().ForkError(msg, args...) } |
||||||
|
func Fatal(msg string, args ...any) { Default().Fatal(msg, args...) } |
||||||
|
func ForkFatal(msg string, args ...any) ChildLogger { return Default().ForkFatal(msg, args...) } |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue