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>
This commit is contained in:
@@ -13,12 +13,15 @@ Implications to work through:
|
||||
"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.
|
||||
- [ ] 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. The CRM already has `tier` + `stack_notes` columns
|
||||
to record that decision per client.
|
||||
- [ ] Revisit the `Clients.tier` field semantics: keep A/B as an internal effort/price band, or
|
||||
- [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 = …`).
|
||||
- [ ] Onboarding form + workflow currently ask for Tier A/B — realign once the model is settled.
|
||||
- [ ] **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
|
||||
@@ -41,18 +44,54 @@ Follow-ups (not yet done):
|
||||
- [ ] 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). Confirm the owner account is strong; remove the
|
||||
dead env.
|
||||
(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.
|
||||
- [x] Booking-sync workflow — DONE, but not as originally scoped: the plan to split
|
||||
Tier-B-Cal.com-webhook / Tier-A-GCal-poll was superseded by the tier-decoupling above. Instead,
|
||||
one shared **Easy!Appointments** instance handles booking for every client (`deploy/booking/`),
|
||||
wired via `n8n/booking-sync.json` (webhook → `POST /api/bookings` → Telegram), and
|
||||
`n8n/onboarding.json` auto-provisions each new client's EA service+provider.
|
||||
|
||||
## Done
|
||||
- [x] 2026-06-25 — Latency fix (n8n executions 1–4 min → ~6 s; DNS + PostHog telemetry).
|
||||
@@ -67,3 +106,7 @@ Follow-ups (not yet done):
|
||||
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).
|
||||
|
||||
Reference in New Issue
Block a user