feat(05-01): restructure fonoteka plugins into Winter directories
- Move models, classes, controllers, middleware, and updates into subpackages - Add self-registering models/updates/classes registries so later plans add files - Keep the genres route and shipped migration SQL unchanged
This commit is contained in:
21
classes/registry.go
Normal file
21
classes/registry.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package classes
|
||||
|
||||
import "gorm.io/gorm"
|
||||
|
||||
var hookRegistrars []func(*gorm.DB) error
|
||||
|
||||
// RegisterHook appends a GORM-callback registrant so later plans add files
|
||||
// without editing plugin.go Boot.
|
||||
func RegisterHook(fn func(*gorm.DB) error) {
|
||||
hookRegistrars = append(hookRegistrars, fn)
|
||||
}
|
||||
|
||||
// RegisterHooks runs every registered hook against gdb.
|
||||
func RegisterHooks(gdb *gorm.DB) error {
|
||||
for _, fn := range hookRegistrars {
|
||||
if err := fn(gdb); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package user
|
||||
package classes
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -6,34 +6,28 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"git.golem15.com/golem15/fonoteka/plugins/golem15/user/models"
|
||||
"git.golem15.com/golem15/summercms/backpack"
|
||||
"git.golem15.com/golem15/summercms/bouncer"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// User is the minimal users row the JWT guard loads.
|
||||
type User struct {
|
||||
ID uint `gorm:"column:id;primaryKey"`
|
||||
Email string `gorm:"column:email"`
|
||||
Password string `gorm:"column:password"`
|
||||
MustChangePassword bool `gorm:"column:must_change_password"`
|
||||
var _ bouncer.UserProvider = GormUsers{}
|
||||
|
||||
// GormUsers loads JWT principals from the users table.
|
||||
type GormUsers struct {
|
||||
App *backpack.App
|
||||
}
|
||||
|
||||
func (User) TableName() string { return "users" }
|
||||
|
||||
type gormUsers struct {
|
||||
app *backpack.App
|
||||
}
|
||||
|
||||
func (g gormUsers) FindByID(ctx context.Context, id uint) (*bouncer.Principal, error) {
|
||||
if g.app == nil {
|
||||
func (g GormUsers) FindByID(ctx context.Context, id uint) (*bouncer.Principal, error) {
|
||||
if g.App == nil {
|
||||
return nil, fmt.Errorf("golem15.user: app is nil")
|
||||
}
|
||||
gdb, ok := g.app.Lookup[*gorm.DB]()
|
||||
gdb, ok := g.App.Lookup[*gorm.DB]()
|
||||
if !ok || gdb == nil {
|
||||
return nil, fmt.Errorf("golem15.user: gorm handle is missing")
|
||||
}
|
||||
var row User
|
||||
var row models.User
|
||||
err := gdb.WithContext(ctx).Where("id = ?", id).Take(&row).Error
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, nil
|
||||
@@ -44,7 +38,8 @@ func (g gormUsers) FindByID(ctx context.Context, id uint) (*bouncer.Principal, e
|
||||
return &bouncer.Principal{ID: row.ID, MustChangePassword: row.MustChangePassword}, nil
|
||||
}
|
||||
|
||||
func jwtSecret(app *backpack.App) (string, error) {
|
||||
// JWTSecret reads golem15.user.jwt.secret and fails loudly when it is empty.
|
||||
func JWTSecret(app *backpack.App) (string, error) {
|
||||
if app == nil || app.Config == nil {
|
||||
return "", fmt.Errorf("golem15.user: jwt.secret is empty (set SUMMER_GOLEM15__USER__JWT__SECRET)")
|
||||
}
|
||||
13
models/registry.go
Normal file
13
models/registry.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package models
|
||||
|
||||
var all []any
|
||||
|
||||
// Register appends GORM models so later plans add files without editing plugin.go.
|
||||
func Register(models ...any) {
|
||||
all = append(all, models...)
|
||||
}
|
||||
|
||||
// All returns every model registered by this plugin's models package.
|
||||
func All() []any {
|
||||
return all
|
||||
}
|
||||
15
models/user.go
Normal file
15
models/user.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package models
|
||||
|
||||
// User is the minimal users row the JWT guard loads.
|
||||
type User struct {
|
||||
ID uint `gorm:"column:id;primaryKey"`
|
||||
Email string `gorm:"column:email"`
|
||||
Password string `gorm:"column:password"`
|
||||
MustChangePassword bool `gorm:"column:must_change_password"`
|
||||
}
|
||||
|
||||
func (User) TableName() string { return "users" }
|
||||
|
||||
func init() {
|
||||
Register(User{})
|
||||
}
|
||||
25
plugin.go
25
plugin.go
@@ -4,11 +4,15 @@ import (
|
||||
"embed"
|
||||
"io/fs"
|
||||
|
||||
"git.golem15.com/golem15/fonoteka/plugins/golem15/user/classes"
|
||||
"git.golem15.com/golem15/fonoteka/plugins/golem15/user/models"
|
||||
"git.golem15.com/golem15/fonoteka/plugins/golem15/user/updates"
|
||||
"git.golem15.com/golem15/summercms/backpack"
|
||||
"git.golem15.com/golem15/summercms/bouncer"
|
||||
"git.golem15.com/golem15/summercms/pact"
|
||||
"git.golem15.com/golem15/summercms/party"
|
||||
"github.com/go-gormigrate/gormigrate/v2"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -17,7 +21,7 @@ var (
|
||||
_ pact.HasMigrations = (*Plugin)(nil)
|
||||
_ pact.HasMiddleware = (*Plugin)(nil)
|
||||
_ pact.HasModels = (*Plugin)(nil)
|
||||
_ bouncer.UserProvider = gormUsers{}
|
||||
_ bouncer.UserProvider = classes.GormUsers{}
|
||||
)
|
||||
|
||||
//go:embed config
|
||||
@@ -38,23 +42,30 @@ func (p *Plugin) Register(app *backpack.App) error {
|
||||
|
||||
func (p *Plugin) Boot(app *backpack.App) error {
|
||||
p.app = app
|
||||
_, err := jwtSecret(app)
|
||||
return err
|
||||
if _, err := classes.JWTSecret(app); err != nil {
|
||||
return err
|
||||
}
|
||||
if gdb, ok := app.Lookup[*gorm.DB](); ok {
|
||||
if err := classes.RegisterHooks(gdb); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Plugin) ConfigFS() fs.FS { return configFS }
|
||||
|
||||
func (p *Plugin) Migrations() []*gormigrate.Migration { return migrations }
|
||||
func (p *Plugin) Migrations() []*gormigrate.Migration { return updates.All() }
|
||||
|
||||
func (p *Plugin) Models() []any { return []any{User{}} }
|
||||
func (p *Plugin) Models() []any { return models.All() }
|
||||
|
||||
func (p *Plugin) Middlewares() map[string]pact.Middleware {
|
||||
secret, err := jwtSecret(p.app)
|
||||
secret, err := classes.JWTSecret(p.app)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return map[string]pact.Middleware{
|
||||
"jwt.auth": bouncer.Middleware(secret, gormUsers{app: p.app}),
|
||||
"jwt.auth": bouncer.Middleware(secret, classes.GormUsers{App: p.app}),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package user
|
||||
package updates
|
||||
|
||||
import (
|
||||
"github.com/go-gormigrate/gormigrate/v2"
|
||||
@@ -27,3 +27,7 @@ CREATE TABLE users (
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
Register(migrations...)
|
||||
}
|
||||
15
updates/registry.go
Normal file
15
updates/registry.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package updates
|
||||
|
||||
import "github.com/go-gormigrate/gormigrate/v2"
|
||||
|
||||
var all []*gormigrate.Migration
|
||||
|
||||
// Register appends gormigrate steps so later plans add files without editing plugin.go.
|
||||
func Register(ms ...*gormigrate.Migration) {
|
||||
all = append(all, ms...)
|
||||
}
|
||||
|
||||
// All returns every migration registered by this plugin's updates package.
|
||||
func All() []*gormigrate.Migration {
|
||||
return all
|
||||
}
|
||||
Reference in New Issue
Block a user