feat: golem15.feedback plugin with the widget config route
- plugin golem15.feedback (requires golem15.user), its config, lang (en, pl), models and migrations
- typed settings singleton golem15_feedback_settings with the WinterCMS defaults and a widget key generated on first creation
- GET /_feedback/api/v1/{key}/config behind the constant-time key check and the fail-closed Origin allow-list, with the feedback-config and feedback-submit buckets
This commit is contained in:
98
plugin.go
Normal file
98
plugin.go
Normal file
@@ -0,0 +1,98 @@
|
||||
// Package feedback is the golem15.feedback plugin: the embeddable feedback
|
||||
// widget of SummerCMS applications. It ports the WinterCMS Golem15.Feedback
|
||||
// plugin: the widget's public config and submit routes behind a widget key
|
||||
// and an Origin allow-list, the per-user "hide the widget" preference, the
|
||||
// settings screen, and the job that files every submission as a G15Office
|
||||
// task with its screenshot.
|
||||
package feedback
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"git.golem15.com/golem15/sm-feedback-plugin/models"
|
||||
"git.golem15.com/golem15/sm-feedback-plugin/updates"
|
||||
"git.golem15.com/golem15/summercms/modules/backpack"
|
||||
"git.golem15.com/golem15/summercms/modules/pact"
|
||||
"git.golem15.com/golem15/summercms/modules/party"
|
||||
"git.golem15.com/golem15/summercms/modules/surf"
|
||||
"github.com/go-gormigrate/gormigrate/v2"
|
||||
)
|
||||
|
||||
var (
|
||||
_ party.Plugin = (*Plugin)(nil)
|
||||
_ pact.HasConfig = (*Plugin)(nil)
|
||||
_ pact.HasLang = (*Plugin)(nil)
|
||||
_ pact.HasMigrations = (*Plugin)(nil)
|
||||
_ pact.HasModels = (*Plugin)(nil)
|
||||
_ pact.HasRoutes = (*Plugin)(nil)
|
||||
_ surf.BucketProvider = (*Plugin)(nil)
|
||||
)
|
||||
|
||||
//go:embed config
|
||||
var configFS embed.FS
|
||||
|
||||
//go:embed lang
|
||||
var langFS embed.FS
|
||||
|
||||
// Bucket names of the widget routes (routes.php RateLimiter::for).
|
||||
const (
|
||||
BucketConfig = "feedback-config"
|
||||
BucketSubmit = "feedback-submit"
|
||||
)
|
||||
|
||||
// Plugin is the golem15.feedback plugin.
|
||||
type Plugin struct {
|
||||
app *backpack.App
|
||||
}
|
||||
|
||||
// ID is the plugin ID, also its config and lang namespace.
|
||||
func (p *Plugin) ID() string { return "golem15.feedback" }
|
||||
|
||||
// Requires is golem15.user: the preference route is JWT-guarded and the
|
||||
// preference rides on the user payload.
|
||||
func (p *Plugin) Requires() []string { return []string{"golem15.user"} }
|
||||
|
||||
// Register keeps the application.
|
||||
func (p *Plugin) Register(app *backpack.App) error {
|
||||
p.app = app
|
||||
return nil
|
||||
}
|
||||
|
||||
// Boot keeps the application.
|
||||
func (p *Plugin) Boot(app *backpack.App) error {
|
||||
p.app = app
|
||||
return nil
|
||||
}
|
||||
|
||||
// ConfigFS ships config/config.yaml, merged under golem15.feedback.*.
|
||||
func (p *Plugin) ConfigFS() fs.FS { return configFS }
|
||||
|
||||
// LangFS ships the golem15.feedback lang namespace (en, pl).
|
||||
func (p *Plugin) LangFS() fs.FS { return langFS }
|
||||
|
||||
// Migrations creates the submissions, preferences and settings tables.
|
||||
func (p *Plugin) Migrations() []*gormigrate.Migration { return updates.All() }
|
||||
|
||||
// Models is Submission, UserPreference and Settings.
|
||||
func (p *Plugin) Models() []any { return models.All() }
|
||||
|
||||
// Buckets are the widget routes' limits by client IP (behind the trusted
|
||||
// proxies): 60 config reads and 10 submissions a minute.
|
||||
func (p *Plugin) Buckets() map[string]surf.Bucket {
|
||||
trusted := surf.TrustedProxies(nil)
|
||||
if p.app != nil {
|
||||
trusted = surf.TrustedProxies(p.app.Config)
|
||||
}
|
||||
byIP := func(r *http.Request) string { return surf.ClientIP(r, trusted) }
|
||||
return map[string]surf.Bucket{
|
||||
BucketConfig: {Max: 60, Decay: time.Minute, Key: byIP},
|
||||
BucketSubmit: {Max: 10, Decay: time.Minute, Key: byIP},
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
party.Register(&Plugin{})
|
||||
}
|
||||
Reference in New Issue
Block a user