docs(11.2): capture phase context
Four repos (sm-summercms-app, vue-summercms-app, sm-summercms-plugin, sm-newsletter-plugin), Nuxt 4 static site in EN and PL embedded in the binary, double opt-in signup with honeypot, consent and neutral responses, and a standalone sending-ready subscribers table.
This commit is contained in:
@@ -529,20 +529,20 @@ Plans:
|
||||
|
||||
### Phase 11.2: Ready to share: summercms.io website and newsletter plugin (INSERTED)
|
||||
|
||||
**Goal:** summercms.io is ready to share publicly. A fresh Vue website runs on a SummerCMS backend. Visitors subscribe for updates through the initial Go version of the Golem15 Newsletter plugin, which confirms each email by double opt-in. The Phase 11.1 docs are served at `/docs` and linked from the site.
|
||||
**Goal:** summercms.io is ready to share publicly. A fresh Nuxt 4 website in English and Polish runs on a SummerCMS binary. Visitors subscribe for updates through the initial Go version of the Golem15 Newsletter plugin, which confirms each email by double opt-in. The Phase 11.1 docs are served at `/docs` and linked from the site.
|
||||
**Requirements**: TBD
|
||||
**Depends on:** Phase 11.1
|
||||
**Repos:** three new repos: (1) `sm-newsletter-plugin`, the Go newsletter plugin as a compiled SummerCMS plugin module; (2) `sm-summercms-app`, the root app for summercms.io, which registers the plugin, serves the website's API and serves the docs; (3) `vue-summercms-app`, the fresh Vue website, held inside `sm-summercms-app`. `summercms.go` changes only if the site needs a framework feature that is missing.
|
||||
**Repos:** four new repos under `git.golem15.com/golem15/`: (1) `sm-summercms-app`, the root app that builds the binary and wires the plugins; (2) `vue-summercms-app`, the Nuxt 4 site, held inside the root app; (3) `sm-summercms-plugin`, the site plugin that serves the embedded site at `/`, the docs at `/docs` and any site-specific data, the WinterCMS site-plugin pattern; (4) `sm-newsletter-plugin`, the reusable signup plugin. `summercms.go` changes only if the site needs a framework feature that is missing.
|
||||
**Naming convention:** the same as OctoberCMS (`oc-`) and WinterCMS (`wn-`), with the `sm-` prefix: root app `sm-<name>-app`, Vue frontend `vue-<name>-app`, plugin `sm-<name>-plugin`, theme `sm-<name>-theme`. The Go package inside a plugin keeps its plain name (`newsletter`). The meta root `summercms.io` and the framework `summercms.go` keep their names. `fonoteka.go` becomes `sm-fonoteka-app`.
|
||||
**Port source:** Golem15.Newsletter, `github.com/golem15com/wn-newsletter-plugin`, checked out at `/media/nvme/dev/golem15/horoskopia.eu/plugins/golem15/newsletter`. The PHP plugin is work in progress. Its audience comes from registered users (it requires `Golem15.User`), and its only public routes are unsubscribe. It has no public subscriber model or signup widget, so this phase adds those in Go. The PHP original is not changed.
|
||||
**Success Criteria** (what must be TRUE):
|
||||
|
||||
1. The newsletter plugin repo exists and ships an initial stub: a public signup endpoint (validated, deduplicated, rate-limited and spam-guarded), double opt-in confirmation mail with a tokenized confirm link, unsubscribe, and a backend subscriber list in the admin SPA. Composing and sending newsletters is out of scope.
|
||||
2. The Vue website repo exists and ships a simple summercms.io landing page with a signup widget. The widget calls the plugin's API on the SummerCMS backend and covers the pending, confirmed and error states.
|
||||
1. The newsletter plugin repo exists and ships an initial stub: a public signup endpoint (validated, honeypot and rate-limited, with required consent), double opt-in confirmation mail with a tokenized confirm link, a welcome mail, unsubscribe, and subscriber add, edit and delete in the admin SPA. Composing and sending newsletters is out of scope.
|
||||
2. The website repo exists and ships a prerendered English and Polish summercms.io landing page with a signup widget. The widget calls the plugin's API on the same origin and covers the pending, confirmed and error states.
|
||||
3. The Phase 11.1 docs are served at `summercms.io/docs`, the landing page links to them, and every link resolves.
|
||||
4. The site deploys with documented run steps: the SummerCMS binary plus the built Vue assets.
|
||||
4. The site deploys as one binary with the Nuxt build and the docs embedded, with documented build and run steps.
|
||||
|
||||
**Open questions:** The Vue build approach (SPA or prerendered). Whether the stub's schema should be designed so newsletter sending can be added later, with confirmed subscribers joining the existing user-based audience.
|
||||
**Context:** `.planning/phases/11.2-ready-to-share-summercms-io-website-and-newsletter-plugin/11.2-CONTEXT.md`
|
||||
|
||||
**Plans:** 0 plans
|
||||
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
# Phase 11.2: Ready to share: summercms.io website and newsletter plugin - Context
|
||||
|
||||
**Gathered:** 2026-09-29
|
||||
**Status:** Ready for planning
|
||||
|
||||
<domain>
|
||||
## Phase Boundary
|
||||
|
||||
summercms.io becomes shareable. A Nuxt 4 landing site in English and Polish runs on a SummerCMS binary. Visitors subscribe for updates through a new `sm-newsletter-plugin`, a stub that collects emails with double opt-in and does not send newsletters. The Phase 11.1 docs are served at `/docs` and linked from the site. The work spans four new repos, laid out like a WinterCMS project (root app, Vue frontend, site plugin and a reusable plugin). The framework (`summercms.go`) changes only if a missing framework feature blocks the site.
|
||||
|
||||
</domain>
|
||||
|
||||
<decisions>
|
||||
## Implementation Decisions
|
||||
|
||||
### Repos and layout
|
||||
- **D-01:** Four new repos follow the `sm-` naming convention (`oc-`/`wn-` in October/Winter):
|
||||
- `sm-summercms-app` is the root app. It builds the binary, holds the other pieces and wires the plugins.
|
||||
- `vue-summercms-app` is the Nuxt 4 site, held inside the root app as `vue-fonoteka-app` is in the fonoteka project.
|
||||
- `sm-summercms-plugin` is the site plugin. It holds everything specific to summercms.io: serving the embedded Nuxt build at `/`, mounting the 11.1 docs output at `/docs`, and any site-specific data or routes. This is the proven WinterCMS pattern from `figs.org.pl` (`figs/website`) and golem15.com.
|
||||
- `sm-newsletter-plugin` is the reusable signup plugin, with Go package `newsletter`.
|
||||
|
||||
Public static-site serving goes in `sm-summercms-plugin`, not in a new framework module. — **Reversibility:** costly — repo names and module paths are referenced by the go.work/replace wiring and every import.
|
||||
- **D-02:** Go module paths live under `git.golem15.com/golem15/`, like the framework (`git.golem15.com/golem15/summercms`): for example `git.golem15.com/golem15/sm-newsletter-plugin`. — **Reversibility:** one-way — a published Go module path is baked into every importer.
|
||||
- **D-03:** The root app wires the framework and both plugins the way `fonoteka.go` does today (a go.work workspace with a local replace during development).
|
||||
|
||||
### Website build and hosting
|
||||
- **D-04:** The site is Nuxt 4 with `nuxt generate` (static output), the same stack as `vue-fonoteka-app`. Every route is real prerendered HTML, so link previews and search engines see content. The signup widget hydrates as normal Vue.
|
||||
- **D-05:** The generated site and the 11.1 docs output are embedded in the Go binary with `go:embed`. One binary serves `/`, `/docs` and the plugin API. The build order (nuxt generate, then `summer docs:build`, then `go build`) is documented and scripted in the root app.
|
||||
- **D-06:** The site is in English and Polish (for example `/` and `/pl/`, via `@nuxtjs/i18n` as in `vue-fonoteka-app`). The confirmation and welcome mails are localized to the subscriber's locale through phrasebook/postcard. The 11.1 docs stay English-only.
|
||||
- **D-07:** The site's served responses must be indexable. The admin-only `boardwalk` behaviour (`noindex, nofollow`, admin CSP, base-path rewrite) must not apply to the public site. The site plugin sets its own cache headers: immutable for hashed assets, no-cache for HTML.
|
||||
|
||||
### Landing page content
|
||||
- **D-08:** The page has these sections: a hero with the email signup, "what it is" (plugins that extend each other, YAML admin forms and lists, scaffolding CLI, headless API, a single compiled binary), "Coming from WinterCMS" linking to the 11.1 concept map, and status and roadmap.
|
||||
- **D-09:** The "Coming from WinterCMS" section tells the lineage as a seasons story: it started in October (OctoberCMS), went through Winter (WinterCMS), and now it's time for Summer. The seasons theme can carry through the copy and visuals.
|
||||
- **D-10:** The page is clearly pre-release in tone. It says the project is early and asks people to subscribe for the first release.
|
||||
- **D-11:** The page has no link to source repos yet. Subscribe is the call to action.
|
||||
- **D-12:** A "Built by Golem15" footer credit links to golem15.com.
|
||||
|
||||
### Signup and double opt-in
|
||||
- **D-13:** Bot protection is a honeypot field, a minimum time-to-submit check and a per-IP `surf` rate-limit bucket. There is no captcha or third-party script.
|
||||
- **D-14:** A required consent checkbox ("I agree to receive SummerCMS updates") links to a privacy note. The plugin stores the consent timestamp, IP and locale with the subscriber as GDPR evidence.
|
||||
- **D-15:** Every signup gets the same neutral "check your inbox" response, and the response never reveals list membership:
|
||||
- pending: resend the confirmation, throttled
|
||||
- unsubscribed: new confirmation cycle
|
||||
- confirmed: no mail
|
||||
- **D-16:** The confirm link is tokenized. On success it redirects to a localized thank-you page on the site and sends a welcome mail with the unsubscribe link, as `figs.org.pl` does. Invalid or expired tokens redirect to a localized error page.
|
||||
- **D-17:** Unsubscribe is a tokenized link in every mail. It marks the subscriber unsubscribed and redirects to a localized "unsubscribed" page.
|
||||
|
||||
### Plugin shape
|
||||
- **D-18:** Subscribers are standalone: the plugin has its own subscribers table and no dependency on the user plugin. Merging confirmed subscribers with a user-based audience (as the PHP `AudienceResolver` does) belongs to the later sending phase.
|
||||
- **D-19:** The stub ships only the subscribers table, but with the fields sending will need: email, status (pending, confirmed, unsubscribed), locale, confirmation and unsubscribe tokens, `confirmed_at`, `unsubscribed_at`, source (form or admin), consent timestamp and IP, and timestamps. The newsletter, template and recipient tables are ported from PHP in the sending phase. — **Reversibility:** costly — the migration shape becomes the base that the sending phase extends.
|
||||
- **D-20:** The admin backend (cabana YAML list and form) supports list, search, filters (status, locale), view, add, edit and delete. Delete is the GDPR erasure path.
|
||||
- **D-21:** An admin-added subscriber is created as pending with source=admin, and the normal confirmation mail is sent. Nobody lands on the list without their own confirmation.
|
||||
- **D-22:** Composing and sending newsletters, templates and campaign CLI are out of scope.
|
||||
|
||||
### Claude's Discretion
|
||||
- How long confirmation tokens stay valid and the resend throttle interval.
|
||||
- Exact URL scheme for the API endpoints and localized result pages.
|
||||
- Token format (random opaque token vs signed).
|
||||
- Visual design within the Direction C v2 brand (dark navy + sunny yellow), copy wording and the exact page structure.
|
||||
- How `sm-summercms-plugin` mounts the docs output, and whether the Nuxt build is embedded by the plugin or the root app.
|
||||
|
||||
</decisions>
|
||||
|
||||
<canonical_refs>
|
||||
## Canonical References
|
||||
|
||||
**Downstream agents MUST read these before planning or implementing.**
|
||||
|
||||
### Phase scope
|
||||
- `.planning/ROADMAP.md` § Phase 11.2: goal, repos, naming convention, port source, success criteria.
|
||||
- `.planning/phases/11.1-summercms-documentation-for-humans-and-ai-agents/11.1-CONTEXT.md`: how the docs are built (`summer docs:build`, static output, `internal/docsite`). 11.2 serves that output at `/docs`.
|
||||
- `.planning/phases/11.1-summercms-documentation-for-humans-and-ai-agents/11.1-UI-SPEC.md`: the brand token values copied from the admin SPA. The site reuses the same palette.
|
||||
|
||||
### Port sources and references
|
||||
- `/media/nvme/dev/golem15/horoskopia.eu/plugins/golem15/newsletter`: Golem15.Newsletter PHP (`github.com/golem15com/wn-newsletter-plugin`). It is the plugin identity and future sending model (`models/`, `classes/AudienceResolver.php`, `classes/UnsubscribeTokenGenerator.php`, `routes.php`). Do not modify it.
|
||||
- `/media/nvme/dev/golem15/figs.org.pl/plugins/figs/website`: the site-plugin pattern (D-01). It also has a working subscribe flow: `models/Subscriber.php`, `models/subscriber/{fields,columns}.yaml`, `components/subscribe/`, and hash confirm/resign routes in `routes.php` that redirect to thank-you pages.
|
||||
- `/media/nvme/dev/golem15/fonoteka/vue-fonoteka-app`: the Nuxt 4 + `@nuxtjs/i18n` setup to mirror for `vue-summercms-app`.
|
||||
- `admin/src/styles/main.css` (`:root` and `.dark` blocks) and `.planning/phases/10-admin-vue-spa/design/README.md`: the Direction C v2 brand tokens.
|
||||
|
||||
### Framework modules used
|
||||
- `modules/postcard/README.md`: confirmation and welcome mail templates.
|
||||
- `modules/phrasebook/README.md`: EN and PL strings for mail and API messages.
|
||||
- `modules/surf/README.md`: routes, named rate-limit buckets and body limits.
|
||||
- `modules/lagoon/README.md`: per-plugin migrations and models.
|
||||
- `modules/cabana/README.md`: YAML list and form for the subscribers admin.
|
||||
- `modules/pact/README.md`: the plugin capability interfaces the two plugins implement.
|
||||
- `modules/boardwalk/README.md`: the admin SPA handler. It is the reference for embedding and cache headers, and it must not be reused as-is for the public site (D-07).
|
||||
- `../fonoteka.go`: the reference for app wiring (go.work, local replace, plugin registration).
|
||||
|
||||
</canonical_refs>
|
||||
|
||||
<code_context>
|
||||
## Existing Code Insights
|
||||
|
||||
### Reusable Assets
|
||||
- `postcard`: plugin-owned Markdown mail templates with memory, log and SMTP drivers. It covers the confirmation and welcome mails.
|
||||
- `surf`: named rate buckets and trusted-proxy client IP. It covers the per-IP signup limit and gives the IP to store as consent evidence.
|
||||
- `cabana` + admin SPA: YAML-driven list, form and filters. The subscriber admin needs no custom Vue.
|
||||
- `lagoon`: GORM models, per-plugin gormigrate migrations and validation.
|
||||
- `phrasebook`: namespaced EN and PL catalogs with fallback.
|
||||
- `boardwalk`: its embed and cache-header handling is a pattern to copy into `sm-summercms-plugin`, but its admin security headers must not be.
|
||||
|
||||
### Established Patterns
|
||||
- Plugins are compiled Go modules registered at build time (no runtime loading).
|
||||
- Every application repo is a go.work workspace requiring the framework with a local replace, as `fonoteka.go` is.
|
||||
- Framework READMEs never name a consuming application. The two new plugins are not framework modules, but `sm-newsletter-plugin` is reusable, so its README uses neutral examples too.
|
||||
- Unit tests are the last plan of the phase. `go vet` and `go test ./...` stay green in every repo.
|
||||
|
||||
### Integration Points
|
||||
- `sm-summercms-app` registers both plugins and serves `/`, `/docs`, `/backend` (admin) and the newsletter API from one mux.
|
||||
- The Nuxt site calls the newsletter API with a same-origin fetch, so no CORS is needed.
|
||||
- The 11.1 `summer docs:build` output feeds the embed step, so Phase 11.2 depends on 11.1 being executed.
|
||||
|
||||
</code_context>
|
||||
|
||||
<specifics>
|
||||
## Specific Ideas
|
||||
|
||||
- The seasons lineage story: October, then Winter, now Summer (D-09).
|
||||
- The site should feel like the admin and docs brand (Direction C v2: dark navy + sunny yellow), so summercms.io, `/docs` and `/backend` look like one product.
|
||||
- Follow the figs.org.pl experience for confirmation: thank-you page, welcome mail and unsubscribe page.
|
||||
|
||||
</specifics>
|
||||
|
||||
<deferred>
|
||||
## Deferred Ideas
|
||||
|
||||
- CSV export of subscribers. It waits for a framework export or toolbar capability; the user noted the framework doesn't have it ready.
|
||||
- Composing and sending newsletters, templates and the campaign CLI, ported from the PHP plugin in a later phase. That phase also merges confirmed subscribers with the user-based audience.
|
||||
- Renaming `fonoteka.go` to `sm-fonoteka-app` is a separate task, outside this phase.
|
||||
- A source repo link on the landing page, once the repos are public.
|
||||
- Translating the docs into Polish.
|
||||
|
||||
### Reviewed Todos (not folded)
|
||||
- "Nest framework packages under modules/" is already delivered by Phase 10.2, and the match was keyword noise.
|
||||
- "Backend admin personal API tokens" is unrelated to the public site.
|
||||
- "Extend fetchguard into a guarded outbound http.Client" is unrelated, because the site makes no outbound fetches.
|
||||
|
||||
</deferred>
|
||||
|
||||
---
|
||||
|
||||
*Phase: 11.2-ready-to-share-summercms-io-website-and-newsletter-plugin*
|
||||
*Context gathered: 2026-09-29*
|
||||
@@ -0,0 +1,93 @@
|
||||
# Phase 11.2: Ready to share: summercms.io website and newsletter plugin - Discussion Log
|
||||
|
||||
> **Audit trail only.** Do not use as input to planning, research, or execution agents.
|
||||
> Decisions are captured in CONTEXT.md — this log preserves the alternatives considered.
|
||||
|
||||
**Date:** 2026-09-29
|
||||
**Phase:** 11.2-ready-to-share-summercms-io-website-and-newsletter-plugin
|
||||
**Areas discussed:** Website build & hosting, Landing page content, Signup & opt-in flow, Plugin shape & future
|
||||
|
||||
---
|
||||
|
||||
## Website build & hosting
|
||||
|
||||
| Option | Description | Selected |
|
||||
|--------|-------------|----------|
|
||||
| Prerendered (vite-ssg) | Vite + Vue with build-time prerendering | |
|
||||
| Nuxt 4 (static generate) | Same stack as vue-fonoteka-app, `nuxt generate` | ✓ |
|
||||
| Plain Vue SPA | Empty HTML shell until JS runs | |
|
||||
|
||||
| Option | Description | Selected |
|
||||
|--------|-------------|----------|
|
||||
| Embedded in the Go binary | go:embed site + docs, one binary | ✓ |
|
||||
| Served from disk by the binary | dist/ next to the binary | |
|
||||
| Static host/CDN + API binary | Separate hosting, needs CORS | |
|
||||
|
||||
| Option | Description | Selected |
|
||||
|--------|-------------|----------|
|
||||
| English only | Docs are English | |
|
||||
| English + Polish | vue-i18n + phrasebook for site and mail | ✓ |
|
||||
|
||||
| Option | Description | Selected |
|
||||
|--------|-------------|----------|
|
||||
| New framework module | Reusable public static handler in summercms.go | |
|
||||
| In sm-summercms-app only | App-local handler | |
|
||||
| You decide | Research picks | |
|
||||
|
||||
**User's choice:** Nuxt 4 static generate, embedded in the binary, EN + PL. Serving: "we do as we did in wintercms - sm-summercms-plugin that does all the stuff around serving data. this is proven approach, used in figs.org.pl, golem15.com etc".
|
||||
**Notes:** The user first asked what "serving code" meant. After the explanation they chose a site plugin, which added a fourth repo.
|
||||
|
||||
---
|
||||
|
||||
## Landing page content
|
||||
|
||||
| Option | Description | Selected |
|
||||
|--------|-------------|----------|
|
||||
| Hero + signup | Pitch with signup in the hero | ✓ |
|
||||
| What it is / features | Feature list | ✓ |
|
||||
| Coming from WinterCMS | Side-by-side, links to the 11.1 concept map | ✓ |
|
||||
| Status & roadmap | Honest pre-release block | ✓ |
|
||||
|
||||
**User's choice:** All four. Notes on the WinterCMS section: "nice if it would have some cool way that it started in October (OctoberCMS) went trough WinterCMS but now it's time for Summer".
|
||||
**Other answers:** status tone was clear pre-release (the recommended option); no repo link yet (recommended); "Built by Golem15" credit (recommended).
|
||||
|
||||
---
|
||||
|
||||
## Signup & opt-in flow
|
||||
|
||||
| Question | Options | Selected |
|
||||
|----------|---------|----------|
|
||||
| Spam guard | Honeypot + rate limit / Cloudflare Turnstile | Honeypot + rate limit |
|
||||
| Consent | Required checkbox / Notice text only | Required checkbox |
|
||||
| Re-signup | Same neutral response / Tell the user their status | Same neutral response |
|
||||
| After confirm | Thank-you page + welcome mail / Thank-you page only | Thank-you page + welcome mail |
|
||||
|
||||
---
|
||||
|
||||
## Plugin shape & future
|
||||
|
||||
| Question | Options | Selected |
|
||||
|----------|---------|----------|
|
||||
| Users | Standalone subscribers / Optional link to users | Standalone |
|
||||
| Schema | Subscribers only, sending-ready / Port all PHP tables now | Subscribers only, sending-ready |
|
||||
| Admin | List+filter+delete / Also CSV export / Also manual add/edit | Free text: "add/edit/delete are must be, csv export - i don't think we have that ready within framework so it waits." |
|
||||
| Module path | git.golem15.com / github.com/golem15com | git.golem15.com |
|
||||
| Admin add status | Pending + confirmation mail / Admin chooses | Pending + confirmation mail |
|
||||
|
||||
**Notes:** The first question batch in this area failed in the UI and was asked again.
|
||||
|
||||
---
|
||||
|
||||
## Claude's Discretion
|
||||
|
||||
- How long tokens stay valid, the resend throttle, the token format and the URL scheme.
|
||||
- Visual design within the Direction C v2 brand, and the copy.
|
||||
- Whether the plugin or the root app embeds the Nuxt build, and how the docs are mounted.
|
||||
|
||||
## Deferred Ideas
|
||||
|
||||
- CSV export of subscribers, which needs framework support.
|
||||
- Sending newsletters and merging audiences, in a later phase.
|
||||
- Renaming fonoteka.go to sm-fonoteka-app.
|
||||
- A source repo link on the landing page.
|
||||
- Polish docs.
|
||||
Reference in New Issue
Block a user