Add template-as-code sync (campaigns template NAME PATH)
Answers "can the unsubscribe template be added via git+API too": commit listmonk's own stock campaign template (already unsubscribe-capable) as email-templates/campaign.html, add a small find-or-create-or-update Template client (confirmed against knadh/listmonk's actual model/handlers), a new `template` subcommand, and a manual-dispatch-only workflow to push it in as the default campaign template. Unlike campaign sync there's no draft/live status to protect, so this is always a safe overwrite.
This commit is contained in:
@@ -10,6 +10,8 @@ campaigns/
|
||||
campaign.md
|
||||
assets/
|
||||
one-pager.pdf
|
||||
email-templates/
|
||||
campaign.html
|
||||
```
|
||||
|
||||
Each campaign is a directory under `campaigns/`, named for its slug. **The directory name is the campaign's identity** — it's sent to listmonk as the campaign's `name` and is what `sync`/`send`/`test` all look it up by. There's no separate `name:`/`slug:` field in frontmatter to keep in sync with the directory; renaming the directory creates a new campaign in listmonk rather than renaming the existing one.
|
||||
@@ -88,18 +90,22 @@ A rejected campaign (check the CI run for `rejected:`) is usually one of these,
|
||||
|
||||
One bad campaign in the push doesn't block the others — check `rejected:` in the sync job's log for which ones and why.
|
||||
|
||||
## Before your first real send
|
||||
## Email template
|
||||
|
||||
listmonk's only currently-provisioned email template (`eec-passthrough`) is a bare passthrough used for `eec`'s transactional course emails — no unsubscribe footer, no branding. Create (or confirm) a real campaign template with a proper unsubscribe link in the listmonk admin UI before running your first `send`; drafts and previews render fine without one, but a real send without it isn't compliant.
|
||||
`campaigns template NAME PATH` pushes an HTML file's content into listmonk as a named template — create if missing, overwrite in place if it already exists, always set as the default campaign template (`is_default: true`) so every campaign created here uses it with no per-campaign `template_id` wiring. Unlike a campaign, a template has no draft/live status to protect, so this is always a safe create-or-update.
|
||||
|
||||
`email-templates/campaign.html` in this repo is listmonk's own stock campaign template (`static/email-templates/default.tpl` upstream), committed verbatim as a known-good starting point rather than something invented from scratch — it already has a real `{{ UnsubscribeURL }}` footer link and the required `{{ template "content" . }}` injection point. Restyle it freely; those two tags are the only load-bearing parts.
|
||||
|
||||
Run once via the `Sync default campaign template` workflow (`workflow_dispatch`, manual — changing the default template affects every future send, so it isn't wired to auto-run on push) before your first real `send`. `eec`'s own provisioned template (`eec-passthrough`) is a separate, bare passthrough used only for its transactional course emails — unrelated to this one.
|
||||
|
||||
## Open items
|
||||
|
||||
Every request shape in `internal/listmonk` is now confirmed against `knadh/listmonk`'s actual Go source (not just its docs, which are incomplete on a few of these): the campaign create/update payload, its `media` field for attachments (listmonk's request/response asymmetry — requests send plain IDs under `media`, responses echo full objects back under the same key), create always defaulting to `draft` regardless of any caller-supplied status, the test-send endpoint's `subscribers` field, and the query-based bulk list action (`PUT /api/subscribers/query/lists`) segmentation uses. What's left is genuinely operational, not code:
|
||||
Every request shape in `internal/listmonk` is now confirmed against `knadh/listmonk`'s actual Go source (not just its docs, which are incomplete on a few of these): the campaign create/update payload, its `media` field for attachments (listmonk's request/response asymmetry — requests send plain IDs under `media`, responses echo full objects back under the same key), create always defaulting to `draft` regardless of any caller-supplied status, the test-send endpoint's `subscribers` field, the query-based bulk list action (`PUT /api/subscribers/query/lists`) segmentation uses, and the template API (`GET`/`POST /api/templates`, `PUT /api/templates/:id`, fields `name`/`type`/`body`/`is_default`). What's left is genuinely operational, not code:
|
||||
|
||||
- **A real, unsubscribe-capable campaign template** needs to exist in the live listmonk instance before a real send (see above) — a content/admin-UI task, not something this tool verifies for you.
|
||||
- **Running the `Sync default campaign template` workflow at least once** — the template file is committed, but nothing pushes it into listmonk until that workflow (or `campaigns template ...` locally) actually runs.
|
||||
- **Provisioning the dedicated listmonk API user and pasting its token into this repo's Gitea secrets** (see Deploying below) hasn't happened yet.
|
||||
- **Gitea Actions' tag-push and `workflow_dispatch` triggers** are standard, long-supported Actions syntax and the runner already successfully uses `uses: actions/checkout@v4` elsewhere in this org, so this should work as written — but it's still worth confirming the first time `send.yml` actually fires.
|
||||
- The source was read against `knadh/listmonk`'s `master` branch; if the live instance runs a substantially older or newer version, a quick diff against its own `cmd/campaigns.go`/`cmd/subscribers.go` is cheap insurance before the first real send.
|
||||
- The source was read against `knadh/listmonk`'s `master` branch; if the live instance runs a substantially older or newer version, a quick diff against its own `cmd/campaigns.go`/`cmd/subscribers.go`/`cmd/templates.go` is cheap insurance before the first real send.
|
||||
|
||||
## Deploying
|
||||
|
||||
|
||||
Reference in New Issue
Block a user