- 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
91 lines
5.2 KiB
Markdown
91 lines
5.2 KiB
Markdown
# golem15.feedback
|
|
|
|
The shared feedback-widget plugin for SummerCMS applications: an embeddable widget's public config and submit API behind a widget key and an Origin allow-list, a per-user "hide the widget" preference, the widget settings, and a job that files every submission as a G15Office task.
|
|
|
|
```go
|
|
import _ "git.golem15.com/golem15/sm-feedback-plugin"
|
|
```
|
|
|
|
Module `git.golem15.com/golem15/sm-feedback-plugin`, package `feedback`, plugin ID `golem15.feedback`.
|
|
|
|
## Overview
|
|
|
|
The plugin is a compiled SummerCMS plugin ported from the WinterCMS `Golem15.Feedback` plugin. Its `init` registers it with the framework, so an application loads it by listing it in `summer.yaml`; `summer build` then emits the blank import above into the application's `plugins.gen.go`. It requires `golem15.user`.
|
|
|
|
The widget routes are public. The widget key in the URL is a public identifier (it ships in the embed snippet), not a secret: the real gate is the Origin check against the operator's allowed-origins list, backed by per-IP rate limits.
|
|
|
|
The plugin ID, the table names, the config keys and the settings code are a contract shared by every application that mounts the plugin: change them only in a backwards-compatible way.
|
|
|
|
## Features
|
|
|
|
- **Widget config.** `GET /_feedback/api/v1/{key}/config` answers the widget's position, whether visitors may hide it, its colors and its labels (`?lang=en` selects the English labels, anything else the Polish ones). A disabled widget, an empty key or a key that differs (compared in constant time) is 404 `{"error":true,"message":"Not found"}`.
|
|
- **Origin gate.** A request without an `Origin` header passes. An `Origin` passes only when its URL host, lower-cased, is on the allowed-origins list (one entry per line; a line is reduced to its URL host when it has one). An empty list refuses every `Origin` with 403 `{"error":true,"message":"Origin not allowed"}`, so a widget that was never configured cannot be embedded anywhere.
|
|
- **Rate limits.** The named buckets `feedback-config` (60 a minute) and `feedback-submit` (10 a minute) count per client IP behind the configured trusted proxies.
|
|
- **Settings.** `models.Settings` is the singleton row of `golem15_feedback_settings`. `models.Instance` creates it on first access with the WinterCMS defaults and a new widget key (`wk_` plus 32 random letters and digits).
|
|
|
|
## Usage
|
|
|
|
Mount the plugin in an application as a git submodule at `plugins/golem15/feedback`:
|
|
|
|
```sh
|
|
git submodule add git@git.golem15.com:golem15/sm-feedback-plugin.git plugins/golem15/feedback
|
|
```
|
|
|
|
Add it to the application's `go.work` and `go.mod`:
|
|
|
|
```text
|
|
use ./plugins/golem15/feedback
|
|
|
|
require git.golem15.com/golem15/sm-feedback-plugin v0.0.0
|
|
|
|
replace git.golem15.com/golem15/sm-feedback-plugin => ./plugins/golem15/feedback
|
|
```
|
|
|
|
List it in `summer.yaml` after `golem15.user` and run `summer build`:
|
|
|
|
```yaml
|
|
module: git.golem15.com/golem15/acme
|
|
binary: acme
|
|
plugins:
|
|
- id: golem15.user
|
|
module: git.golem15.com/golem15/sm-user-plugin
|
|
- id: golem15.feedback
|
|
module: git.golem15.com/golem15/sm-feedback-plugin
|
|
```
|
|
|
|
The application's `http.cors.paths` must list `_feedback/api/*`: the widget calls the API from other origins.
|
|
|
|
The plugin's own `go.mod` replaces the framework with `../../../../summercms.go` and the user plugin with `../user`, so it expects the framework checkout next to the application checkout and the user plugin mounted beside it.
|
|
|
|
## API reference
|
|
|
|
- `feedback`: `Plugin`, the `golem15.feedback` plugin (config, lang, migrations, models, routes, buckets); `BucketConfig`, `BucketSubmit`.
|
|
- `controllers/api`: `Config`, `KeyMatches`, `OriginAllowed`.
|
|
- `models`: `Settings` (table `golem15_feedback_settings`), `SettingsID`, `SettingsCode`, `DefaultSettings`, `Instance`, `NewWidgetKey`, `Labels`, `URLHost`, `ASCIILower`; `Submission` (table `golem15_feedback_submissions`), `StatusPending`, `StatusSent`, `StatusFailed`, `TypeBug`, `TypeFeature`, `TypeOther`, `ScreenshotField`, `G15OfficeTypeMap`; `UserPreference` (table `golem15_feedback_user_preferences`), `IsWidgetHidden`, `SetWidgetHidden`; `Register`, `All`.
|
|
- `updates`: the migrations (`202610030201_create_feedback_submissions`, `202610030202_create_feedback_user_preferences`, `202610030203_create_feedback_settings`), `Register`, `All`.
|
|
|
|
## Configuration
|
|
|
|
Defaults live in `config/config.yaml` and are merged under `golem15.feedback.*`.
|
|
|
|
| Key | Default | Meaning |
|
|
|-----|---------|---------|
|
|
| `golem15.feedback.g15_office.base_url` | empty | The G15Office base URL submissions are filed under. |
|
|
| `golem15.feedback.g15_office.token` | empty | The G15Office API token. Never commit it. |
|
|
| `golem15.feedback.g15_office.project` | empty | The G15Office project the tasks are created in. |
|
|
|
|
Override them with `SUMMER_GOLEM15__FEEDBACK__G15_OFFICE__BASE_URL`, `SUMMER_GOLEM15__FEEDBACK__G15_OFFICE__TOKEN` and `SUMMER_GOLEM15__FEEDBACK__G15_OFFICE__PROJECT`.
|
|
|
|
## Dependencies
|
|
|
|
- The SummerCMS framework, `git.golem15.com/golem15/summercms` (modules `backpack`, `compass`, `lagoon`, `pact`, `party`, `surf`, `wire`).
|
|
- The user plugin, `git.golem15.com/golem15/sm-user-plugin`.
|
|
- GORM and gormigrate, as pinned in `go.mod`.
|
|
- testcontainers-go for the Postgres-backed tests.
|
|
|
|
## Testing
|
|
|
|
```sh
|
|
go test ./... # starts Postgres with testcontainers (needs Docker)
|
|
```
|