Files
smb-online/TODO.md
T
mivanchenko 94ae2d578d Add credentials store to the CRM, docs cleanup, deploy pipeline TODO
Adds a `credentials` entity to the back office (never mirrored to
Sheets, gated by the CRM token even to read) so client logins like
the auto-generated Easy!Appointments provider password can be viewed
and copied from the dashboard instead of getting lost — the actual
cause of the happynails password going missing. Onboarding now saves
that generated password instead of discarding it. Also adds
Documentation.md, brings README/TODO in line with the current
Postgres-first architecture, and tidies the backlog.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-15 14:13:34 +02:00

113 lines
8.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# TODO / Backlog
## Rethink the tier model — decouple tier from business type
**Goal:** tiers should NOT be tied to a kind of business. Which "tier" (and whether a
client's site is self-hosted or runs on Google's free services) is a decision made
**between me and the client**, case by case — not something baked into a demo.
Implications to work through:
- [x] **Demos are stack-agnostic starters.** ~~Drop tier/stack labels and jargon (self-hosted,
Cal.com, Google-Stack, "≈ 0 €") from the public-facing demos & chooser.~~ DONE 2026-06-25:
chooser reworded; all three demo pages neutralised — demo bars now read "Beispielseite · So
könnte Ihr Online-Auftritt aussehen", booking widgets relabelled to generic
"Online-Terminbuchung"/"Sichere Buchung", Google-Kalender/Cal.com/self-hosted/Tier labels
removed from rendered copy *and* HTML comments, demo booking dates now render on the current
week via JS so nothing looks stale.
- [x] After a client signs, **I decide hosting**: self-host the page on the homelab, or stand it
up on the client's free Google account. Already how it works in practice — the CRM has
`tier` + `stack_notes` columns recording that per-client decision, and every deploy this
session (happynails' credential, the client stacks in `deploy/clients/`) went through that
path rather than a fixed per-tier template. Nothing left to build here.
- [ ] **Needs your call:** Revisit the `Clients.tier` field semantics — keep A/B as an internal effort/price band, or
replace with something hosting-agnostic (e.g. `hosting = google | selfhosted`, `plan = …`).
- [ ] **Blocked on the above:** Onboarding form + workflow currently ask for Tier A/B — realign
once the field semantics are settled.
## Onboard dashboard → manage Leads & Clients (CRUD) — DONE 2026-06-25
Built as a DB-first back-office (architecture pivot: **Postgres is the source of truth**, Sheets
is a one-way mirror — see `backoffice/` and the `smb-db-source-of-truth` memory). Live at
**onboard.mivanchenko.de/crm** behind the existing basic-auth.
- [x] **Read:** Leads & Clients tables, served by `smb-crm` straight from Postgres.
- [x] **Add:** "+ Neu" modal per entity; auto-IDs (C-#### / L-epoch), renewal auto-compute.
- [x] **Edit:** per-row ✎ modal, token-protected PATCH.
- [x] **Delete:** per-row 🗑 (token-protected), audit-logged — replaces the scratchpad cleanup.
- [x] Behind Caddy basic-auth; mutations gated by `CRM_API_TOKEN`; all changes audit-logged.
- [x] One-way **DB→Sheets mirror** keeps the Sheet overview current after every change.
- [x] **n8n ingest swap:** `lead-intake` & `onboarding` now POST into the DB API (not Sheets);
Telegram notify unchanged. `renewal-reminder` still reads Sheets (fine — Sheets mirrors DB).
Follow-ups (not yet done):
- [ ] Extend CRUD UI beyond Leads/Clients (projects, bookings, invoices, activity) if useful.
Dashboard (`backoffice/app/static/index.html`) still only has Leads/Clients tabs.
- [x] Daily backup of the `smb-db` Postgres volume — DONE: `deploy/backup/smb-db-backup.sh`
(`pg_dump` + gzip, keeps 14 days, cron on the homelab).
- [ ] Make the DB→Sheets overwrite atomic (currently clear-then-write; brief mid-write window a
reader could catch the cleared sheet — harmless for a human overview).
## Credentials in the CRM — copy-to-clipboard — DONE 2026-07-15
**Goal:** be able to grab a client's login (e.g. their Easy!Appointments provider password) from
the CRM dashboard itself instead of digging through Vaultwarden or a live n8n execution log —
prompted by not being able to find the happynails booking password anywhere. New `credentials`
entity (`backoffice/db/init.sql`, `backoffice/app/db.py`), **deliberately never mirrored to
Sheets** (`mirror: False`, enforced in `mirror_async`/`mirror_entity`/`/api/sync` and skipped by
`import_from_sheets.py`) and gated by `X-CRM-Token` even to read. Dashboard has a new
**Credentials** tab with a masked value, "👁 zeigen" reveal toggle, "📋 Kopieren" copy button.
`n8n/onboarding.json` now saves the auto-generated EA provider password instead of discarding it
(root cause of the happynails password being unrecoverable — it was never persisted anywhere).
Migrated + shipped to the live homelab (`smb-db` schema, `smb-crm` rebuilt, confirmed healthy).
Happynails' (`C-0002`) reset EA password is recorded as `CR-1784116056728`.
Follow-ups (not yet done — all need you, not more code):
- [ ] Re-import the updated `n8n/onboarding.json` into the live n8n instance (the new "Save
credential" node). Needs the real `__CRM_TOKEN__`/`__EA_AUTH__` credential bindings re-attached
in the n8n UI — not safely scriptable from outside n8n.
- [ ] The happynails credential's `username` is still blank — confirm it in the EA admin UI and
fill it in via the dashboard's ✎ edit on the Credentials tab.
- [ ] Your call, no rush: encrypt `credentials.secret` at rest (e.g. pgcrypto) instead of
plaintext if the dashboard is ever exposed more broadly than Caddy basic-auth + host security.
## Deploy pipeline — trigger from the Gitea UI
**Goal:** stop hand-running scp/ssh/docker-compose-build for every change (that's how the
credentials feature above shipped). `git.mivanchenko.de` (Gitea, already the `origin` remote for
every repo here, running alongside the other homelab containers) supports **Gitea Actions**
workflows in `.gitea/workflows/` that run on a self-hosted runner and can be started with a
manual "Run workflow" button in the repo UI, not just on push.
- [ ] Register a Gitea Actions runner (can run directly on the homelab host, or in its own
container with the Docker socket mounted so it can `docker compose build`/`up -d`).
- [ ] `.gitea/workflows/deploy.yml`: `workflow_dispatch` trigger (the UI button), optionally also
on push to `main`. Steps: checkout → sync changed paths to `/home/mivanchenko/smb-crm/` (and
whichever other `deploy/*` groups changed) → apply any pending SQL migrations → `docker compose
build` + `up -d --no-deps <service>` for just the affected Compose group(s) → hit `/healthz`
(or the equivalent) to confirm before declaring success.
- [ ] Needs real secrets in Gitea's repo/runner secrets store (SSH deploy key or a runner already
on the host, `CRM_API_TOKEN`, etc.) — **your call** on which, since it's a homelab access
decision.
- [ ] Once trustworthy, this replaces the manual migration step described in the credentials
section above and the `new-client.sh` → scp → ssh flow in `deploy/clients/README.md`.
## Other
- [ ] Harden n8n auth: compose still has deprecated `N8N_BASIC_AUTH_*` with password `password`
(legacy/ignored by modern n8n owner-login). **Needs your call** — I can remove the dead env, but
whether the owner account itself is strong enough is a judgment only you can make.
- [ ] Orders: the pizzeria demo posts orders into the `Leads` tab via the lead-intake webhook as a
stop-gap. Build a proper **Orders** flow + sheet tab (items, total, mode, status) when the
order-management product is real.
## Done
- [x] 2026-06-25 — Latency fix (n8n executions 14 min → ~6 s; DNS + PostHog telemetry).
- [x] 2026-06-25 — Chooser reworded to client-friendly, jargon-free copy.
- [x] 2026-06-25 — Pizzeria demo with online ordering added.
- [x] 2026-06-25 — Barbershop "Google Kalender" dot-overlap styling fixed.
- [x] 2026-06-25 — All three demo pages neutralised (stack-agnostic, non-stale dates).
- [x] 2026-06-25 — DB-first back office: Postgres source of truth + Sheets mirror + CRUD + n8n
ingest swap (lead-intake/onboarding → DB API). Live at onboard.mivanchenko.de/crm.
- [x] 2026-07-14 — Shared self-hosted booking: Easy!Appointments stack (`deploy/booking/`),
auto-provisioning wired into the onboarding workflow, `booking-sync` workflow syncing bookings
to the CRM.
- [x] 2026-07-14 — Per-client deploy tooling: `deploy/clients/` isolated-stack model +
`new-client.sh` scaffolder; back-office updates.
- [x] 2026-07-14 — Booking-sync workflow, done differently than originally scoped: the planned
Tier-B-Cal.com-webhook / Tier-A-GCal-poll split was superseded by the tier-decoupling above —
one shared Easy!Appointments instance handles booking for every client instead.
- [x] 2026-07-15 — Credentials-in-the-CRM feature built and shipped live (see above).