From a95d9dda769744445ce6086e48af85377d7bc9ed Mon Sep 17 00:00:00 2001 From: "zhangwenbing.zwb" Date: Wed, 25 Oct 2023 20:43:11 +0800 Subject: [PATCH 01/11] feat: make admin path configurable --- config/config.go | 2 ++ config/model.go | 1 + event/listener/start.go | 7 ++++++- service/impl/option.go | 4 ++++ service/option.go | 1 + 5 files changed, 14 insertions(+), 1 deletion(-) diff --git a/config/config.go b/config/config.go index ec6ace98..7148dc93 100644 --- a/config/config.go +++ b/config/config.go @@ -30,6 +30,8 @@ func NewConfig() *Config { viper.SetConfigName("config") } + viper.SetDefault("sonic.admin_url_path", "admin") + conf := &Config{} if err := viper.ReadInConfig(); err != nil { panic(err) diff --git a/config/model.go b/config/model.go index 755fcfa8..a25657c7 100644 --- a/config/model.go +++ b/config/model.go @@ -49,4 +49,5 @@ type Sonic struct { TemplateDir string `mapstructure:"template_dir"` ThemeDir string AdminResourcesDir string + AdminUrlPath string `mapstructure:"admin_url_path"` } diff --git a/event/listener/start.go b/event/listener/start.go index 3cf1acf6..209de4e0 100644 --- a/event/listener/start.go +++ b/event/listener/start.go @@ -86,7 +86,12 @@ func (s *StartListener) printStartInfo(ctx context.Context) error { site := logger.BlueBold + "Sonic started at " + blogURL + logger.Reset log.Info(site) fmt.Println(site) - adminSite := logger.BlueBold + "Sonic admin started at " + blogURL + "/admin" + logger.Reset + + adminUrlPath, err := s.optionService.GetAdminUrlPath(ctx) + if err != nil { + return err + } + adminSite := logger.BlueBold + "Sonic admin started at " + blogURL + "/" + adminUrlPath + logger.Reset log.Info(adminSite) fmt.Println(adminSite) return nil diff --git a/service/impl/option.go b/service/impl/option.go index a3131461..b2b96c16 100644 --- a/service/impl/option.go +++ b/service/impl/option.go @@ -469,3 +469,7 @@ func (o *optionServiceImpl) GetAttachmentType(ctx context.Context) consts.Attach return consts.AttachmentTypeLocal } } + +func (o *optionServiceImpl) GetAdminUrlPath(ctx context.Context) (string, error) { + return o.Config.Sonic.AdminUrlPath, nil +} diff --git a/service/option.go b/service/option.go index 028f49c0..4a46fb58 100644 --- a/service/option.go +++ b/service/option.go @@ -32,6 +32,7 @@ type OptionService interface { GetLinkPrefix(ctx context.Context) (string, error) GetSheetPrefix(ctx context.Context) (string, error) GetAttachmentType(ctx context.Context) consts.AttachmentType + GetAdminUrlPath(ctx context.Context) (string, error) } type ClientOptionService interface { From 56157ba32a9ff8dbd5154132cf96887655b0077a Mon Sep 17 00:00:00 2001 From: textworld Date: Wed, 15 Nov 2023 09:31:07 +0800 Subject: [PATCH 02/11] feat: add manage apis for ApplicationPassword --- cmd/generate/generate.go | 2 +- dal/application_password.gen.go | 359 +++++++++++++++++++++++ dal/gen.go | 20 +- go.mod | 12 +- go.sum | 16 + handler/admin/application_password.go | 59 ++++ handler/admin/init.go | 1 + handler/router.go | 6 + handler/server.go | 313 ++++++++++---------- model/dto/application_password.go | 9 + model/entity/application_password.gen.go | 28 ++ model/param/application_password.go | 5 + scripts/table.sql | 12 + service/application_password.go | 13 + service/impl/application_password.go | 134 +++++++++ service/impl/init.go | 1 + util/encrypt.go | 13 + 17 files changed, 832 insertions(+), 171 deletions(-) create mode 100644 dal/application_password.gen.go create mode 100644 handler/admin/application_password.go create mode 100644 model/dto/application_password.go create mode 100644 model/entity/application_password.gen.go create mode 100644 model/param/application_password.go create mode 100644 service/application_password.go create mode 100644 service/impl/application_password.go create mode 100644 util/encrypt.go diff --git a/cmd/generate/generate.go b/cmd/generate/generate.go index d12fdf69..a98332b8 100644 --- a/cmd/generate/generate.go +++ b/cmd/generate/generate.go @@ -43,7 +43,6 @@ func main() { g.GenerateModel("category", gen.FieldType("type", "consts.CategoryType")), g.GenerateModel("comment", gen.FieldType("type", "consts.CommentType"), gen.FieldType("status", "consts.CommentStatus")), g.GenerateModel("comment_black"), - g.GenerateModel("flyway_schema_history"), g.GenerateModel("journal", gen.FieldType("type", "consts.JournalType")), g.GenerateModel("link"), g.GenerateModel("log", gen.FieldType("type", "consts.LogType")), @@ -57,6 +56,7 @@ func main() { g.GenerateModel("tag"), g.GenerateModel("theme_setting"), g.GenerateModel("user", gen.FieldType("mfa_type", "consts.MFAType")), + g.GenerateModel("application_password"), ) // apply diy interfaces on structs or table models diff --git a/dal/application_password.gen.go b/dal/application_password.gen.go new file mode 100644 index 00000000..e21aa7be --- /dev/null +++ b/dal/application_password.gen.go @@ -0,0 +1,359 @@ +// Code generated by gorm.io/gen. DO NOT EDIT. +// Code generated by gorm.io/gen. DO NOT EDIT. +// Code generated by gorm.io/gen. DO NOT EDIT. + +package dal + +import ( + "context" + + "gorm.io/gorm" + "gorm.io/gorm/clause" + "gorm.io/gorm/schema" + + "gorm.io/gen" + "gorm.io/gen/field" + + "gorm.io/plugin/dbresolver" + + "github.com/go-sonic/sonic/model/entity" +) + +func newApplicationPassword(db *gorm.DB, opts ...gen.DOOption) applicationPassword { + _applicationPassword := applicationPassword{} + + _applicationPassword.applicationPasswordDo.UseDB(db, opts...) + _applicationPassword.applicationPasswordDo.UseModel(&entity.ApplicationPassword{}) + + tableName := _applicationPassword.applicationPasswordDo.TableName() + _applicationPassword.ALL = field.NewAsterisk(tableName) + _applicationPassword.ID = field.NewInt32(tableName, "id") + _applicationPassword.CreateTime = field.NewTime(tableName, "create_time") + _applicationPassword.UpdateTime = field.NewTime(tableName, "update_time") + _applicationPassword.Name = field.NewString(tableName, "name") + _applicationPassword.Password = field.NewString(tableName, "password") + _applicationPassword.UserID = field.NewInt32(tableName, "user_id") + _applicationPassword.LastActivateTime = field.NewTime(tableName, "last_activate_time") + _applicationPassword.LastActivateIP = field.NewString(tableName, "last_activate_ip") + + _applicationPassword.fillFieldMap() + + return _applicationPassword +} + +type applicationPassword struct { + applicationPasswordDo applicationPasswordDo + + ALL field.Asterisk + ID field.Int32 + CreateTime field.Time + UpdateTime field.Time + Name field.String + Password field.String + UserID field.Int32 + LastActivateTime field.Time + LastActivateIP field.String + + fieldMap map[string]field.Expr +} + +func (a applicationPassword) Table(newTableName string) *applicationPassword { + a.applicationPasswordDo.UseTable(newTableName) + return a.updateTableName(newTableName) +} + +func (a applicationPassword) As(alias string) *applicationPassword { + a.applicationPasswordDo.DO = *(a.applicationPasswordDo.As(alias).(*gen.DO)) + return a.updateTableName(alias) +} + +func (a *applicationPassword) updateTableName(table string) *applicationPassword { + a.ALL = field.NewAsterisk(table) + a.ID = field.NewInt32(table, "id") + a.CreateTime = field.NewTime(table, "create_time") + a.UpdateTime = field.NewTime(table, "update_time") + a.Name = field.NewString(table, "name") + a.Password = field.NewString(table, "password") + a.UserID = field.NewInt32(table, "user_id") + a.LastActivateTime = field.NewTime(table, "last_activate_time") + a.LastActivateIP = field.NewString(table, "last_activate_ip") + + a.fillFieldMap() + + return a +} + +func (a *applicationPassword) WithContext(ctx context.Context) *applicationPasswordDo { + return a.applicationPasswordDo.WithContext(ctx) +} + +func (a applicationPassword) TableName() string { return a.applicationPasswordDo.TableName() } + +func (a applicationPassword) Alias() string { return a.applicationPasswordDo.Alias() } + +func (a applicationPassword) Columns(cols ...field.Expr) gen.Columns { + return a.applicationPasswordDo.Columns(cols...) +} + +func (a *applicationPassword) GetFieldByName(fieldName string) (field.OrderExpr, bool) { + _f, ok := a.fieldMap[fieldName] + if !ok || _f == nil { + return nil, false + } + _oe, ok := _f.(field.OrderExpr) + return _oe, ok +} + +func (a *applicationPassword) fillFieldMap() { + a.fieldMap = make(map[string]field.Expr, 8) + a.fieldMap["id"] = a.ID + a.fieldMap["create_time"] = a.CreateTime + a.fieldMap["update_time"] = a.UpdateTime + a.fieldMap["name"] = a.Name + a.fieldMap["password"] = a.Password + a.fieldMap["user_id"] = a.UserID + a.fieldMap["last_activate_time"] = a.LastActivateTime + a.fieldMap["last_activate_ip"] = a.LastActivateIP +} + +func (a applicationPassword) clone(db *gorm.DB) applicationPassword { + a.applicationPasswordDo.ReplaceConnPool(db.Statement.ConnPool) + return a +} + +func (a applicationPassword) replaceDB(db *gorm.DB) applicationPassword { + a.applicationPasswordDo.ReplaceDB(db) + return a +} + +type applicationPasswordDo struct{ gen.DO } + +func (a applicationPasswordDo) Debug() *applicationPasswordDo { + return a.withDO(a.DO.Debug()) +} + +func (a applicationPasswordDo) WithContext(ctx context.Context) *applicationPasswordDo { + return a.withDO(a.DO.WithContext(ctx)) +} + +func (a applicationPasswordDo) ReadDB() *applicationPasswordDo { + return a.Clauses(dbresolver.Read) +} + +func (a applicationPasswordDo) WriteDB() *applicationPasswordDo { + return a.Clauses(dbresolver.Write) +} + +func (a applicationPasswordDo) Session(config *gorm.Session) *applicationPasswordDo { + return a.withDO(a.DO.Session(config)) +} + +func (a applicationPasswordDo) Clauses(conds ...clause.Expression) *applicationPasswordDo { + return a.withDO(a.DO.Clauses(conds...)) +} + +func (a applicationPasswordDo) Returning(value interface{}, columns ...string) *applicationPasswordDo { + return a.withDO(a.DO.Returning(value, columns...)) +} + +func (a applicationPasswordDo) Not(conds ...gen.Condition) *applicationPasswordDo { + return a.withDO(a.DO.Not(conds...)) +} + +func (a applicationPasswordDo) Or(conds ...gen.Condition) *applicationPasswordDo { + return a.withDO(a.DO.Or(conds...)) +} + +func (a applicationPasswordDo) Select(conds ...field.Expr) *applicationPasswordDo { + return a.withDO(a.DO.Select(conds...)) +} + +func (a applicationPasswordDo) Where(conds ...gen.Condition) *applicationPasswordDo { + return a.withDO(a.DO.Where(conds...)) +} + +func (a applicationPasswordDo) Order(conds ...field.Expr) *applicationPasswordDo { + return a.withDO(a.DO.Order(conds...)) +} + +func (a applicationPasswordDo) Distinct(cols ...field.Expr) *applicationPasswordDo { + return a.withDO(a.DO.Distinct(cols...)) +} + +func (a applicationPasswordDo) Omit(cols ...field.Expr) *applicationPasswordDo { + return a.withDO(a.DO.Omit(cols...)) +} + +func (a applicationPasswordDo) Join(table schema.Tabler, on ...field.Expr) *applicationPasswordDo { + return a.withDO(a.DO.Join(table, on...)) +} + +func (a applicationPasswordDo) LeftJoin(table schema.Tabler, on ...field.Expr) *applicationPasswordDo { + return a.withDO(a.DO.LeftJoin(table, on...)) +} + +func (a applicationPasswordDo) RightJoin(table schema.Tabler, on ...field.Expr) *applicationPasswordDo { + return a.withDO(a.DO.RightJoin(table, on...)) +} + +func (a applicationPasswordDo) Group(cols ...field.Expr) *applicationPasswordDo { + return a.withDO(a.DO.Group(cols...)) +} + +func (a applicationPasswordDo) Having(conds ...gen.Condition) *applicationPasswordDo { + return a.withDO(a.DO.Having(conds...)) +} + +func (a applicationPasswordDo) Limit(limit int) *applicationPasswordDo { + return a.withDO(a.DO.Limit(limit)) +} + +func (a applicationPasswordDo) Offset(offset int) *applicationPasswordDo { + return a.withDO(a.DO.Offset(offset)) +} + +func (a applicationPasswordDo) Scopes(funcs ...func(gen.Dao) gen.Dao) *applicationPasswordDo { + return a.withDO(a.DO.Scopes(funcs...)) +} + +func (a applicationPasswordDo) Unscoped() *applicationPasswordDo { + return a.withDO(a.DO.Unscoped()) +} + +func (a applicationPasswordDo) Create(values ...*entity.ApplicationPassword) error { + if len(values) == 0 { + return nil + } + return a.DO.Create(values) +} + +func (a applicationPasswordDo) CreateInBatches(values []*entity.ApplicationPassword, batchSize int) error { + return a.DO.CreateInBatches(values, batchSize) +} + +// Save : !!! underlying implementation is different with GORM +// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values) +func (a applicationPasswordDo) Save(values ...*entity.ApplicationPassword) error { + if len(values) == 0 { + return nil + } + return a.DO.Save(values) +} + +func (a applicationPasswordDo) First() (*entity.ApplicationPassword, error) { + if result, err := a.DO.First(); err != nil { + return nil, err + } else { + return result.(*entity.ApplicationPassword), nil + } +} + +func (a applicationPasswordDo) Take() (*entity.ApplicationPassword, error) { + if result, err := a.DO.Take(); err != nil { + return nil, err + } else { + return result.(*entity.ApplicationPassword), nil + } +} + +func (a applicationPasswordDo) Last() (*entity.ApplicationPassword, error) { + if result, err := a.DO.Last(); err != nil { + return nil, err + } else { + return result.(*entity.ApplicationPassword), nil + } +} + +func (a applicationPasswordDo) Find() ([]*entity.ApplicationPassword, error) { + result, err := a.DO.Find() + return result.([]*entity.ApplicationPassword), err +} + +func (a applicationPasswordDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*entity.ApplicationPassword, err error) { + buf := make([]*entity.ApplicationPassword, 0, batchSize) + err = a.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error { + defer func() { results = append(results, buf...) }() + return fc(tx, batch) + }) + return results, err +} + +func (a applicationPasswordDo) FindInBatches(result *[]*entity.ApplicationPassword, batchSize int, fc func(tx gen.Dao, batch int) error) error { + return a.DO.FindInBatches(result, batchSize, fc) +} + +func (a applicationPasswordDo) Attrs(attrs ...field.AssignExpr) *applicationPasswordDo { + return a.withDO(a.DO.Attrs(attrs...)) +} + +func (a applicationPasswordDo) Assign(attrs ...field.AssignExpr) *applicationPasswordDo { + return a.withDO(a.DO.Assign(attrs...)) +} + +func (a applicationPasswordDo) Joins(fields ...field.RelationField) *applicationPasswordDo { + for _, _f := range fields { + a = *a.withDO(a.DO.Joins(_f)) + } + return &a +} + +func (a applicationPasswordDo) Preload(fields ...field.RelationField) *applicationPasswordDo { + for _, _f := range fields { + a = *a.withDO(a.DO.Preload(_f)) + } + return &a +} + +func (a applicationPasswordDo) FirstOrInit() (*entity.ApplicationPassword, error) { + if result, err := a.DO.FirstOrInit(); err != nil { + return nil, err + } else { + return result.(*entity.ApplicationPassword), nil + } +} + +func (a applicationPasswordDo) FirstOrCreate() (*entity.ApplicationPassword, error) { + if result, err := a.DO.FirstOrCreate(); err != nil { + return nil, err + } else { + return result.(*entity.ApplicationPassword), nil + } +} + +func (a applicationPasswordDo) FindByPage(offset int, limit int) (result []*entity.ApplicationPassword, count int64, err error) { + result, err = a.Offset(offset).Limit(limit).Find() + if err != nil { + return + } + + if size := len(result); 0 < limit && 0 < size && size < limit { + count = int64(size + offset) + return + } + + count, err = a.Offset(-1).Limit(-1).Count() + return +} + +func (a applicationPasswordDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) { + count, err = a.Count() + if err != nil { + return + } + + err = a.Offset(offset).Limit(limit).Scan(result) + return +} + +func (a applicationPasswordDo) Scan(result interface{}) (err error) { + return a.DO.Scan(result) +} + +func (a applicationPasswordDo) Delete(models ...*entity.ApplicationPassword) (result gen.ResultInfo, err error) { + return a.DO.Delete(models) +} + +func (a *applicationPasswordDo) withDO(do gen.Dao) *applicationPasswordDo { + a.DO = *do.(*gen.DO) + return a +} diff --git a/dal/gen.go b/dal/gen.go index 4fc226c9..1e7ea8c5 100644 --- a/dal/gen.go +++ b/dal/gen.go @@ -8,18 +8,20 @@ import ( "context" "database/sql" - "gorm.io/gen" "gorm.io/gorm" + + "gorm.io/gen" + "gorm.io/plugin/dbresolver" ) var ( Q = new(Query) + ApplicationPassword *applicationPassword Attachment *attachment Category *category Comment *comment CommentBlack *commentBlack - FlywaySchemaHistory *flywaySchemaHistory Journal *journal Link *link Log *log @@ -37,11 +39,11 @@ var ( func SetDefault(db *gorm.DB, opts ...gen.DOOption) { *Q = *Use(db, opts...) + ApplicationPassword = &Q.ApplicationPassword Attachment = &Q.Attachment Category = &Q.Category Comment = &Q.Comment CommentBlack = &Q.CommentBlack - FlywaySchemaHistory = &Q.FlywaySchemaHistory Journal = &Q.Journal Link = &Q.Link Log = &Q.Log @@ -60,11 +62,11 @@ func SetDefault(db *gorm.DB, opts ...gen.DOOption) { func Use(db *gorm.DB, opts ...gen.DOOption) *Query { return &Query{ db: db, + ApplicationPassword: newApplicationPassword(db, opts...), Attachment: newAttachment(db, opts...), Category: newCategory(db, opts...), Comment: newComment(db, opts...), CommentBlack: newCommentBlack(db, opts...), - FlywaySchemaHistory: newFlywaySchemaHistory(db, opts...), Journal: newJournal(db, opts...), Link: newLink(db, opts...), Log: newLog(db, opts...), @@ -84,11 +86,11 @@ func Use(db *gorm.DB, opts ...gen.DOOption) *Query { type Query struct { db *gorm.DB + ApplicationPassword applicationPassword Attachment attachment Category category Comment comment CommentBlack commentBlack - FlywaySchemaHistory flywaySchemaHistory Journal journal Link link Log log @@ -109,11 +111,11 @@ func (q *Query) Available() bool { return q.db != nil } func (q *Query) clone(db *gorm.DB) *Query { return &Query{ db: db, + ApplicationPassword: q.ApplicationPassword.clone(db), Attachment: q.Attachment.clone(db), Category: q.Category.clone(db), Comment: q.Comment.clone(db), CommentBlack: q.CommentBlack.clone(db), - FlywaySchemaHistory: q.FlywaySchemaHistory.clone(db), Journal: q.Journal.clone(db), Link: q.Link.clone(db), Log: q.Log.clone(db), @@ -141,11 +143,11 @@ func (q *Query) WriteDB() *Query { func (q *Query) ReplaceDB(db *gorm.DB) *Query { return &Query{ db: db, + ApplicationPassword: q.ApplicationPassword.replaceDB(db), Attachment: q.Attachment.replaceDB(db), Category: q.Category.replaceDB(db), Comment: q.Comment.replaceDB(db), CommentBlack: q.CommentBlack.replaceDB(db), - FlywaySchemaHistory: q.FlywaySchemaHistory.replaceDB(db), Journal: q.Journal.replaceDB(db), Link: q.Link.replaceDB(db), Log: q.Log.replaceDB(db), @@ -163,11 +165,11 @@ func (q *Query) ReplaceDB(db *gorm.DB) *Query { } type queryCtx struct { + ApplicationPassword *applicationPasswordDo Attachment *attachmentDo Category *categoryDo Comment *commentDo CommentBlack *commentBlackDo - FlywaySchemaHistory *flywaySchemaHistoryDo Journal *journalDo Link *linkDo Log *logDo @@ -185,11 +187,11 @@ type queryCtx struct { func (q *Query) WithContext(ctx context.Context) *queryCtx { return &queryCtx{ + ApplicationPassword: q.ApplicationPassword.WithContext(ctx), Attachment: q.Attachment.WithContext(ctx), Category: q.Category.WithContext(ctx), Comment: q.Comment.WithContext(ctx), CommentBlack: q.CommentBlack.WithContext(ctx), - FlywaySchemaHistory: q.FlywaySchemaHistory.WithContext(ctx), Journal: q.Journal.WithContext(ctx), Link: q.Link.WithContext(ctx), Log: q.Log.WithContext(ctx), diff --git a/go.mod b/go.mod index 4093fc76..4540e40d 100644 --- a/go.mod +++ b/go.mod @@ -31,7 +31,7 @@ require ( go.uber.org/dig v1.17.1 go.uber.org/fx v1.20.1 go.uber.org/zap v1.26.0 - golang.org/x/crypto v0.14.0 + golang.org/x/crypto v0.15.0 golang.org/x/image v0.13.0 gopkg.in/yaml.v2 v2.4.0 gorm.io/driver/mysql v1.5.2 @@ -105,12 +105,12 @@ require ( go.uber.org/multierr v1.11.0 // indirect golang.org/x/arch v0.5.0 // indirect golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect - golang.org/x/mod v0.13.0 // indirect - golang.org/x/net v0.17.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect + golang.org/x/mod v0.14.0 // indirect + golang.org/x/net v0.18.0 // indirect + golang.org/x/sys v0.14.0 // indirect + golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.14.0 // indirect + golang.org/x/tools v0.15.0 // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect diff --git a/go.sum b/go.sum index 52b12af1..4b880e46 100644 --- a/go.sum +++ b/go.sum @@ -350,6 +350,8 @@ github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6g github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= +github.com/sethvargo/go-password v0.2.0 h1:BTDl4CC/gjf/axHMaDQtw507ogrXLci6XRiLc7i/UHI= +github.com/sethvargo/go-password v0.2.0/go.mod h1:Ym4Mr9JXLBycr02MFuVQ/0JHidNetSgbzutTr3zsYXE= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= @@ -441,6 +443,8 @@ golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2Uz golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= +golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -483,6 +487,8 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -521,6 +527,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= +golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -544,6 +552,7 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -593,6 +602,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= @@ -600,6 +611,7 @@ golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -613,6 +625,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -669,6 +683,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= +golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8= +golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/handler/admin/application_password.go b/handler/admin/application_password.go new file mode 100644 index 00000000..ab5c2d96 --- /dev/null +++ b/handler/admin/application_password.go @@ -0,0 +1,59 @@ +package admin + +import ( + "errors" + "github.com/gin-gonic/gin" + "github.com/go-playground/validator/v10" + "github.com/go-sonic/sonic/handler/trans" + "github.com/go-sonic/sonic/model/param" + "github.com/go-sonic/sonic/service" + "github.com/go-sonic/sonic/util" + "github.com/go-sonic/sonic/util/xerr" +) + +type ApplicationPasswordHandler struct { + ApplicationPasswordService service.ApplicationPasswordService +} + +func NewApplicationPasswordHandler(applicationPasswordService service.ApplicationPasswordService) *ApplicationPasswordHandler { + return &ApplicationPasswordHandler{ + ApplicationPasswordService: applicationPasswordService, + } +} + +func (a *ApplicationPasswordHandler) Create(ctx *gin.Context) (interface{}, error) { + appPwdParam, err := parseAppPwdParam(ctx) + if err != nil { + return nil, err + } + + return a.ApplicationPasswordService.CreatePwd(ctx, appPwdParam) +} + +func (a *ApplicationPasswordHandler) Delete(ctx *gin.Context) (interface{}, error) { + name, err := util.ParamString(ctx, "name") + if err != nil { + return nil, err + } + + appPwdParam := ¶m.ApplicationPasswordParam{Name: name} + + return nil, a.ApplicationPasswordService.DeletePwd(ctx, appPwdParam) +} + +func (a *ApplicationPasswordHandler) List(ctx *gin.Context) (interface{}, error) { + return a.ApplicationPasswordService.List(ctx) +} + +func parseAppPwdParam(ctx *gin.Context) (*param.ApplicationPasswordParam, error) { + var appPwdParam param.ApplicationPasswordParam + err := ctx.ShouldBindJSON(&appPwdParam) + if err != nil { + e := validator.ValidationErrors{} + if errors.As(err, &e) { + return nil, xerr.WithStatus(e, xerr.StatusBadRequest).WithMsg(trans.Translate(e)) + } + return nil, xerr.WithStatus(err, xerr.StatusBadRequest) + } + return &appPwdParam, nil +} diff --git a/handler/admin/init.go b/handler/admin/init.go index f1a9d542..a7cfc800 100644 --- a/handler/admin/init.go +++ b/handler/admin/init.go @@ -25,5 +25,6 @@ func init() { NewThemeHandler, NewUserHandler, NewEmailHandler, + NewApplicationPasswordHandler, ) } diff --git a/handler/router.go b/handler/router.go index b453efd2..74c98a33 100644 --- a/handler/router.go +++ b/handler/router.go @@ -271,6 +271,12 @@ func (s *Server) RegisterRouters() { themeRouter.POST("reload", s.wrapHandler(s.ThemeHandler.ReloadTheme)) themeRouter.GET("activation/template/exists", s.wrapHandler(s.ThemeHandler.TemplateExist)) } + { + appPwdRouter := authRouter.Group("application_password") + appPwdRouter.POST("", s.wrapHandler(s.ApplicationPasswordHandler.Create)) + appPwdRouter.DELETE("/:name", s.wrapHandler(s.ApplicationPasswordHandler.Delete)) + appPwdRouter.GET("", s.wrapHandler(s.ApplicationPasswordHandler.List)) + } { emailRouter := authRouter.Group("/mails") emailRouter.POST("/test", s.wrapHandler(s.EmailHandler.Test)) diff --git a/handler/server.go b/handler/server.go index 24e2dfc4..a1f79354 100644 --- a/handler/server.go +++ b/handler/server.go @@ -25,113 +25,115 @@ import ( ) type Server struct { - logger *zap.Logger - Config *config.Config - HTTPServer *http.Server - Router *gin.Engine - Template *template.Template - AuthMiddleware *middleware.AuthMiddleware - LogMiddleware *middleware.GinLoggerMiddleware - RecoveryMiddleware *middleware.RecoveryMiddleware - InstallRedirectMiddleware *middleware.InstallRedirectMiddleware - OptionService service.OptionService - ThemeService service.ThemeService - SheetService service.SheetService - AdminHandler *admin.AdminHandler - AttachmentHandler *admin.AttachmentHandler - BackupHandler *admin.BackupHandler - CategoryHandler *admin.CategoryHandler - InstallHandler *admin.InstallHandler - JournalHandler *admin.JournalHandler - JournalCommentHandler *admin.JournalCommentHandler - LinkHandler *admin.LinkHandler - LogHandler *admin.LogHandler - MenuHandler *admin.MenuHandler - OptionHandler *admin.OptionHandler - PhotoHandler *admin.PhotoHandler - PostHandler *admin.PostHandler - PostCommentHandler *admin.PostCommentHandler - SheetHandler *admin.SheetHandler - SheetCommentHandler *admin.SheetCommentHandler - StatisticHandler *admin.StatisticHandler - TagHandler *admin.TagHandler - ThemeHandler *admin.ThemeHandler - UserHandler *admin.UserHandler - EmailHandler *admin.EmailHandler - IndexHandler *content.IndexHandler - FeedHandler *content.FeedHandler - ArchiveHandler *content.ArchiveHandler - ViewHandler *content.ViewHandler - ContentCategoryHandler *content.CategoryHandler - ContentSheetHandler *content.SheetHandler - ContentTagHandler *content.TagHandler - ContentLinkHandler *content.LinkHandler - ContentPhotoHandler *content.PhotoHandler - ContentJournalHandler *content.JournalHandler - ContentSearchHandler *content.SearchHandler - ContentAPIArchiveHandler *api.ArchiveHandler - ContentAPICategoryHandler *api.CategoryHandler - ContentAPIJournalHandler *api.JournalHandler - ContentAPILinkHandler *api.LinkHandler - ContentAPIPostHandler *api.PostHandler - ContentAPISheetHandler *api.SheetHandler - ContentAPIOptionHandler *api.OptionHandler - ContentAPIPhotoHandler *api.PhotoHandler + logger *zap.Logger + Config *config.Config + HTTPServer *http.Server + Router *gin.Engine + Template *template.Template + AuthMiddleware *middleware.AuthMiddleware + LogMiddleware *middleware.GinLoggerMiddleware + RecoveryMiddleware *middleware.RecoveryMiddleware + InstallRedirectMiddleware *middleware.InstallRedirectMiddleware + OptionService service.OptionService + ThemeService service.ThemeService + SheetService service.SheetService + AdminHandler *admin.AdminHandler + AttachmentHandler *admin.AttachmentHandler + BackupHandler *admin.BackupHandler + CategoryHandler *admin.CategoryHandler + InstallHandler *admin.InstallHandler + JournalHandler *admin.JournalHandler + JournalCommentHandler *admin.JournalCommentHandler + LinkHandler *admin.LinkHandler + LogHandler *admin.LogHandler + MenuHandler *admin.MenuHandler + OptionHandler *admin.OptionHandler + PhotoHandler *admin.PhotoHandler + PostHandler *admin.PostHandler + PostCommentHandler *admin.PostCommentHandler + SheetHandler *admin.SheetHandler + SheetCommentHandler *admin.SheetCommentHandler + StatisticHandler *admin.StatisticHandler + TagHandler *admin.TagHandler + ThemeHandler *admin.ThemeHandler + UserHandler *admin.UserHandler + EmailHandler *admin.EmailHandler + IndexHandler *content.IndexHandler + FeedHandler *content.FeedHandler + ArchiveHandler *content.ArchiveHandler + ViewHandler *content.ViewHandler + ContentCategoryHandler *content.CategoryHandler + ContentSheetHandler *content.SheetHandler + ContentTagHandler *content.TagHandler + ContentLinkHandler *content.LinkHandler + ContentPhotoHandler *content.PhotoHandler + ContentJournalHandler *content.JournalHandler + ContentSearchHandler *content.SearchHandler + ContentAPIArchiveHandler *api.ArchiveHandler + ContentAPICategoryHandler *api.CategoryHandler + ContentAPIJournalHandler *api.JournalHandler + ContentAPILinkHandler *api.LinkHandler + ContentAPIPostHandler *api.PostHandler + ContentAPISheetHandler *api.SheetHandler + ContentAPIOptionHandler *api.OptionHandler + ContentAPIPhotoHandler *api.PhotoHandler + ApplicationPasswordHandler *admin.ApplicationPasswordHandler } type ServerParams struct { dig.In - Config *config.Config - Logger *zap.Logger - Event event.Bus - Template *template.Template - AuthMiddleware *middleware.AuthMiddleware - LogMiddleware *middleware.GinLoggerMiddleware - RecoveryMiddleware *middleware.RecoveryMiddleware - InstallRedirectMiddleware *middleware.InstallRedirectMiddleware - OptionService service.OptionService - ThemeService service.ThemeService - SheetService service.SheetService - AdminHandler *admin.AdminHandler - AttachmentHandler *admin.AttachmentHandler - BackupHandler *admin.BackupHandler - CategoryHandler *admin.CategoryHandler - InstallHandler *admin.InstallHandler - JournalHandler *admin.JournalHandler - JournalCommentHandler *admin.JournalCommentHandler - LinkHandler *admin.LinkHandler - LogHandler *admin.LogHandler - MenuHandler *admin.MenuHandler - OptionHandler *admin.OptionHandler - PhotoHandler *admin.PhotoHandler - PostHandler *admin.PostHandler - PostCommentHandler *admin.PostCommentHandler - SheetHandler *admin.SheetHandler - SheetCommentHandler *admin.SheetCommentHandler - StatisticHandler *admin.StatisticHandler - TagHandler *admin.TagHandler - ThemeHandler *admin.ThemeHandler - UserHandler *admin.UserHandler - EmailHandler *admin.EmailHandler - IndexHandler *content.IndexHandler - FeedHandler *content.FeedHandler - ArchiveHandler *content.ArchiveHandler - ViewHandler *content.ViewHandler - ContentCategoryHandler *content.CategoryHandler - ContentSheetHandler *content.SheetHandler - ContentTagHandler *content.TagHandler - ContentLinkHandler *content.LinkHandler - ContentPhotoHandler *content.PhotoHandler - ContentJournalHandler *content.JournalHandler - ContentSearchHandler *content.SearchHandler - ContentAPIArchiveHandler *api.ArchiveHandler - ContentAPICategoryHandler *api.CategoryHandler - ContentAPIJournalHandler *api.JournalHandler - ContentAPILinkHandler *api.LinkHandler - ContentAPIPostHandler *api.PostHandler - ContentAPISheetHandler *api.SheetHandler - ContentAPIOptionHandler *api.OptionHandler - ContentAPIPhotoHandler *api.PhotoHandler + Config *config.Config + Logger *zap.Logger + Event event.Bus + Template *template.Template + AuthMiddleware *middleware.AuthMiddleware + LogMiddleware *middleware.GinLoggerMiddleware + RecoveryMiddleware *middleware.RecoveryMiddleware + InstallRedirectMiddleware *middleware.InstallRedirectMiddleware + OptionService service.OptionService + ThemeService service.ThemeService + SheetService service.SheetService + AdminHandler *admin.AdminHandler + AttachmentHandler *admin.AttachmentHandler + BackupHandler *admin.BackupHandler + CategoryHandler *admin.CategoryHandler + InstallHandler *admin.InstallHandler + JournalHandler *admin.JournalHandler + JournalCommentHandler *admin.JournalCommentHandler + LinkHandler *admin.LinkHandler + LogHandler *admin.LogHandler + MenuHandler *admin.MenuHandler + OptionHandler *admin.OptionHandler + PhotoHandler *admin.PhotoHandler + PostHandler *admin.PostHandler + PostCommentHandler *admin.PostCommentHandler + SheetHandler *admin.SheetHandler + SheetCommentHandler *admin.SheetCommentHandler + StatisticHandler *admin.StatisticHandler + TagHandler *admin.TagHandler + ThemeHandler *admin.ThemeHandler + UserHandler *admin.UserHandler + EmailHandler *admin.EmailHandler + ApplicationPasswordHandler *admin.ApplicationPasswordHandler + IndexHandler *content.IndexHandler + FeedHandler *content.FeedHandler + ArchiveHandler *content.ArchiveHandler + ViewHandler *content.ViewHandler + ContentCategoryHandler *content.CategoryHandler + ContentSheetHandler *content.SheetHandler + ContentTagHandler *content.TagHandler + ContentLinkHandler *content.LinkHandler + ContentPhotoHandler *content.PhotoHandler + ContentJournalHandler *content.JournalHandler + ContentSearchHandler *content.SearchHandler + ContentAPIArchiveHandler *api.ArchiveHandler + ContentAPICategoryHandler *api.CategoryHandler + ContentAPIJournalHandler *api.JournalHandler + ContentAPILinkHandler *api.LinkHandler + ContentAPIPostHandler *api.PostHandler + ContentAPISheetHandler *api.SheetHandler + ContentAPIOptionHandler *api.OptionHandler + ContentAPIPhotoHandler *api.PhotoHandler } func NewServer(param ServerParams, lifecycle fx.Lifecycle) *Server { @@ -145,58 +147,59 @@ func NewServer(param ServerParams, lifecycle fx.Lifecycle) *Server { } s := &Server{ - logger: param.Logger, - Config: param.Config, - HTTPServer: httpServer, - Router: router, - Template: param.Template, - AuthMiddleware: param.AuthMiddleware, - LogMiddleware: param.LogMiddleware, - RecoveryMiddleware: param.RecoveryMiddleware, - InstallRedirectMiddleware: param.InstallRedirectMiddleware, - AdminHandler: param.AdminHandler, - AttachmentHandler: param.AttachmentHandler, - BackupHandler: param.BackupHandler, - CategoryHandler: param.CategoryHandler, - InstallHandler: param.InstallHandler, - JournalHandler: param.JournalHandler, - JournalCommentHandler: param.JournalCommentHandler, - LinkHandler: param.LinkHandler, - LogHandler: param.LogHandler, - MenuHandler: param.MenuHandler, - OptionHandler: param.OptionHandler, - PhotoHandler: param.PhotoHandler, - PostHandler: param.PostHandler, - PostCommentHandler: param.PostCommentHandler, - SheetHandler: param.SheetHandler, - SheetCommentHandler: param.SheetCommentHandler, - StatisticHandler: param.StatisticHandler, - TagHandler: param.TagHandler, - ThemeHandler: param.ThemeHandler, - UserHandler: param.UserHandler, - EmailHandler: param.EmailHandler, - OptionService: param.OptionService, - ThemeService: param.ThemeService, - SheetService: param.SheetService, - IndexHandler: param.IndexHandler, - FeedHandler: param.FeedHandler, - ArchiveHandler: param.ArchiveHandler, - ViewHandler: param.ViewHandler, - ContentCategoryHandler: param.ContentCategoryHandler, - ContentSheetHandler: param.ContentSheetHandler, - ContentTagHandler: param.ContentTagHandler, - ContentLinkHandler: param.ContentLinkHandler, - ContentPhotoHandler: param.ContentPhotoHandler, - ContentJournalHandler: param.ContentJournalHandler, - ContentAPIArchiveHandler: param.ContentAPIArchiveHandler, - ContentAPICategoryHandler: param.ContentAPICategoryHandler, - ContentAPIJournalHandler: param.ContentAPIJournalHandler, - ContentAPILinkHandler: param.ContentAPILinkHandler, - ContentAPIPostHandler: param.ContentAPIPostHandler, - ContentAPISheetHandler: param.ContentAPISheetHandler, - ContentAPIOptionHandler: param.ContentAPIOptionHandler, - ContentSearchHandler: param.ContentSearchHandler, - ContentAPIPhotoHandler: param.ContentAPIPhotoHandler, + logger: param.Logger, + Config: param.Config, + HTTPServer: httpServer, + Router: router, + Template: param.Template, + AuthMiddleware: param.AuthMiddleware, + LogMiddleware: param.LogMiddleware, + RecoveryMiddleware: param.RecoveryMiddleware, + InstallRedirectMiddleware: param.InstallRedirectMiddleware, + AdminHandler: param.AdminHandler, + AttachmentHandler: param.AttachmentHandler, + BackupHandler: param.BackupHandler, + CategoryHandler: param.CategoryHandler, + InstallHandler: param.InstallHandler, + JournalHandler: param.JournalHandler, + JournalCommentHandler: param.JournalCommentHandler, + LinkHandler: param.LinkHandler, + LogHandler: param.LogHandler, + MenuHandler: param.MenuHandler, + OptionHandler: param.OptionHandler, + PhotoHandler: param.PhotoHandler, + PostHandler: param.PostHandler, + PostCommentHandler: param.PostCommentHandler, + SheetHandler: param.SheetHandler, + SheetCommentHandler: param.SheetCommentHandler, + StatisticHandler: param.StatisticHandler, + TagHandler: param.TagHandler, + ThemeHandler: param.ThemeHandler, + UserHandler: param.UserHandler, + EmailHandler: param.EmailHandler, + OptionService: param.OptionService, + ThemeService: param.ThemeService, + SheetService: param.SheetService, + IndexHandler: param.IndexHandler, + FeedHandler: param.FeedHandler, + ArchiveHandler: param.ArchiveHandler, + ViewHandler: param.ViewHandler, + ContentCategoryHandler: param.ContentCategoryHandler, + ContentSheetHandler: param.ContentSheetHandler, + ContentTagHandler: param.ContentTagHandler, + ContentLinkHandler: param.ContentLinkHandler, + ContentPhotoHandler: param.ContentPhotoHandler, + ContentJournalHandler: param.ContentJournalHandler, + ContentAPIArchiveHandler: param.ContentAPIArchiveHandler, + ContentAPICategoryHandler: param.ContentAPICategoryHandler, + ContentAPIJournalHandler: param.ContentAPIJournalHandler, + ContentAPILinkHandler: param.ContentAPILinkHandler, + ContentAPIPostHandler: param.ContentAPIPostHandler, + ContentAPISheetHandler: param.ContentAPISheetHandler, + ContentAPIOptionHandler: param.ContentAPIOptionHandler, + ContentSearchHandler: param.ContentSearchHandler, + ContentAPIPhotoHandler: param.ContentAPIPhotoHandler, + ApplicationPasswordHandler: param.ApplicationPasswordHandler, } lifecycle.Append(fx.Hook{ OnStop: httpServer.Shutdown, diff --git a/model/dto/application_password.go b/model/dto/application_password.go new file mode 100644 index 00000000..f737b451 --- /dev/null +++ b/model/dto/application_password.go @@ -0,0 +1,9 @@ +package dto + +type ApplicationPasswordDTO struct { + Name string `json:"name"` + Password string `json:"password"` + LastActivateTime int64 `json:"last_activate_time"` + LastActivateIp string `json:"last_activate_ip"` + CreateTime int64 `json:"create_time"` +} diff --git a/model/entity/application_password.gen.go b/model/entity/application_password.gen.go new file mode 100644 index 00000000..769f054f --- /dev/null +++ b/model/entity/application_password.gen.go @@ -0,0 +1,28 @@ +// Code generated by gorm.io/gen. DO NOT EDIT. +// Code generated by gorm.io/gen. DO NOT EDIT. +// Code generated by gorm.io/gen. DO NOT EDIT. + +package entity + +import ( + "time" +) + +const TableNameApplicationPassword = "application_password" + +// ApplicationPassword mapped from table +type ApplicationPassword struct { + ID *int32 `gorm:"column:id;type:INTEGER" json:"id"` + CreateTime time.Time `gorm:"column:create_time;type:datetime;not null" json:"create_time"` + UpdateTime *time.Time `gorm:"column:update_time;type:datetime" json:"update_time"` + Name string `gorm:"column:name;type:varchar(32);not null" json:"name"` + Password string `gorm:"column:password;type:varchar(256);not null" json:"password"` + UserID int32 `gorm:"column:user_id;type:integer;not null" json:"user_id"` + LastActivateTime *time.Time `gorm:"column:last_activate_time;type:datetime" json:"last_activate_time"` + LastActivateIP string `gorm:"column:last_activate_ip;type:varchar(128);not null;default:'' not null" json:"last_activate_ip"` +} + +// TableName ApplicationPassword's table name +func (*ApplicationPassword) TableName() string { + return TableNameApplicationPassword +} diff --git a/model/param/application_password.go b/model/param/application_password.go new file mode 100644 index 00000000..e056c496 --- /dev/null +++ b/model/param/application_password.go @@ -0,0 +1,5 @@ +package param + +type ApplicationPasswordParam struct { + Name string `json:"name" binding:"required"` +} diff --git a/scripts/table.sql b/scripts/table.sql index 50449429..72b58b05 100644 --- a/scripts/table.sql +++ b/scripts/table.sql @@ -294,3 +294,15 @@ create table if not exists user ) ENGINE = INNODB DEFAULT charset = utf8mb4; +create table if not exists application_password +( + id int auto_increment primary key, + create_time datetime(6) not null, + update_time datetime(6) null, + name varchar(32) not null, + password varchar(256) not null, + user_id int not null, + last_activate_time datetime(6) null, + last_activate_ip varchar(128) default '' not null +) ENGINE = INNODB + DEFAULT charset = utf8mb4; diff --git a/service/application_password.go b/service/application_password.go new file mode 100644 index 00000000..152d9af7 --- /dev/null +++ b/service/application_password.go @@ -0,0 +1,13 @@ +package service + +import ( + "context" + "github.com/go-sonic/sonic/model/dto" + "github.com/go-sonic/sonic/model/param" +) + +type ApplicationPasswordService interface { + CreatePwd(ctx context.Context, appPwdParam *param.ApplicationPasswordParam) (*dto.ApplicationPasswordDTO, error) + DeletePwd(ctx context.Context, appPwdParam *param.ApplicationPasswordParam) error + List(ctx context.Context) ([]*dto.ApplicationPasswordDTO, error) +} diff --git a/service/impl/application_password.go b/service/impl/application_password.go new file mode 100644 index 00000000..1d39f715 --- /dev/null +++ b/service/impl/application_password.go @@ -0,0 +1,134 @@ +package impl + +import ( + "context" + "github.com/go-sonic/sonic/model/dto" + "github.com/go-sonic/sonic/model/entity" + "github.com/go-sonic/sonic/util" + "github.com/go-sonic/sonic/util/xerr" + "strings" + "time" + + "github.com/go-sonic/sonic/dal" + "github.com/go-sonic/sonic/model/param" + "github.com/go-sonic/sonic/service" +) + +type applicationPasswordServiceImpl struct { + OptionService service.OptionService + AuthenticateService service.AuthenticateService +} + +func NewApplicationPasswordService(optionService service.OptionService, authenticateService service.AuthenticateService) service.ApplicationPasswordService { + return &applicationPasswordServiceImpl{ + OptionService: optionService, + AuthenticateService: authenticateService, + } +} + +func (a *applicationPasswordServiceImpl) CreatePwd(ctx context.Context, param *param.ApplicationPasswordParam) (*dto.ApplicationPasswordDTO, error) { + var err error + appPwdDTO := &dto.ApplicationPasswordDTO{ + Name: param.Name, + } + + user, err := MustGetAuthorizedUser(ctx) + if err != nil { + return nil, err + } + + err = dal.GetQueryByCtx(ctx).Transaction(func(tx *dal.Query) error { + appPwdDAL := tx.ApplicationPassword + + count, err := appPwdDAL.WithContext(ctx).Select().Where(appPwdDAL.Name.Eq(param.Name), appPwdDAL.UserID.Eq(user.ID)).Count() + if err != nil { + return WrapDBErr(err) + } + + if count > 0 { + return xerr.BadParam.New("").WithMsg("名称已经存在(Application password name already exists)").WithStatus(xerr.StatusBadRequest) + } + + token := util.GenUUIDWithOutDash() + tokenMd5 := util.Md5(token) + // pass claim token to frond, but save in db after md5 + appPwdDTO.Password = token + + currentTime := time.Now() + + appPwdEntity := &entity.ApplicationPassword{ + CreateTime: currentTime, + UpdateTime: ¤tTime, + Name: param.Name, + Password: tokenMd5, + UserID: user.ID, + LastActivateTime: nil, + LastActivateIP: "", + } + err = appPwdDAL.WithContext(ctx).Create(appPwdEntity) + if err != nil { + return WrapDBErr(err) + } + + return nil + }) + + if err != nil { + return nil, err + } + + return appPwdDTO, nil +} + +func (a *applicationPasswordServiceImpl) DeletePwd(ctx context.Context, appPwdParam *param.ApplicationPasswordParam) error { + var err error + user, err := MustGetAuthorizedUser(ctx) + if err != nil { + return err + } + + if appPwdParam == nil || len(strings.TrimSpace(appPwdParam.Name)) == 0 { + return xerr.BadParam.New("name参数为空").WithStatus(xerr.StatusBadRequest). + WithMsg("name 参数不能为空") + } + + appPwdParam.Name = strings.TrimSpace(appPwdParam.Name) + + appPwdDAL := dal.GetQueryByCtx(ctx).ApplicationPassword + if _, err = appPwdDAL.WithContext(ctx).Where(appPwdDAL.UserID.Eq(user.ID), appPwdDAL.Name.Eq(appPwdParam.Name)).Delete(); err != nil { + return WrapDBErr(err) + } + + return nil +} + +func (a *applicationPasswordServiceImpl) List(ctx context.Context) ([]*dto.ApplicationPasswordDTO, error) { + user, err := MustGetAuthorizedUser(ctx) + if err != nil { + return nil, err + } + + appPwdDAL := dal.GetQueryByCtx(ctx).ApplicationPassword + entities, err := appPwdDAL.WithContext(ctx).Where(appPwdDAL.UserID.Eq(user.ID)).Find() + if err != nil { + return nil, WrapDBErr(err) + } + + appPwdDTOList := make([]*dto.ApplicationPasswordDTO, len(entities)) + + for _, appPwdEntity := range entities { + var lastActivateTime int64 + if appPwdEntity.LastActivateTime == nil { + lastActivateTime = appPwdEntity.LastActivateTime.Unix() + } + appPwdDTO := &dto.ApplicationPasswordDTO{ + Name: appPwdEntity.Name, + LastActivateIp: appPwdEntity.LastActivateIP, + LastActivateTime: lastActivateTime, + CreateTime: appPwdEntity.CreateTime.Unix(), + } + appPwdDTOList = append(appPwdDTOList, appPwdDTO) + } + + return appPwdDTOList, nil +} diff --git a/service/impl/init.go b/service/impl/init.go index 826cf752..d7fb9378 100644 --- a/service/impl/init.go +++ b/service/impl/init.go @@ -40,5 +40,6 @@ func init() { NewUserService, NewExportImport, storage.NewFileStorageComposite, + NewApplicationPasswordService, ) } diff --git a/util/encrypt.go b/util/encrypt.go new file mode 100644 index 00000000..adf28f97 --- /dev/null +++ b/util/encrypt.go @@ -0,0 +1,13 @@ +package util + +import ( + "crypto/md5" + "encoding/hex" +) + +func Md5(s string) string { + d := []byte(s) + m := md5.New() + m.Write(d) + return hex.EncodeToString(m.Sum(nil)) +} From b77bcce0c64e9a198b9ededf1ba4fd4d7712346f Mon Sep 17 00:00:00 2001 From: textworld Date: Fri, 17 Nov 2023 21:33:55 +0800 Subject: [PATCH 03/11] feat: add manage apis for ApplicationPassword --- handler/content/wp/category.go | 51 ++++ handler/content/wp/init.go | 11 + handler/content/wp/post.go | 128 ++++++++ handler/content/wp/user.go | 31 ++ handler/middleware/application_password.go | 96 ++++++ handler/router.go | 8 + handler/server.go | 329 +++++++++++---------- main.go | 1 + model/dto/wp/category.go | 13 + model/dto/wp/post.go | 30 ++ model/dto/wp/user.go | 4 + model/param/wp_post.go | 30 ++ service/application_password.go | 3 + service/impl/application_password.go | 60 +++- util/common.go | 12 + 15 files changed, 638 insertions(+), 169 deletions(-) create mode 100644 handler/content/wp/category.go create mode 100644 handler/content/wp/init.go create mode 100644 handler/content/wp/post.go create mode 100644 handler/content/wp/user.go create mode 100644 handler/middleware/application_password.go create mode 100644 model/dto/wp/category.go create mode 100644 model/dto/wp/post.go create mode 100644 model/dto/wp/user.go create mode 100644 model/param/wp_post.go diff --git a/handler/content/wp/category.go b/handler/content/wp/category.go new file mode 100644 index 00000000..5bb5b188 --- /dev/null +++ b/handler/content/wp/category.go @@ -0,0 +1,51 @@ +package wp + +import ( + "github.com/gin-gonic/gin" + "github.com/go-sonic/sonic/model/dto/wp" + "github.com/go-sonic/sonic/model/entity" + "github.com/go-sonic/sonic/model/param" + "github.com/go-sonic/sonic/service" +) + +type CategoryHandler struct { + CategoryService service.CategoryService +} + +func NewCategoryHandler(categoryService service.CategoryService) *CategoryHandler { + return &CategoryHandler{ + CategoryService: categoryService, + } +} + +func (c *CategoryHandler) List(ctx *gin.Context) (interface{}, error) { + sort := ¶m.Sort{ + Fields: []string{"name,desc"}, + } + categoryEntities, err := c.CategoryService.ListAll(ctx, sort) + if err != nil { + return nil, err + } + + categoryDTOList := make([]*wp.CategoryDTO, len(categoryEntities)) + for _, categoryEntity := range categoryEntities { + categoryDTOList = append(categoryDTOList, convertToCategoryDTO(categoryEntity)) + } + + return categoryDTOList, nil +} + +func convertToCategoryDTO(categoryEntity *entity.Category) *wp.CategoryDTO { + categoryDTO := &wp.CategoryDTO{ + ID: categoryEntity.ID, + Count: 0, + Description: categoryEntity.Description, + Link: "", + Name: categoryEntity.Name, + Slug: categoryEntity.Slug, + Taxonomy: "", + Parent: categoryEntity.ParentID, + Meta: nil, + } + return categoryDTO +} diff --git a/handler/content/wp/init.go b/handler/content/wp/init.go new file mode 100644 index 00000000..dabe91ef --- /dev/null +++ b/handler/content/wp/init.go @@ -0,0 +1,11 @@ +package wp + +import "github.com/go-sonic/sonic/injection" + +func init() { + injection.Provide( + NewPostHandler, + NewUserHandler, + NewCategoryHandler, + ) +} diff --git a/handler/content/wp/post.go b/handler/content/wp/post.go new file mode 100644 index 00000000..586d5065 --- /dev/null +++ b/handler/content/wp/post.go @@ -0,0 +1,128 @@ +package wp + +import ( + "encoding/json" + "github.com/gin-gonic/gin" + "github.com/go-sonic/sonic/consts" + "github.com/go-sonic/sonic/log" + "github.com/go-sonic/sonic/model/dto/wp" + "github.com/go-sonic/sonic/model/entity" + "github.com/go-sonic/sonic/model/param" + "github.com/go-sonic/sonic/service" + "github.com/go-sonic/sonic/util" + "strings" + "time" +) + +type PostHandler struct { + PostService service.PostService +} + +func NewPostHandler(postService service.PostService) *PostHandler { + return &PostHandler{ + PostService: postService, + } +} + +func (handler *PostHandler) Create(ctx *gin.Context) (interface{}, error) { + var wpPost param.WpPost + err := ctx.ShouldBindJSON(&wpPost) + if err != nil { + return nil, util.WrapJsonBindErr(err) + } + + bytes, err := json.Marshal(wpPost) + if err != nil { + return nil, err + } + log.CtxInfo(ctx, "wpPost: "+string(bytes)) + + postParam := convertToPostParam(&wpPost) + + create, err := handler.PostService.Create(ctx, postParam) + if err != nil { + return nil, err + } + + return convertToWpPost(create), nil +} + +func convertToPostParam(wpPost *param.WpPost) *param.Post { + var paramPostStatus = consts.PostStatusPublished + if strings.ToLower(wpPost.Content) == "draft" { + paramPostStatus = consts.PostStatusDraft + } + + createTime := time.Now().Unix() + + return ¶m.Post{ + Title: wpPost.Title, + Status: paramPostStatus, + Slug: wpPost.Slug, + EditorType: nil, + OriginalContent: wpPost.Content, + Summary: "", + Thumbnail: "", + DisallowComment: false, + Password: wpPost.Password, + Template: "", + TopPriority: 0, + CreateTime: &createTime, + MetaKeywords: "", + MetaDescription: "", + TagIDs: make([]int32, 0), + CategoryIDs: make([]int32, 0), + MetaParam: nil, + Content: wpPost.Content, + EditTime: nil, + UpdateTime: nil, + } +} + +func convertToWpPost(postEntity *entity.Post) *wp.PostDTO { + timeFormat := time.RFC3339 + var wpStatus = "publish" + var wpCommentStatus = "open" + var wpContent = make(map[string]interface{}) + + if postEntity.Status == consts.PostStatusDraft { + wpStatus = "draft" + } + + if postEntity.DisallowComment { + wpCommentStatus = "close" + } + + wpContent["rendered"] = postEntity.OriginalContent + wpContent["protected"] = false + + var postDTO = &wp.PostDTO{ + Date: postEntity.CreateTime.Format(timeFormat), + DateGmt: postEntity.CreateTime.UTC().Format(timeFormat), + Guid: nil, + Id: postEntity.ID, + Link: "", + Modified: postEntity.UpdateTime.Format(timeFormat), + ModifiedGmt: postEntity.UpdateTime.UTC().Format(timeFormat), + Slug: "", + Status: wpStatus, + Type: "post", + Password: "standard", + PermalinkTemplate: "", + GeneratedSlug: "", + Title: "", + Content: wpContent, + Author: 0, + Excerpt: nil, + FeaturedMedia: 0, + CommentStatus: wpCommentStatus, + PingStatus: "open", + Format: "standard", + Meta: nil, + Sticky: false, + Template: "", + Categories: make([]int32, 0), + Tags: make([]int32, 0), + } + return postDTO +} diff --git a/handler/content/wp/user.go b/handler/content/wp/user.go new file mode 100644 index 00000000..13202433 --- /dev/null +++ b/handler/content/wp/user.go @@ -0,0 +1,31 @@ +package wp + +import ( + "github.com/gin-gonic/gin" + "github.com/go-sonic/sonic/model/dto" + "github.com/go-sonic/sonic/service" +) + +type UserHandler struct { + UserService service.UserService +} + +func NewUserHandler(userService service.UserService) *UserHandler { + return &UserHandler{ + UserService: userService, + } +} + +func (u *UserHandler) List(ctx *gin.Context) (interface{}, error) { + allUser, err := u.UserService.GetAllUser(ctx) + if err != nil { + return nil, err + } + + userDTOList := make([]*dto.User, len(allUser)) + for _, user := range allUser { + userDTO := u.UserService.ConvertToDTO(ctx, user) + userDTOList = append(userDTOList, userDTO) + } + return userDTOList, nil +} diff --git a/handler/middleware/application_password.go b/handler/middleware/application_password.go new file mode 100644 index 00000000..046ec53e --- /dev/null +++ b/handler/middleware/application_password.go @@ -0,0 +1,96 @@ +package middleware + +import ( + "encoding/base64" + "fmt" + "github.com/gin-gonic/gin" + "github.com/go-sonic/sonic/consts" + "github.com/go-sonic/sonic/model/dto" + "github.com/go-sonic/sonic/service" + "net/http" + "regexp" + "strings" +) + +type ApplicationPasswordMiddleware struct { + PasswordService service.ApplicationPasswordService + UserService service.UserService +} + +func NewApplicationPasswordMiddleware(passwordService service.ApplicationPasswordService, userService service.UserService) *ApplicationPasswordMiddleware { + m := &ApplicationPasswordMiddleware{ + PasswordService: passwordService, + UserService: userService, + } + return m +} + +func (a *ApplicationPasswordMiddleware) Get() error { + return nil +} + +func (a *ApplicationPasswordMiddleware) GetWrapHandler() gin.HandlerFunc { + + return func(ctx *gin.Context) { + header := ctx.GetHeader("Authorization") + if len(header) == 0 { + abortUnauthorized(ctx) + return + } + + match := verifyHeader(header) + if !match { + abortUnauthorized(ctx) + return + } + + bytes, err := base64.StdEncoding.DecodeString(header[6:]) + if err != nil { + abortUnauthorized(ctx) + return + } + + userPass := string(bytes) + + if !strings.Contains(userPass, ":") { + abortUnauthorized(ctx) + return + } + + splits := strings.SplitN(userPass, ":", 2) + + userEntity, err := a.UserService.GetByUsername(ctx, splits[0]) + if err != nil { + abortUnauthorized(ctx) + return + } + + pwdEntity, err := a.PasswordService.Verify(ctx, userEntity.ID, splits[1]) + if err != nil || pwdEntity == nil { + abortUnauthorized(ctx) + return + } + + err = a.PasswordService.Update(ctx, *pwdEntity.ID, ctx.ClientIP()) + if err != nil { + ctx.AbortWithStatusJSON(http.StatusInternalServerError, &dto.BaseDTO{ + Status: http.StatusInternalServerError, + Message: fmt.Sprintf("Update application password entity error, err=%s", err), + }) + return + } + ctx.Set(consts.AuthorizedUser, userEntity) + } +} + +func abortUnauthorized(ctx *gin.Context) { + ctx.AbortWithStatusJSON(http.StatusUnauthorized, &dto.BaseDTO{ + Status: http.StatusUnauthorized, + Message: "Unauthorized", + }) +} + +func verifyHeader(header string) bool { + compile, err := regexp.Compile("^Basic [a-z\\d/+]*={0,2}") + return err != nil || compile.MatchString(header) +} diff --git a/handler/router.go b/handler/router.go index 74c98a33..2d39c5bc 100644 --- a/handler/router.go +++ b/handler/router.go @@ -44,6 +44,13 @@ func (s *Server) RegisterRouters() { StaticFS(consts.SonicUploadDir, gin.Dir(s.Config.Sonic.UploadDir, false)) staticRouter.StaticFS("/themes/", gin.Dir(s.Config.Sonic.ThemeDir, false)) } + { + wpCompatibleRouter := router.Group("/wp-json/wp/v2") + wpCompatibleRouter.Use(s.ApplicationPasswordMiddleware.GetWrapHandler()) + wpCompatibleRouter.POST("/posts", s.wrapHandler(s.WpPostHandler.Create)) + wpCompatibleRouter.GET("/users", s.wrapHandler(s.WpUserHandler.List)) + wpCompatibleRouter.GET("/categories", s.wrapHandler(s.WpCategoryHandler.List)) + } { adminAPIRouter := router.Group("/api/admin") adminAPIRouter.Use(s.LogMiddleware.LoggerWithConfig(middleware.GinLoggerConfig{}), s.RecoveryMiddleware.RecoveryWithLogger(), s.InstallRedirectMiddleware.InstallRedirect()) @@ -353,6 +360,7 @@ func (s *Server) RegisterRouters() { contentAPIRouter.GET("/options/comment", s.wrapHandler(s.ContentAPIOptionHandler.Comment)) } + } } diff --git a/handler/server.go b/handler/server.go index a1f79354..c25ac4e0 100644 --- a/handler/server.go +++ b/handler/server.go @@ -3,6 +3,7 @@ package handler import ( "context" "fmt" + "github.com/go-sonic/sonic/handler/content/wp" "net/http" "os" "strconv" @@ -25,115 +26,123 @@ import ( ) type Server struct { - logger *zap.Logger - Config *config.Config - HTTPServer *http.Server - Router *gin.Engine - Template *template.Template - AuthMiddleware *middleware.AuthMiddleware - LogMiddleware *middleware.GinLoggerMiddleware - RecoveryMiddleware *middleware.RecoveryMiddleware - InstallRedirectMiddleware *middleware.InstallRedirectMiddleware - OptionService service.OptionService - ThemeService service.ThemeService - SheetService service.SheetService - AdminHandler *admin.AdminHandler - AttachmentHandler *admin.AttachmentHandler - BackupHandler *admin.BackupHandler - CategoryHandler *admin.CategoryHandler - InstallHandler *admin.InstallHandler - JournalHandler *admin.JournalHandler - JournalCommentHandler *admin.JournalCommentHandler - LinkHandler *admin.LinkHandler - LogHandler *admin.LogHandler - MenuHandler *admin.MenuHandler - OptionHandler *admin.OptionHandler - PhotoHandler *admin.PhotoHandler - PostHandler *admin.PostHandler - PostCommentHandler *admin.PostCommentHandler - SheetHandler *admin.SheetHandler - SheetCommentHandler *admin.SheetCommentHandler - StatisticHandler *admin.StatisticHandler - TagHandler *admin.TagHandler - ThemeHandler *admin.ThemeHandler - UserHandler *admin.UserHandler - EmailHandler *admin.EmailHandler - IndexHandler *content.IndexHandler - FeedHandler *content.FeedHandler - ArchiveHandler *content.ArchiveHandler - ViewHandler *content.ViewHandler - ContentCategoryHandler *content.CategoryHandler - ContentSheetHandler *content.SheetHandler - ContentTagHandler *content.TagHandler - ContentLinkHandler *content.LinkHandler - ContentPhotoHandler *content.PhotoHandler - ContentJournalHandler *content.JournalHandler - ContentSearchHandler *content.SearchHandler - ContentAPIArchiveHandler *api.ArchiveHandler - ContentAPICategoryHandler *api.CategoryHandler - ContentAPIJournalHandler *api.JournalHandler - ContentAPILinkHandler *api.LinkHandler - ContentAPIPostHandler *api.PostHandler - ContentAPISheetHandler *api.SheetHandler - ContentAPIOptionHandler *api.OptionHandler - ContentAPIPhotoHandler *api.PhotoHandler - ApplicationPasswordHandler *admin.ApplicationPasswordHandler + logger *zap.Logger + Config *config.Config + HTTPServer *http.Server + Router *gin.Engine + Template *template.Template + AuthMiddleware *middleware.AuthMiddleware + LogMiddleware *middleware.GinLoggerMiddleware + RecoveryMiddleware *middleware.RecoveryMiddleware + InstallRedirectMiddleware *middleware.InstallRedirectMiddleware + ApplicationPasswordMiddleware *middleware.ApplicationPasswordMiddleware + OptionService service.OptionService + ThemeService service.ThemeService + SheetService service.SheetService + AdminHandler *admin.AdminHandler + AttachmentHandler *admin.AttachmentHandler + BackupHandler *admin.BackupHandler + CategoryHandler *admin.CategoryHandler + InstallHandler *admin.InstallHandler + JournalHandler *admin.JournalHandler + JournalCommentHandler *admin.JournalCommentHandler + LinkHandler *admin.LinkHandler + LogHandler *admin.LogHandler + MenuHandler *admin.MenuHandler + OptionHandler *admin.OptionHandler + PhotoHandler *admin.PhotoHandler + PostHandler *admin.PostHandler + PostCommentHandler *admin.PostCommentHandler + SheetHandler *admin.SheetHandler + SheetCommentHandler *admin.SheetCommentHandler + StatisticHandler *admin.StatisticHandler + TagHandler *admin.TagHandler + ThemeHandler *admin.ThemeHandler + UserHandler *admin.UserHandler + EmailHandler *admin.EmailHandler + ApplicationPasswordHandler *admin.ApplicationPasswordHandler + IndexHandler *content.IndexHandler + FeedHandler *content.FeedHandler + ArchiveHandler *content.ArchiveHandler + ViewHandler *content.ViewHandler + ContentCategoryHandler *content.CategoryHandler + ContentSheetHandler *content.SheetHandler + ContentTagHandler *content.TagHandler + ContentLinkHandler *content.LinkHandler + ContentPhotoHandler *content.PhotoHandler + ContentJournalHandler *content.JournalHandler + ContentSearchHandler *content.SearchHandler + ContentAPIArchiveHandler *api.ArchiveHandler + ContentAPICategoryHandler *api.CategoryHandler + ContentAPIJournalHandler *api.JournalHandler + ContentAPILinkHandler *api.LinkHandler + ContentAPIPostHandler *api.PostHandler + ContentAPISheetHandler *api.SheetHandler + ContentAPIOptionHandler *api.OptionHandler + ContentAPIPhotoHandler *api.PhotoHandler + WpPostHandler *wp.PostHandler + WpUserHandler *wp.UserHandler + WpCategoryHandler *wp.CategoryHandler } type ServerParams struct { dig.In - Config *config.Config - Logger *zap.Logger - Event event.Bus - Template *template.Template - AuthMiddleware *middleware.AuthMiddleware - LogMiddleware *middleware.GinLoggerMiddleware - RecoveryMiddleware *middleware.RecoveryMiddleware - InstallRedirectMiddleware *middleware.InstallRedirectMiddleware - OptionService service.OptionService - ThemeService service.ThemeService - SheetService service.SheetService - AdminHandler *admin.AdminHandler - AttachmentHandler *admin.AttachmentHandler - BackupHandler *admin.BackupHandler - CategoryHandler *admin.CategoryHandler - InstallHandler *admin.InstallHandler - JournalHandler *admin.JournalHandler - JournalCommentHandler *admin.JournalCommentHandler - LinkHandler *admin.LinkHandler - LogHandler *admin.LogHandler - MenuHandler *admin.MenuHandler - OptionHandler *admin.OptionHandler - PhotoHandler *admin.PhotoHandler - PostHandler *admin.PostHandler - PostCommentHandler *admin.PostCommentHandler - SheetHandler *admin.SheetHandler - SheetCommentHandler *admin.SheetCommentHandler - StatisticHandler *admin.StatisticHandler - TagHandler *admin.TagHandler - ThemeHandler *admin.ThemeHandler - UserHandler *admin.UserHandler - EmailHandler *admin.EmailHandler - ApplicationPasswordHandler *admin.ApplicationPasswordHandler - IndexHandler *content.IndexHandler - FeedHandler *content.FeedHandler - ArchiveHandler *content.ArchiveHandler - ViewHandler *content.ViewHandler - ContentCategoryHandler *content.CategoryHandler - ContentSheetHandler *content.SheetHandler - ContentTagHandler *content.TagHandler - ContentLinkHandler *content.LinkHandler - ContentPhotoHandler *content.PhotoHandler - ContentJournalHandler *content.JournalHandler - ContentSearchHandler *content.SearchHandler - ContentAPIArchiveHandler *api.ArchiveHandler - ContentAPICategoryHandler *api.CategoryHandler - ContentAPIJournalHandler *api.JournalHandler - ContentAPILinkHandler *api.LinkHandler - ContentAPIPostHandler *api.PostHandler - ContentAPISheetHandler *api.SheetHandler - ContentAPIOptionHandler *api.OptionHandler - ContentAPIPhotoHandler *api.PhotoHandler + Config *config.Config + Logger *zap.Logger + Event event.Bus + Template *template.Template + AuthMiddleware *middleware.AuthMiddleware + LogMiddleware *middleware.GinLoggerMiddleware + RecoveryMiddleware *middleware.RecoveryMiddleware + InstallRedirectMiddleware *middleware.InstallRedirectMiddleware + ApplicationPasswordMiddleware *middleware.ApplicationPasswordMiddleware + OptionService service.OptionService + ThemeService service.ThemeService + SheetService service.SheetService + AdminHandler *admin.AdminHandler + AttachmentHandler *admin.AttachmentHandler + BackupHandler *admin.BackupHandler + CategoryHandler *admin.CategoryHandler + InstallHandler *admin.InstallHandler + JournalHandler *admin.JournalHandler + JournalCommentHandler *admin.JournalCommentHandler + LinkHandler *admin.LinkHandler + LogHandler *admin.LogHandler + MenuHandler *admin.MenuHandler + OptionHandler *admin.OptionHandler + PhotoHandler *admin.PhotoHandler + PostHandler *admin.PostHandler + PostCommentHandler *admin.PostCommentHandler + SheetHandler *admin.SheetHandler + SheetCommentHandler *admin.SheetCommentHandler + StatisticHandler *admin.StatisticHandler + TagHandler *admin.TagHandler + ThemeHandler *admin.ThemeHandler + UserHandler *admin.UserHandler + EmailHandler *admin.EmailHandler + ApplicationPasswordHandler *admin.ApplicationPasswordHandler + IndexHandler *content.IndexHandler + FeedHandler *content.FeedHandler + ArchiveHandler *content.ArchiveHandler + ViewHandler *content.ViewHandler + ContentCategoryHandler *content.CategoryHandler + ContentSheetHandler *content.SheetHandler + ContentTagHandler *content.TagHandler + ContentLinkHandler *content.LinkHandler + ContentPhotoHandler *content.PhotoHandler + ContentJournalHandler *content.JournalHandler + ContentSearchHandler *content.SearchHandler + ContentAPIArchiveHandler *api.ArchiveHandler + ContentAPICategoryHandler *api.CategoryHandler + ContentAPIJournalHandler *api.JournalHandler + ContentAPILinkHandler *api.LinkHandler + ContentAPIPostHandler *api.PostHandler + ContentAPISheetHandler *api.SheetHandler + ContentAPIOptionHandler *api.OptionHandler + ContentAPIPhotoHandler *api.PhotoHandler + WpPostHandler *wp.PostHandler + WpUserHandler *wp.UserHandler + WpCategoryHandler *wp.CategoryHandler } func NewServer(param ServerParams, lifecycle fx.Lifecycle) *Server { @@ -147,59 +156,63 @@ func NewServer(param ServerParams, lifecycle fx.Lifecycle) *Server { } s := &Server{ - logger: param.Logger, - Config: param.Config, - HTTPServer: httpServer, - Router: router, - Template: param.Template, - AuthMiddleware: param.AuthMiddleware, - LogMiddleware: param.LogMiddleware, - RecoveryMiddleware: param.RecoveryMiddleware, - InstallRedirectMiddleware: param.InstallRedirectMiddleware, - AdminHandler: param.AdminHandler, - AttachmentHandler: param.AttachmentHandler, - BackupHandler: param.BackupHandler, - CategoryHandler: param.CategoryHandler, - InstallHandler: param.InstallHandler, - JournalHandler: param.JournalHandler, - JournalCommentHandler: param.JournalCommentHandler, - LinkHandler: param.LinkHandler, - LogHandler: param.LogHandler, - MenuHandler: param.MenuHandler, - OptionHandler: param.OptionHandler, - PhotoHandler: param.PhotoHandler, - PostHandler: param.PostHandler, - PostCommentHandler: param.PostCommentHandler, - SheetHandler: param.SheetHandler, - SheetCommentHandler: param.SheetCommentHandler, - StatisticHandler: param.StatisticHandler, - TagHandler: param.TagHandler, - ThemeHandler: param.ThemeHandler, - UserHandler: param.UserHandler, - EmailHandler: param.EmailHandler, - OptionService: param.OptionService, - ThemeService: param.ThemeService, - SheetService: param.SheetService, - IndexHandler: param.IndexHandler, - FeedHandler: param.FeedHandler, - ArchiveHandler: param.ArchiveHandler, - ViewHandler: param.ViewHandler, - ContentCategoryHandler: param.ContentCategoryHandler, - ContentSheetHandler: param.ContentSheetHandler, - ContentTagHandler: param.ContentTagHandler, - ContentLinkHandler: param.ContentLinkHandler, - ContentPhotoHandler: param.ContentPhotoHandler, - ContentJournalHandler: param.ContentJournalHandler, - ContentAPIArchiveHandler: param.ContentAPIArchiveHandler, - ContentAPICategoryHandler: param.ContentAPICategoryHandler, - ContentAPIJournalHandler: param.ContentAPIJournalHandler, - ContentAPILinkHandler: param.ContentAPILinkHandler, - ContentAPIPostHandler: param.ContentAPIPostHandler, - ContentAPISheetHandler: param.ContentAPISheetHandler, - ContentAPIOptionHandler: param.ContentAPIOptionHandler, - ContentSearchHandler: param.ContentSearchHandler, - ContentAPIPhotoHandler: param.ContentAPIPhotoHandler, - ApplicationPasswordHandler: param.ApplicationPasswordHandler, + logger: param.Logger, + Config: param.Config, + HTTPServer: httpServer, + Router: router, + Template: param.Template, + AuthMiddleware: param.AuthMiddleware, + LogMiddleware: param.LogMiddleware, + RecoveryMiddleware: param.RecoveryMiddleware, + InstallRedirectMiddleware: param.InstallRedirectMiddleware, + ApplicationPasswordMiddleware: param.ApplicationPasswordMiddleware, + AdminHandler: param.AdminHandler, + AttachmentHandler: param.AttachmentHandler, + BackupHandler: param.BackupHandler, + CategoryHandler: param.CategoryHandler, + InstallHandler: param.InstallHandler, + JournalHandler: param.JournalHandler, + JournalCommentHandler: param.JournalCommentHandler, + LinkHandler: param.LinkHandler, + LogHandler: param.LogHandler, + MenuHandler: param.MenuHandler, + OptionHandler: param.OptionHandler, + PhotoHandler: param.PhotoHandler, + PostHandler: param.PostHandler, + PostCommentHandler: param.PostCommentHandler, + SheetHandler: param.SheetHandler, + SheetCommentHandler: param.SheetCommentHandler, + StatisticHandler: param.StatisticHandler, + TagHandler: param.TagHandler, + ThemeHandler: param.ThemeHandler, + UserHandler: param.UserHandler, + EmailHandler: param.EmailHandler, + OptionService: param.OptionService, + ThemeService: param.ThemeService, + SheetService: param.SheetService, + IndexHandler: param.IndexHandler, + FeedHandler: param.FeedHandler, + ArchiveHandler: param.ArchiveHandler, + ViewHandler: param.ViewHandler, + ContentCategoryHandler: param.ContentCategoryHandler, + ContentSheetHandler: param.ContentSheetHandler, + ContentTagHandler: param.ContentTagHandler, + ContentLinkHandler: param.ContentLinkHandler, + ContentPhotoHandler: param.ContentPhotoHandler, + ContentJournalHandler: param.ContentJournalHandler, + ContentAPIArchiveHandler: param.ContentAPIArchiveHandler, + ContentAPICategoryHandler: param.ContentAPICategoryHandler, + ContentAPIJournalHandler: param.ContentAPIJournalHandler, + ContentAPILinkHandler: param.ContentAPILinkHandler, + ContentAPIPostHandler: param.ContentAPIPostHandler, + ContentAPISheetHandler: param.ContentAPISheetHandler, + ContentAPIOptionHandler: param.ContentAPIOptionHandler, + ContentSearchHandler: param.ContentSearchHandler, + ContentAPIPhotoHandler: param.ContentAPIPhotoHandler, + ApplicationPasswordHandler: param.ApplicationPasswordHandler, + WpPostHandler: param.WpPostHandler, + WpUserHandler: param.WpUserHandler, + WpCategoryHandler: param.WpCategoryHandler, } lifecycle.Append(fx.Hook{ OnStop: httpServer.Shutdown, diff --git a/main.go b/main.go index 66ae8a33..ca526cba 100644 --- a/main.go +++ b/main.go @@ -47,6 +47,7 @@ func InitApp() *fx.App { middleware.NewGinLoggerMiddleware, middleware.NewRecoveryMiddleware, middleware.NewInstallRedirectMiddleware, + middleware.NewApplicationPasswordMiddleware, ), fx.Populate(&dal.DB), fx.Populate(&eventBus), diff --git a/model/dto/wp/category.go b/model/dto/wp/category.go new file mode 100644 index 00000000..1fdfd59d --- /dev/null +++ b/model/dto/wp/category.go @@ -0,0 +1,13 @@ +package wp + +type CategoryDTO struct { + ID int32 `json:"id"` + Count int32 `json:"count"` + Description string `json:"description"` + Link string `json:"link"` + Name string `json:"name"` + Slug string `json:"slug"` + Taxonomy string `json:"taxonomy"` + Parent int32 `json:"parent"` + Meta map[string]interface{} `json:"meta"` +} diff --git a/model/dto/wp/post.go b/model/dto/wp/post.go new file mode 100644 index 00000000..9f32113e --- /dev/null +++ b/model/dto/wp/post.go @@ -0,0 +1,30 @@ +package wp + +type PostDTO struct { + Date string `json:"date"` + DateGmt string `json:"date_gmt"` + Guid map[string]interface{} `json:"guid"` + Id int32 `json:"id"` + Link string `json:"link"` + Modified string `json:"modified"` + ModifiedGmt string `json:"modified_gmt"` + Slug string `json:"slug"` + Status string `json:"status"` + Type string `json:"type"` + Password string `json:"password"` + PermalinkTemplate string `json:"permalink_template"` + GeneratedSlug string `json:"generated_slug"` + Title string `json:"title"` + Content map[string]interface{} `json:"content"` + Author int32 `json:"author"` + Excerpt map[string]interface{} `json:"excerpt"` + FeaturedMedia int32 `json:"featured_media"` + CommentStatus string `json:"comment_status"` + PingStatus string `json:"ping_status"` + Format string `json:"format"` + Meta map[string]interface{} `json:"meta"` + Sticky bool `json:"sticky"` + Template string `json:"template"` + Categories []int32 `json:"categories"` + Tags []int32 `json:"tags"` +} diff --git a/model/dto/wp/user.go b/model/dto/wp/user.go new file mode 100644 index 00000000..ac211bd6 --- /dev/null +++ b/model/dto/wp/user.go @@ -0,0 +1,4 @@ +package wp + +type UserDTO struct { +} diff --git a/model/param/wp_post.go b/model/param/wp_post.go new file mode 100644 index 00000000..3eaaeb88 --- /dev/null +++ b/model/param/wp_post.go @@ -0,0 +1,30 @@ +package param + +type WpPost struct { + Date string `json:"date"` + DateGmt string `json:"date_gmt"` + Guid map[string]interface{} `json:"guid"` + Id int32 `json:"id"` + Link string `json:"link"` + Modified string `json:"modified"` + ModifiedGmt string `json:"modified_gmt"` + Slug string `json:"slug"` + Status string `json:"status"` + Type string `json:"type"` + Password string `json:"password"` + PermalinkTemplate string `json:"permalink_template"` + GeneratedSlug string `json:"generated_slug"` + Title string `json:"title"` + Content string `json:"content"` + Author int32 `json:"author"` + Excerpt map[string]interface{} `json:"excerpt"` + FeaturedMedia int32 `json:"featured_media"` + CommentStatus string `json:"comment_status"` + PingStatus string `json:"ping_status"` + Format string `json:"format"` + Meta map[string]interface{} `json:"meta"` + Sticky bool `json:"sticky"` + Template string `json:"template"` + Categories []int32 `json:"categories"` + Tags []int32 `json:"tags"` +} diff --git a/service/application_password.go b/service/application_password.go index 152d9af7..454b1b96 100644 --- a/service/application_password.go +++ b/service/application_password.go @@ -3,6 +3,7 @@ package service import ( "context" "github.com/go-sonic/sonic/model/dto" + "github.com/go-sonic/sonic/model/entity" "github.com/go-sonic/sonic/model/param" ) @@ -10,4 +11,6 @@ type ApplicationPasswordService interface { CreatePwd(ctx context.Context, appPwdParam *param.ApplicationPasswordParam) (*dto.ApplicationPasswordDTO, error) DeletePwd(ctx context.Context, appPwdParam *param.ApplicationPasswordParam) error List(ctx context.Context) ([]*dto.ApplicationPasswordDTO, error) + Verify(ctx context.Context, userId int32, pwd string) (*entity.ApplicationPassword, error) + Update(ctx context.Context, entityId int32, ip string) error } diff --git a/service/impl/application_password.go b/service/impl/application_password.go index 1d39f715..d5211ba3 100644 --- a/service/impl/application_password.go +++ b/service/impl/application_password.go @@ -117,18 +117,56 @@ func (a *applicationPasswordServiceImpl) List(ctx context.Context) ([]*dto.Appli appPwdDTOList := make([]*dto.ApplicationPasswordDTO, len(entities)) for _, appPwdEntity := range entities { - var lastActivateTime int64 - if appPwdEntity.LastActivateTime == nil { - lastActivateTime = appPwdEntity.LastActivateTime.Unix() - } - appPwdDTO := &dto.ApplicationPasswordDTO{ - Name: appPwdEntity.Name, - LastActivateIp: appPwdEntity.LastActivateIP, - LastActivateTime: lastActivateTime, - CreateTime: appPwdEntity.CreateTime.Unix(), - } - appPwdDTOList = append(appPwdDTOList, appPwdDTO) + appPwdDTOList = append(appPwdDTOList, a.ConvertToDTO(appPwdEntity)) } return appPwdDTOList, nil } + +func (a *applicationPasswordServiceImpl) Verify(ctx context.Context, userId int32, pwd string) (*entity.ApplicationPassword, error) { + appPwdDAL := dal.GetQueryByCtx(ctx).ApplicationPassword + entityList, err := appPwdDAL.WithContext(ctx).Where(appPwdDAL.UserID.Eq(userId)).Find() + if err != nil { + return nil, WrapDBErr(err) + } + + pwdMd5 := util.Md5(pwd) + + for _, appPwdEntity := range entityList { + if appPwdEntity.Password == pwdMd5 { + return appPwdEntity, nil + } + } + return nil, nil +} + +func (a *applicationPasswordServiceImpl) Update(ctx context.Context, entityId int32, ip string) error { + appPwdDAL := dal.GetQueryByCtx(ctx).ApplicationPassword + now := time.Now() + + _, err := appPwdDAL.WithContext(ctx).Where(appPwdDAL.ID.Eq(entityId)).Updates(entity.ApplicationPassword{ + LastActivateIP: ip, + LastActivateTime: &now, + }) + + if err != nil { + return WrapDBErr(err) + } + + return nil +} + +func (a *applicationPasswordServiceImpl) ConvertToDTO(appPwdEntity *entity.ApplicationPassword) *dto.ApplicationPasswordDTO { + var lastActivateTime int64 + if appPwdEntity.LastActivateTime == nil { + lastActivateTime = appPwdEntity.LastActivateTime.Unix() + } + appPwdDTO := &dto.ApplicationPasswordDTO{ + Name: appPwdEntity.Name, + LastActivateIp: appPwdEntity.LastActivateIP, + LastActivateTime: lastActivateTime, + CreateTime: appPwdEntity.CreateTime.Unix(), + } + + return appPwdDTO +} diff --git a/util/common.go b/util/common.go index 36a4a6f2..f77fbf0f 100644 --- a/util/common.go +++ b/util/common.go @@ -1,6 +1,10 @@ package util import ( + "errors" + "github.com/go-playground/validator/v10" + "github.com/go-sonic/sonic/handler/trans" + "github.com/go-sonic/sonic/util/xerr" "regexp" "strings" "unicode/utf8" @@ -45,3 +49,11 @@ func HTMLFormatWordCount(html string) int64 { text := CleanHTMLTag(html) return int64(utf8.RuneCountInString(text) - len(blankRegexp.FindSubmatchIndex(StringToBytes(text)))) } + +func WrapJsonBindErr(err error) error { + e := validator.ValidationErrors{} + if errors.As(err, &e) { + return xerr.WithStatus(e, xerr.StatusBadRequest).WithMsg(trans.Translate(e)) + } + return xerr.WithStatus(err, xerr.StatusBadRequest) +} From 013b939ac7fcd99007d79059c371bb8c374325db Mon Sep 17 00:00:00 2001 From: textworld Date: Wed, 22 Nov 2023 09:04:20 +0800 Subject: [PATCH 04/11] feat: add some api for wordpress --- consts/consts.go | 4 + handler/content/wp/category.go | 2 +- handler/content/wp/init.go | 1 + handler/content/wp/post.go | 132 +++++++++++++++++++++++++++------ handler/content/wp/tag.go | 53 +++++++++++++ handler/content/wp/user.go | 2 +- handler/router.go | 18 ++++- handler/server.go | 18 +++++ model/dto/base.go | 6 ++ model/dto/wp/tag.go | 12 +++ model/param/wp_post.go | 5 ++ model/param/wp_tag.go | 5 ++ service/impl/tag.go | 16 ++++ service/tag.go | 1 + 14 files changed, 249 insertions(+), 26 deletions(-) create mode 100644 handler/content/wp/tag.go create mode 100644 model/dto/wp/tag.go create mode 100644 model/param/wp_tag.go diff --git a/consts/consts.go b/consts/consts.go index 58fd491e..9f503f06 100644 --- a/consts/consts.go +++ b/consts/consts.go @@ -46,3 +46,7 @@ var ( BuildTime string BuildCommit string ) + +const ( + LocalDateTimeFormat = "2006-01-02T15:04:05" +) diff --git a/handler/content/wp/category.go b/handler/content/wp/category.go index 5bb5b188..99561f56 100644 --- a/handler/content/wp/category.go +++ b/handler/content/wp/category.go @@ -27,7 +27,7 @@ func (c *CategoryHandler) List(ctx *gin.Context) (interface{}, error) { return nil, err } - categoryDTOList := make([]*wp.CategoryDTO, len(categoryEntities)) + categoryDTOList := make([]*wp.CategoryDTO, 0, len(categoryEntities)) for _, categoryEntity := range categoryEntities { categoryDTOList = append(categoryDTOList, convertToCategoryDTO(categoryEntity)) } diff --git a/handler/content/wp/init.go b/handler/content/wp/init.go index dabe91ef..93096671 100644 --- a/handler/content/wp/init.go +++ b/handler/content/wp/init.go @@ -7,5 +7,6 @@ func init() { NewPostHandler, NewUserHandler, NewCategoryHandler, + NewTagHandler, ) } diff --git a/handler/content/wp/post.go b/handler/content/wp/post.go index 586d5065..ac739833 100644 --- a/handler/content/wp/post.go +++ b/handler/content/wp/post.go @@ -3,13 +3,15 @@ package wp import ( "encoding/json" "github.com/gin-gonic/gin" - "github.com/go-sonic/sonic/consts" + sonicconst "github.com/go-sonic/sonic/consts" "github.com/go-sonic/sonic/log" "github.com/go-sonic/sonic/model/dto/wp" "github.com/go-sonic/sonic/model/entity" "github.com/go-sonic/sonic/model/param" "github.com/go-sonic/sonic/service" "github.com/go-sonic/sonic/util" + "github.com/go-sonic/sonic/util/xerr" + "strconv" "strings" "time" ) @@ -24,50 +26,133 @@ func NewPostHandler(postService service.PostService) *PostHandler { } } +func (handler *PostHandler) List(ctx *gin.Context) (interface{}, error) { + var wpPostQuery param.WpPostQuery + if err := ctx.ShouldBind(&wpPostQuery); err != nil { + return nil, util.WrapJsonBindErr(err) + } + + var postQuery param.PostQuery + postQuery.PageSize = wpPostQuery.Page + postQuery.PageNum = wpPostQuery.PerPage + entities, _, err := handler.PostService.Page(ctx, postQuery) + if err != nil { + return nil, err + } + + wpPostList := make([]*wp.PostDTO, 0, len(entities)) + for _, postEntity := range entities { + wpPostList = append(wpPostList, convertToWpPost(postEntity)) + } + + return wpPostList, nil +} + func (handler *PostHandler) Create(ctx *gin.Context) (interface{}, error) { - var wpPost param.WpPost - err := ctx.ShouldBindJSON(&wpPost) + postParam, err := parsePostParam(ctx) if err != nil { return nil, util.WrapJsonBindErr(err) } - bytes, err := json.Marshal(wpPost) + create, err := handler.PostService.Create(ctx, postParam) if err != nil { return nil, err } - log.CtxInfo(ctx, "wpPost: "+string(bytes)) + return convertToWpPost(create), nil +} - postParam := convertToPostParam(&wpPost) +func (handler *PostHandler) Update(ctx *gin.Context) (interface{}, error) { + postParam, err := parsePostParam(ctx) + if err != nil { + return nil, util.WrapJsonBindErr(err) + } - create, err := handler.PostService.Create(ctx, postParam) + postIDStr := ctx.Param("postID") + postID, err := strconv.ParseInt(postIDStr, 10, 32) + if err != nil { + return nil, xerr.WithStatus(err, xerr.StatusBadRequest).WithMsg("Parameter error") + } + + postDetail, err := handler.PostService.Update(ctx, int32(postID), postParam) if err != nil { return nil, err } - return convertToWpPost(create), nil + return convertToWpPost(postDetail), nil +} + +func (handler *PostHandler) Delete(ctx *gin.Context) (interface{}, error) { + postIDStr := ctx.Param("postID") + postID, err := strconv.ParseInt(postIDStr, 10, 32) + if err != nil { + return nil, xerr.WithStatus(err, xerr.StatusBadRequest).WithMsg("Parameter error") + } + + postEntity, err := handler.PostService.GetByPostID(ctx, int32(postID)) + if err != nil { + return nil, err + } + + if err = handler.PostService.Delete(ctx, int32(postID)); err != nil { + return nil, err + } + + return convertToWpPost(postEntity), nil +} + +func parsePostParam(ctx *gin.Context) (*param.Post, error) { + var wpPost param.WpPost + err := ctx.ShouldBindJSON(&wpPost) + if err != nil { + return nil, util.WrapJsonBindErr(err) + } + + bytes, err := json.Marshal(wpPost) + if err != nil { + return nil, err + } + log.CtxInfo(ctx, "wpPost: "+string(bytes)) + + return convertToPostParam(&wpPost) + } -func convertToPostParam(wpPost *param.WpPost) *param.Post { - var paramPostStatus = consts.PostStatusPublished - if strings.ToLower(wpPost.Content) == "draft" { - paramPostStatus = consts.PostStatusDraft +func convertToPostParam(wpPost *param.WpPost) (*param.Post, error) { + var paramPostStatus = sonicconst.PostStatusPublished + if strings.ToLower(wpPost.Status) == "draft" { + paramPostStatus = sonicconst.PostStatusDraft } - createTime := time.Now().Unix() + editorType := sonicconst.EditorTypeRichText + disallowComment := false + if strings.ToLower(wpPost.CommentStatus) == "closed" { + disallowComment = true + } + + var createTime *int64 + + if strings.TrimSpace(wpPost.Date) != "" { + datetime, err := time.Parse(sonicconst.LocalDateTimeFormat, wpPost.Date) + if err != nil { + return nil, err + } + dateTimeMills := datetime.UnixMilli() + createTime = &dateTimeMills + } return ¶m.Post{ Title: wpPost.Title, Status: paramPostStatus, Slug: wpPost.Slug, - EditorType: nil, + EditorType: &editorType, OriginalContent: wpPost.Content, Summary: "", Thumbnail: "", - DisallowComment: false, + DisallowComment: disallowComment, Password: wpPost.Password, Template: "", TopPriority: 0, - CreateTime: &createTime, + CreateTime: createTime, MetaKeywords: "", MetaDescription: "", TagIDs: make([]int32, 0), @@ -76,7 +161,7 @@ func convertToPostParam(wpPost *param.WpPost) *param.Post { Content: wpPost.Content, EditTime: nil, UpdateTime: nil, - } + }, nil } func convertToWpPost(postEntity *entity.Post) *wp.PostDTO { @@ -85,7 +170,7 @@ func convertToWpPost(postEntity *entity.Post) *wp.PostDTO { var wpCommentStatus = "open" var wpContent = make(map[string]interface{}) - if postEntity.Status == consts.PostStatusDraft { + if postEntity.Status == sonicconst.PostStatusDraft { wpStatus = "draft" } @@ -102,15 +187,15 @@ func convertToWpPost(postEntity *entity.Post) *wp.PostDTO { Guid: nil, Id: postEntity.ID, Link: "", - Modified: postEntity.UpdateTime.Format(timeFormat), - ModifiedGmt: postEntity.UpdateTime.UTC().Format(timeFormat), + Modified: "", + ModifiedGmt: "", Slug: "", Status: wpStatus, Type: "post", Password: "standard", PermalinkTemplate: "", GeneratedSlug: "", - Title: "", + Title: postEntity.Title, Content: wpContent, Author: 0, Excerpt: nil, @@ -124,5 +209,10 @@ func convertToWpPost(postEntity *entity.Post) *wp.PostDTO { Categories: make([]int32, 0), Tags: make([]int32, 0), } + + if postEntity.UpdateTime != nil { + postDTO.Modified = postEntity.UpdateTime.Format(timeFormat) + postDTO.ModifiedGmt = postEntity.UpdateTime.UTC().Format(timeFormat) + } return postDTO } diff --git a/handler/content/wp/tag.go b/handler/content/wp/tag.go new file mode 100644 index 00000000..8bcd1662 --- /dev/null +++ b/handler/content/wp/tag.go @@ -0,0 +1,53 @@ +package wp + +import ( + "github.com/gin-gonic/gin" + "github.com/go-sonic/sonic/model/dto/wp" + "github.com/go-sonic/sonic/model/entity" + "github.com/go-sonic/sonic/model/param" + "github.com/go-sonic/sonic/service" +) + +type TagHandler struct { + TagService service.TagService +} + +func NewTagHandler(tagService service.TagService) *TagHandler { + return &TagHandler{ + TagService: tagService, + } +} + +func (handler *TagHandler) List(ctx *gin.Context) (interface{}, error) { + var err error + var listParam param.TagListParam + if err = ctx.ShouldBindJSON(&listParam); err != nil { + return nil, err + } + + entities, err := handler.TagService.ListByOption(ctx, &listParam) + if err != nil { + return nil, err + } + + tagDTOList := make([]*wp.TagDTO, 0, len(entities)) + for _, tagEntity := range entities { + tagDTOList = append(tagDTOList, convertToWpTag(tagEntity)) + } + + return tagDTOList, nil +} + +func convertToWpTag(tagEntity *entity.Tag) *wp.TagDTO { + tagDTO := &wp.TagDTO{ + ID: tagEntity.ID, + Count: 0, + Description: "", + Link: "", + Name: tagEntity.Name, + Slug: tagEntity.Slug, + Taxonomy: "", + Meta: nil, + } + return tagDTO +} diff --git a/handler/content/wp/user.go b/handler/content/wp/user.go index 13202433..07cdb08a 100644 --- a/handler/content/wp/user.go +++ b/handler/content/wp/user.go @@ -22,7 +22,7 @@ func (u *UserHandler) List(ctx *gin.Context) (interface{}, error) { return nil, err } - userDTOList := make([]*dto.User, len(allUser)) + userDTOList := make([]*dto.User, 0, len(allUser)) for _, user := range allUser { userDTO := u.UserService.ConvertToDTO(ctx, user) userDTOList = append(userDTOList, userDTO) diff --git a/handler/router.go b/handler/router.go index 6484ed95..ab361229 100644 --- a/handler/router.go +++ b/handler/router.go @@ -47,9 +47,21 @@ func (s *Server) RegisterRouters() { { wpCompatibleRouter := router.Group("/wp-json/wp/v2") wpCompatibleRouter.Use(s.ApplicationPasswordMiddleware.GetWrapHandler()) - wpCompatibleRouter.POST("/posts", s.wrapHandler(s.WpPostHandler.Create)) - wpCompatibleRouter.GET("/users", s.wrapHandler(s.WpUserHandler.List)) - wpCompatibleRouter.GET("/categories", s.wrapHandler(s.WpCategoryHandler.List)) + { + wpCompatibleRouter.GET("/posts", s.wrapWpHandler(s.WpPostHandler.List)) + wpCompatibleRouter.POST("/posts", s.wrapWpHandler(s.WpPostHandler.Create)) + wpCompatibleRouter.POST("/posts/:postID", s.wrapWpHandler(s.WpPostHandler.Update)) + wpCompatibleRouter.DELETE("/posts/:postID", s.wrapWpHandler(s.WpPostHandler.Delete)) + } + { + wpCompatibleRouter.GET("/tags", s.wrapWpHandler(s.WpTagHandler.List)) + } + { + wpCompatibleRouter.GET("/users", s.wrapWpHandler(s.WpUserHandler.List)) + } + { + wpCompatibleRouter.GET("/categories", s.wrapWpHandler(s.WpCategoryHandler.List)) + } } { adminAPIRouter := router.Group("/api/admin") diff --git a/handler/server.go b/handler/server.go index c25ac4e0..908d4a38 100644 --- a/handler/server.go +++ b/handler/server.go @@ -83,6 +83,7 @@ type Server struct { WpPostHandler *wp.PostHandler WpUserHandler *wp.UserHandler WpCategoryHandler *wp.CategoryHandler + WpTagHandler *wp.TagHandler } type ServerParams struct { @@ -143,6 +144,7 @@ type ServerParams struct { WpPostHandler *wp.PostHandler WpUserHandler *wp.UserHandler WpCategoryHandler *wp.CategoryHandler + WpTagHandler *wp.TagHandler } func NewServer(param ServerParams, lifecycle fx.Lifecycle) *Server { @@ -213,6 +215,7 @@ func NewServer(param ServerParams, lifecycle fx.Lifecycle) *Server { WpPostHandler: param.WpPostHandler, WpUserHandler: param.WpUserHandler, WpCategoryHandler: param.WpCategoryHandler, + WpTagHandler: param.WpTagHandler, } lifecycle.Append(fx.Hook{ OnStop: httpServer.Shutdown, @@ -256,6 +259,21 @@ func (s *Server) wrapHandler(handler wrapperHandler) gin.HandlerFunc { } } +func (s *Server) wrapWpHandler(handler wrapperHandler) gin.HandlerFunc { + return func(ctx *gin.Context) { + data, err := handler(ctx) + if err != nil { + s.logger.Error("handler error", zap.Error(err)) + status := xerr.GetHTTPStatus(err) + ctx.JSON(status, &dto.BaseWpDTO{Code: status, Message: xerr.GetMessage(err), Data: map[string]interface{}{"status": status}}) + return + } + + ctx.JSON(http.StatusOK, data) + return + } +} + type wrapperHTMLHandler func(ctx *gin.Context, model template.Model) (templateName string, err error) var ( diff --git a/model/dto/base.go b/model/dto/base.go index 7b72b15a..8b61c722 100644 --- a/model/dto/base.go +++ b/model/dto/base.go @@ -14,6 +14,12 @@ type BaseDTO struct { Data interface{} `json:"data"` } +type BaseWpDTO struct { + Code int `json:"code"` + Message string `json:"message"` + Data interface{} `json:"data"` +} + type Page struct { Content interface{} `json:"content"` Pages int `json:"pages"` diff --git a/model/dto/wp/tag.go b/model/dto/wp/tag.go new file mode 100644 index 00000000..d11c7ca7 --- /dev/null +++ b/model/dto/wp/tag.go @@ -0,0 +1,12 @@ +package wp + +type TagDTO struct { + ID int32 `json:"id"` + Count int32 `json:"count"` + Description string `json:"description"` + Link string `json:"link"` + Name string `json:"name"` + Slug string `json:"slug"` + Taxonomy string `json:"taxonomy"` + Meta map[string]interface{} `json:"meta"` +} diff --git a/model/param/wp_post.go b/model/param/wp_post.go index 3eaaeb88..05f4aacf 100644 --- a/model/param/wp_post.go +++ b/model/param/wp_post.go @@ -28,3 +28,8 @@ type WpPost struct { Categories []int32 `json:"categories"` Tags []int32 `json:"tags"` } + +type WpPostQuery struct { + Page int `form:"page" default:"1"` + PerPage int `form:"per_page" default:"10"` +} diff --git a/model/param/wp_tag.go b/model/param/wp_tag.go new file mode 100644 index 00000000..dacefe87 --- /dev/null +++ b/model/param/wp_tag.go @@ -0,0 +1,5 @@ +package param + +type TagListParam struct { + Search string `json:"search"` +} diff --git a/service/impl/tag.go b/service/impl/tag.go index 9b349d15..0bcf4ad3 100644 --- a/service/impl/tag.go +++ b/service/impl/tag.go @@ -230,3 +230,19 @@ func (t tagServiceImpl) GetByName(ctx context.Context, name string) (*entity.Tag tag, err := tagDAL.WithContext(ctx).Where(tagDAL.Name.Eq(name)).First() return tag, WrapDBErr(err) } + +func (t tagServiceImpl) ListByOption(ctx context.Context, option *param.TagListParam) ([]*entity.Tag, error) { + tagDAL := dal.GetQueryByCtx(ctx).Tag + if option == nil { + return tagDAL.WithContext(ctx).Where().Find() + } + + query := tagDAL.WithContext(ctx) + + search := strings.TrimSpace(option.Search) + if search != "" { + query.Where(tagDAL.Name.Like("%" + search + "%")) + } + + return query.Find() +} diff --git a/service/tag.go b/service/tag.go index 0a2800bf..299c6dd7 100644 --- a/service/tag.go +++ b/service/tag.go @@ -20,4 +20,5 @@ type TagService interface { Update(ctx context.Context, id int32, tagParam *param.Tag) (*entity.Tag, error) Delete(ctx context.Context, id int32) error CountAllTag(ctx context.Context) (int64, error) + ListByOption(ctx context.Context, option *param.TagListParam) ([]*entity.Tag, error) } From 8cdafbcd4d0ef05fb8cecaa38306bc50c3a6f584 Mon Sep 17 00:00:00 2001 From: textworld Date: Thu, 23 Nov 2023 08:34:23 +0800 Subject: [PATCH 05/11] feat: add manage apis for ApplicationPassword --- handler/content/wp/tag.go | 38 +++++++++++++++++++++++++++++++++++++- handler/router.go | 1 + model/param/wp_tag.go | 9 ++++++++- service/impl/tag.go | 2 +- 4 files changed, 47 insertions(+), 3 deletions(-) diff --git a/handler/content/wp/tag.go b/handler/content/wp/tag.go index 8bcd1662..3de6fd58 100644 --- a/handler/content/wp/tag.go +++ b/handler/content/wp/tag.go @@ -6,6 +6,8 @@ import ( "github.com/go-sonic/sonic/model/entity" "github.com/go-sonic/sonic/model/param" "github.com/go-sonic/sonic/service" + "github.com/go-sonic/sonic/util/xerr" + "strings" ) type TagHandler struct { @@ -21,7 +23,7 @@ func NewTagHandler(tagService service.TagService) *TagHandler { func (handler *TagHandler) List(ctx *gin.Context) (interface{}, error) { var err error var listParam param.TagListParam - if err = ctx.ShouldBindJSON(&listParam); err != nil { + if err = ctx.ShouldBind(&listParam); err != nil { return nil, err } @@ -38,6 +40,40 @@ func (handler *TagHandler) List(ctx *gin.Context) (interface{}, error) { return tagDTOList, nil } +func (handler *TagHandler) Create(ctx *gin.Context) (interface{}, error) { + var err error + var createParam param.TagCreateParam + if err = ctx.ShouldBindJSON(&createParam); err != nil { + return nil, err + } + + createParam.Name = strings.TrimSpace(createParam.Name) + if createParam.Name == "" { + return nil, xerr.WithStatus(err, xerr.StatusBadRequest).WithMsg("blank name") + } + + tagEntity, err := handler.TagService.GetByName(ctx, createParam.Name) + if err != nil { + return nil, err + } + + if tagEntity != nil { + return nil, xerr.WithStatus(err, xerr.StatusBadRequest).WithMsg("tag exists") + } + + tagParam := ¶m.Tag{ + Name: createParam.Name, + Slug: createParam.Slug, + Thumbnail: "", + Color: "", + } + create, err := handler.TagService.Create(ctx, tagParam) + if err != nil { + return nil, err + } + return convertToWpTag(create), nil +} + func convertToWpTag(tagEntity *entity.Tag) *wp.TagDTO { tagDTO := &wp.TagDTO{ ID: tagEntity.ID, diff --git a/handler/router.go b/handler/router.go index ab361229..6bdbd134 100644 --- a/handler/router.go +++ b/handler/router.go @@ -55,6 +55,7 @@ func (s *Server) RegisterRouters() { } { wpCompatibleRouter.GET("/tags", s.wrapWpHandler(s.WpTagHandler.List)) + wpCompatibleRouter.POST("/tags", s.wrapWpHandler(s.WpTagHandler.Create)) } { wpCompatibleRouter.GET("/users", s.wrapWpHandler(s.WpUserHandler.List)) diff --git a/model/param/wp_tag.go b/model/param/wp_tag.go index dacefe87..4672a11c 100644 --- a/model/param/wp_tag.go +++ b/model/param/wp_tag.go @@ -1,5 +1,12 @@ package param type TagListParam struct { - Search string `json:"search"` + Search string `form:"search" json:"search"` +} + +type TagCreateParam struct { + Description string `json:"description"` + Name string `json:"name"` + Slug string `json:"slug"` + Meta map[string]interface{} `json:"meta"` } diff --git a/service/impl/tag.go b/service/impl/tag.go index 0bcf4ad3..726ee750 100644 --- a/service/impl/tag.go +++ b/service/impl/tag.go @@ -241,7 +241,7 @@ func (t tagServiceImpl) ListByOption(ctx context.Context, option *param.TagListP search := strings.TrimSpace(option.Search) if search != "" { - query.Where(tagDAL.Name.Like("%" + search + "%")) + query = query.Where(tagDAL.Name.Like("%" + search + "%")) } return query.Find() From f0c92e1d01463037671db88479759e908191b9e9 Mon Sep 17 00:00:00 2001 From: textworld Date: Thu, 23 Nov 2023 08:38:41 +0800 Subject: [PATCH 06/11] =?UTF-8?q?refactor=C3=AF:=20remove=20unused=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- model/dto/wp/user.go | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 model/dto/wp/user.go diff --git a/model/dto/wp/user.go b/model/dto/wp/user.go deleted file mode 100644 index ac211bd6..00000000 --- a/model/dto/wp/user.go +++ /dev/null @@ -1,4 +0,0 @@ -package wp - -type UserDTO struct { -} From 6d3f54ecee8c0e45dc5fb45f6561b1f0cad1ee4a Mon Sep 17 00:00:00 2001 From: textworld Date: Thu, 30 Nov 2023 22:02:37 +0800 Subject: [PATCH 07/11] style: golint --- handler/content/wp/post.go | 4 ++-- handler/middleware/application_password.go | 8 +++++--- handler/router.go | 1 - handler/server.go | 1 - model/dto/application_password.go | 2 +- model/dto/wp/post.go | 4 ++-- model/param/wp_post.go | 4 ++-- service/application_password.go | 4 ++-- service/impl/application_password.go | 10 +++++----- 9 files changed, 19 insertions(+), 19 deletions(-) diff --git a/handler/content/wp/post.go b/handler/content/wp/post.go index ac739833..814b2e4e 100644 --- a/handler/content/wp/post.go +++ b/handler/content/wp/post.go @@ -184,8 +184,8 @@ func convertToWpPost(postEntity *entity.Post) *wp.PostDTO { var postDTO = &wp.PostDTO{ Date: postEntity.CreateTime.Format(timeFormat), DateGmt: postEntity.CreateTime.UTC().Format(timeFormat), - Guid: nil, - Id: postEntity.ID, + GUID: nil, + ID: postEntity.ID, Link: "", Modified: "", ModifiedGmt: "", diff --git a/handler/middleware/application_password.go b/handler/middleware/application_password.go index 046ec53e..66507af1 100644 --- a/handler/middleware/application_password.go +++ b/handler/middleware/application_password.go @@ -12,6 +12,10 @@ import ( "strings" ) +var ( + basicAuthRegexp = regexp.MustCompile(`^Basic [a-z\\d/+]*={0,2}`) +) + type ApplicationPasswordMiddleware struct { PasswordService service.ApplicationPasswordService UserService service.UserService @@ -30,7 +34,6 @@ func (a *ApplicationPasswordMiddleware) Get() error { } func (a *ApplicationPasswordMiddleware) GetWrapHandler() gin.HandlerFunc { - return func(ctx *gin.Context) { header := ctx.GetHeader("Authorization") if len(header) == 0 { @@ -91,6 +94,5 @@ func abortUnauthorized(ctx *gin.Context) { } func verifyHeader(header string) bool { - compile, err := regexp.Compile("^Basic [a-z\\d/+]*={0,2}") - return err != nil || compile.MatchString(header) + return basicAuthRegexp.MatchString(header) } diff --git a/handler/router.go b/handler/router.go index 6bdbd134..5086b95a 100644 --- a/handler/router.go +++ b/handler/router.go @@ -373,7 +373,6 @@ func (s *Server) RegisterRouters() { contentAPIRouter.GET("/options/comment", s.wrapHandler(s.ContentAPIOptionHandler.Comment)) } - } } diff --git a/handler/server.go b/handler/server.go index 908d4a38..09f2abfb 100644 --- a/handler/server.go +++ b/handler/server.go @@ -270,7 +270,6 @@ func (s *Server) wrapWpHandler(handler wrapperHandler) gin.HandlerFunc { } ctx.JSON(http.StatusOK, data) - return } } diff --git a/model/dto/application_password.go b/model/dto/application_password.go index f737b451..fd9ffba2 100644 --- a/model/dto/application_password.go +++ b/model/dto/application_password.go @@ -4,6 +4,6 @@ type ApplicationPasswordDTO struct { Name string `json:"name"` Password string `json:"password"` LastActivateTime int64 `json:"last_activate_time"` - LastActivateIp string `json:"last_activate_ip"` + LastActiveIP string `json:"last_activate_ip"` CreateTime int64 `json:"create_time"` } diff --git a/model/dto/wp/post.go b/model/dto/wp/post.go index 9f32113e..c06fae12 100644 --- a/model/dto/wp/post.go +++ b/model/dto/wp/post.go @@ -3,8 +3,8 @@ package wp type PostDTO struct { Date string `json:"date"` DateGmt string `json:"date_gmt"` - Guid map[string]interface{} `json:"guid"` - Id int32 `json:"id"` + GUID map[string]interface{} `json:"guid"` + ID int32 `json:"id"` Link string `json:"link"` Modified string `json:"modified"` ModifiedGmt string `json:"modified_gmt"` diff --git a/model/param/wp_post.go b/model/param/wp_post.go index 05f4aacf..7ec01fe5 100644 --- a/model/param/wp_post.go +++ b/model/param/wp_post.go @@ -3,8 +3,8 @@ package param type WpPost struct { Date string `json:"date"` DateGmt string `json:"date_gmt"` - Guid map[string]interface{} `json:"guid"` - Id int32 `json:"id"` + GUID map[string]interface{} `json:"guid"` + ID int32 `json:"id"` Link string `json:"link"` Modified string `json:"modified"` ModifiedGmt string `json:"modified_gmt"` diff --git a/service/application_password.go b/service/application_password.go index 454b1b96..57346043 100644 --- a/service/application_password.go +++ b/service/application_password.go @@ -11,6 +11,6 @@ type ApplicationPasswordService interface { CreatePwd(ctx context.Context, appPwdParam *param.ApplicationPasswordParam) (*dto.ApplicationPasswordDTO, error) DeletePwd(ctx context.Context, appPwdParam *param.ApplicationPasswordParam) error List(ctx context.Context) ([]*dto.ApplicationPasswordDTO, error) - Verify(ctx context.Context, userId int32, pwd string) (*entity.ApplicationPassword, error) - Update(ctx context.Context, entityId int32, ip string) error + Verify(ctx context.Context, userID int32, pwd string) (*entity.ApplicationPassword, error) + Update(ctx context.Context, entityID int32, ip string) error } diff --git a/service/impl/application_password.go b/service/impl/application_password.go index d5211ba3..ca2c9ab7 100644 --- a/service/impl/application_password.go +++ b/service/impl/application_password.go @@ -123,9 +123,9 @@ func (a *applicationPasswordServiceImpl) List(ctx context.Context) ([]*dto.Appli return appPwdDTOList, nil } -func (a *applicationPasswordServiceImpl) Verify(ctx context.Context, userId int32, pwd string) (*entity.ApplicationPassword, error) { +func (a *applicationPasswordServiceImpl) Verify(ctx context.Context, userID int32, pwd string) (*entity.ApplicationPassword, error) { appPwdDAL := dal.GetQueryByCtx(ctx).ApplicationPassword - entityList, err := appPwdDAL.WithContext(ctx).Where(appPwdDAL.UserID.Eq(userId)).Find() + entityList, err := appPwdDAL.WithContext(ctx).Where(appPwdDAL.UserID.Eq(userID)).Find() if err != nil { return nil, WrapDBErr(err) } @@ -140,11 +140,11 @@ func (a *applicationPasswordServiceImpl) Verify(ctx context.Context, userId int3 return nil, nil } -func (a *applicationPasswordServiceImpl) Update(ctx context.Context, entityId int32, ip string) error { +func (a *applicationPasswordServiceImpl) Update(ctx context.Context, entityID int32, ip string) error { appPwdDAL := dal.GetQueryByCtx(ctx).ApplicationPassword now := time.Now() - _, err := appPwdDAL.WithContext(ctx).Where(appPwdDAL.ID.Eq(entityId)).Updates(entity.ApplicationPassword{ + _, err := appPwdDAL.WithContext(ctx).Where(appPwdDAL.ID.Eq(entityID)).Updates(entity.ApplicationPassword{ LastActivateIP: ip, LastActivateTime: &now, }) @@ -163,7 +163,7 @@ func (a *applicationPasswordServiceImpl) ConvertToDTO(appPwdEntity *entity.Appli } appPwdDTO := &dto.ApplicationPasswordDTO{ Name: appPwdEntity.Name, - LastActivateIp: appPwdEntity.LastActivateIP, + LastActiveIP: appPwdEntity.LastActivateIP, LastActivateTime: lastActivateTime, CreateTime: appPwdEntity.CreateTime.Unix(), } From 8e6de2e20fd2f3696d9215cd5f3a6615635f269d Mon Sep 17 00:00:00 2001 From: textworld Date: Thu, 30 Nov 2023 22:28:30 +0800 Subject: [PATCH 08/11] style: use goimport --- handler/content/wp/post.go | 15 ++++++++------- service/impl/application_password.go | 5 +++-- util/common.go | 9 +++++---- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/handler/content/wp/post.go b/handler/content/wp/post.go index 814b2e4e..ee0e3279 100644 --- a/handler/content/wp/post.go +++ b/handler/content/wp/post.go @@ -2,6 +2,10 @@ package wp import ( "encoding/json" + "strconv" + "strings" + "time" + "github.com/gin-gonic/gin" sonicconst "github.com/go-sonic/sonic/consts" "github.com/go-sonic/sonic/log" @@ -11,9 +15,6 @@ import ( "github.com/go-sonic/sonic/service" "github.com/go-sonic/sonic/util" "github.com/go-sonic/sonic/util/xerr" - "strconv" - "strings" - "time" ) type PostHandler struct { @@ -29,7 +30,7 @@ func NewPostHandler(postService service.PostService) *PostHandler { func (handler *PostHandler) List(ctx *gin.Context) (interface{}, error) { var wpPostQuery param.WpPostQuery if err := ctx.ShouldBind(&wpPostQuery); err != nil { - return nil, util.WrapJsonBindErr(err) + return nil, util.WrapJSONBindErr(err) } var postQuery param.PostQuery @@ -51,7 +52,7 @@ func (handler *PostHandler) List(ctx *gin.Context) (interface{}, error) { func (handler *PostHandler) Create(ctx *gin.Context) (interface{}, error) { postParam, err := parsePostParam(ctx) if err != nil { - return nil, util.WrapJsonBindErr(err) + return nil, util.WrapJSONBindErr(err) } create, err := handler.PostService.Create(ctx, postParam) @@ -64,7 +65,7 @@ func (handler *PostHandler) Create(ctx *gin.Context) (interface{}, error) { func (handler *PostHandler) Update(ctx *gin.Context) (interface{}, error) { postParam, err := parsePostParam(ctx) if err != nil { - return nil, util.WrapJsonBindErr(err) + return nil, util.WrapJSONBindErr(err) } postIDStr := ctx.Param("postID") @@ -104,7 +105,7 @@ func parsePostParam(ctx *gin.Context) (*param.Post, error) { var wpPost param.WpPost err := ctx.ShouldBindJSON(&wpPost) if err != nil { - return nil, util.WrapJsonBindErr(err) + return nil, util.WrapJSONBindErr(err) } bytes, err := json.Marshal(wpPost) diff --git a/service/impl/application_password.go b/service/impl/application_password.go index ca2c9ab7..0d206c1b 100644 --- a/service/impl/application_password.go +++ b/service/impl/application_password.go @@ -2,12 +2,13 @@ package impl import ( "context" + "strings" + "time" + "github.com/go-sonic/sonic/model/dto" "github.com/go-sonic/sonic/model/entity" "github.com/go-sonic/sonic/util" "github.com/go-sonic/sonic/util/xerr" - "strings" - "time" "github.com/go-sonic/sonic/dal" "github.com/go-sonic/sonic/model/param" diff --git a/util/common.go b/util/common.go index f77fbf0f..5c5b22c4 100644 --- a/util/common.go +++ b/util/common.go @@ -2,12 +2,13 @@ package util import ( "errors" - "github.com/go-playground/validator/v10" - "github.com/go-sonic/sonic/handler/trans" - "github.com/go-sonic/sonic/util/xerr" "regexp" "strings" "unicode/utf8" + + "github.com/go-playground/validator/v10" + "github.com/go-sonic/sonic/handler/trans" + "github.com/go-sonic/sonic/util/xerr" ) func IfElse(condition bool, a, b interface{}) interface{} { @@ -50,7 +51,7 @@ func HTMLFormatWordCount(html string) int64 { return int64(utf8.RuneCountInString(text) - len(blankRegexp.FindSubmatchIndex(StringToBytes(text)))) } -func WrapJsonBindErr(err error) error { +func WrapJSONBindErr(err error) error { e := validator.ValidationErrors{} if errors.As(err, &e) { return xerr.WithStatus(e, xerr.StatusBadRequest).WithMsg(trans.Translate(e)) From 9ba38425131364dec88a6604e76825fd0d6427ad Mon Sep 17 00:00:00 2001 From: textworld Date: Fri, 1 Dec 2023 09:18:17 +0800 Subject: [PATCH 09/11] style: golintci --- handler/content/wp/post.go | 1 + handler/middleware/application_password.go | 8 +++++--- handler/server.go | 3 ++- util/common.go | 1 + 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/handler/content/wp/post.go b/handler/content/wp/post.go index ee0e3279..e2027ce6 100644 --- a/handler/content/wp/post.go +++ b/handler/content/wp/post.go @@ -7,6 +7,7 @@ import ( "time" "github.com/gin-gonic/gin" + sonicconst "github.com/go-sonic/sonic/consts" "github.com/go-sonic/sonic/log" "github.com/go-sonic/sonic/model/dto/wp" diff --git a/handler/middleware/application_password.go b/handler/middleware/application_password.go index 66507af1..c8e504f3 100644 --- a/handler/middleware/application_password.go +++ b/handler/middleware/application_password.go @@ -3,13 +3,15 @@ package middleware import ( "encoding/base64" "fmt" + "net/http" + "regexp" + "strings" + "github.com/gin-gonic/gin" + "github.com/go-sonic/sonic/consts" "github.com/go-sonic/sonic/model/dto" "github.com/go-sonic/sonic/service" - "net/http" - "regexp" - "strings" ) var ( diff --git a/handler/server.go b/handler/server.go index 09f2abfb..0ccec9f6 100644 --- a/handler/server.go +++ b/handler/server.go @@ -3,11 +3,12 @@ package handler import ( "context" "fmt" - "github.com/go-sonic/sonic/handler/content/wp" "net/http" "os" "strconv" + "github.com/go-sonic/sonic/handler/content/wp" + "github.com/gin-gonic/gin" "go.uber.org/dig" "go.uber.org/fx" diff --git a/util/common.go b/util/common.go index 5c5b22c4..0de5c223 100644 --- a/util/common.go +++ b/util/common.go @@ -7,6 +7,7 @@ import ( "unicode/utf8" "github.com/go-playground/validator/v10" + "github.com/go-sonic/sonic/handler/trans" "github.com/go-sonic/sonic/util/xerr" ) From ad38517fd7eda46d09f6ad1e4fa96e58806e5530 Mon Sep 17 00:00:00 2001 From: textworld Date: Mon, 4 Dec 2023 23:00:31 +0800 Subject: [PATCH 10/11] style: style all project --- handler/admin/application_password.go | 1 + handler/content/wp/post.go | 11 +++++------ handler/content/wp/tag.go | 3 ++- handler/middleware/application_password.go | 4 +--- service/application_password.go | 1 + service/impl/application_password.go | 1 - 6 files changed, 10 insertions(+), 11 deletions(-) diff --git a/handler/admin/application_password.go b/handler/admin/application_password.go index ab5c2d96..0b186dc5 100644 --- a/handler/admin/application_password.go +++ b/handler/admin/application_password.go @@ -2,6 +2,7 @@ package admin import ( "errors" + "github.com/gin-gonic/gin" "github.com/go-playground/validator/v10" "github.com/go-sonic/sonic/handler/trans" diff --git a/handler/content/wp/post.go b/handler/content/wp/post.go index e2027ce6..90024005 100644 --- a/handler/content/wp/post.go +++ b/handler/content/wp/post.go @@ -116,11 +116,10 @@ func parsePostParam(ctx *gin.Context) (*param.Post, error) { log.CtxInfo(ctx, "wpPost: "+string(bytes)) return convertToPostParam(&wpPost) - } func convertToPostParam(wpPost *param.WpPost) (*param.Post, error) { - var paramPostStatus = sonicconst.PostStatusPublished + paramPostStatus := sonicconst.PostStatusPublished if strings.ToLower(wpPost.Status) == "draft" { paramPostStatus = sonicconst.PostStatusDraft } @@ -168,9 +167,9 @@ func convertToPostParam(wpPost *param.WpPost) (*param.Post, error) { func convertToWpPost(postEntity *entity.Post) *wp.PostDTO { timeFormat := time.RFC3339 - var wpStatus = "publish" - var wpCommentStatus = "open" - var wpContent = make(map[string]interface{}) + wpStatus := "publish" + wpCommentStatus := "open" + wpContent := make(map[string]interface{}) if postEntity.Status == sonicconst.PostStatusDraft { wpStatus = "draft" @@ -183,7 +182,7 @@ func convertToWpPost(postEntity *entity.Post) *wp.PostDTO { wpContent["rendered"] = postEntity.OriginalContent wpContent["protected"] = false - var postDTO = &wp.PostDTO{ + postDTO := &wp.PostDTO{ Date: postEntity.CreateTime.Format(timeFormat), DateGmt: postEntity.CreateTime.UTC().Format(timeFormat), GUID: nil, diff --git a/handler/content/wp/tag.go b/handler/content/wp/tag.go index 3de6fd58..e81ed507 100644 --- a/handler/content/wp/tag.go +++ b/handler/content/wp/tag.go @@ -1,13 +1,14 @@ package wp import ( + "strings" + "github.com/gin-gonic/gin" "github.com/go-sonic/sonic/model/dto/wp" "github.com/go-sonic/sonic/model/entity" "github.com/go-sonic/sonic/model/param" "github.com/go-sonic/sonic/service" "github.com/go-sonic/sonic/util/xerr" - "strings" ) type TagHandler struct { diff --git a/handler/middleware/application_password.go b/handler/middleware/application_password.go index c8e504f3..0ef3c190 100644 --- a/handler/middleware/application_password.go +++ b/handler/middleware/application_password.go @@ -14,9 +14,7 @@ import ( "github.com/go-sonic/sonic/service" ) -var ( - basicAuthRegexp = regexp.MustCompile(`^Basic [a-z\\d/+]*={0,2}`) -) +var basicAuthRegexp = regexp.MustCompile(`^Basic [a-z\\d/+]*={0,2}`) type ApplicationPasswordMiddleware struct { PasswordService service.ApplicationPasswordService diff --git a/service/application_password.go b/service/application_password.go index 57346043..aec45f64 100644 --- a/service/application_password.go +++ b/service/application_password.go @@ -2,6 +2,7 @@ package service import ( "context" + "github.com/go-sonic/sonic/model/dto" "github.com/go-sonic/sonic/model/entity" "github.com/go-sonic/sonic/model/param" diff --git a/service/impl/application_password.go b/service/impl/application_password.go index 0d206c1b..8572c4ed 100644 --- a/service/impl/application_password.go +++ b/service/impl/application_password.go @@ -149,7 +149,6 @@ func (a *applicationPasswordServiceImpl) Update(ctx context.Context, entityID in LastActivateIP: ip, LastActivateTime: &now, }) - if err != nil { return WrapDBErr(err) } From 3fcf41fdaa5c7cf9bb14ceca826dd548b5825fbf Mon Sep 17 00:00:00 2001 From: textworld Date: Tue, 5 Dec 2023 08:23:28 +0800 Subject: [PATCH 11/11] style: fix imports --- handler/admin/application_password.go | 1 + handler/content/wp/category.go | 1 + handler/content/wp/tag.go | 1 + handler/content/wp/user.go | 1 + 4 files changed, 4 insertions(+) diff --git a/handler/admin/application_password.go b/handler/admin/application_password.go index 0b186dc5..ea01d383 100644 --- a/handler/admin/application_password.go +++ b/handler/admin/application_password.go @@ -5,6 +5,7 @@ import ( "github.com/gin-gonic/gin" "github.com/go-playground/validator/v10" + "github.com/go-sonic/sonic/handler/trans" "github.com/go-sonic/sonic/model/param" "github.com/go-sonic/sonic/service" diff --git a/handler/content/wp/category.go b/handler/content/wp/category.go index 99561f56..3cc8dc7c 100644 --- a/handler/content/wp/category.go +++ b/handler/content/wp/category.go @@ -2,6 +2,7 @@ package wp import ( "github.com/gin-gonic/gin" + "github.com/go-sonic/sonic/model/dto/wp" "github.com/go-sonic/sonic/model/entity" "github.com/go-sonic/sonic/model/param" diff --git a/handler/content/wp/tag.go b/handler/content/wp/tag.go index e81ed507..1e850cea 100644 --- a/handler/content/wp/tag.go +++ b/handler/content/wp/tag.go @@ -4,6 +4,7 @@ import ( "strings" "github.com/gin-gonic/gin" + "github.com/go-sonic/sonic/model/dto/wp" "github.com/go-sonic/sonic/model/entity" "github.com/go-sonic/sonic/model/param" diff --git a/handler/content/wp/user.go b/handler/content/wp/user.go index 07cdb08a..4f08370f 100644 --- a/handler/content/wp/user.go +++ b/handler/content/wp/user.go @@ -2,6 +2,7 @@ package wp import ( "github.com/gin-gonic/gin" + "github.com/go-sonic/sonic/model/dto" "github.com/go-sonic/sonic/service" )