274 lines
13 KiB
Markdown
274 lines
13 KiB
Markdown
# Demo Email Domain Implementation Plan
|
|
|
|
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
|
|
**Goal:** Standardize demo accounts on `@agendamax.demo`, make every demo login explicit in the README, and migrate local and Coolify data without deleting records.
|
|
|
|
**Architecture:** Keep the current simple demo authentication and replace only the two platform-owned demo email constants plus their test fixtures. Apply one targeted, idempotent SQLite `UPDATE` to the exact existing admin and owner rows; appointments, businesses, and related records remain unchanged. Deploy the source through the existing GitHub mirror and verify the live app through health and login endpoints.
|
|
|
|
**Tech Stack:** React/Vite, Express, TypeScript, Node 22 `node:sqlite`, SQLite, PowerShell/OpenSSH, Coolify v4.1.2.
|
|
|
|
## Global Constraints
|
|
|
|
- Keep the demo password exactly `demo1234`.
|
|
- Replace active `@agendapro.demo` references with `@agendamax.demo`; retain the old domain only in explicit negative-login assertions or migration-history documentation.
|
|
- Do not reset or delete local or production business, appointment, client, employee, ticket, or review data.
|
|
- Never commit `.env.local.ps1` or print token values.
|
|
- Production deployment must finish before the production data migration is run.
|
|
- If production deployment fails, stop before changing the production SQLite database and use the documented rollback path.
|
|
|
|
---
|
|
|
|
### Task 1: Update Demo Identity References and Access Documentation
|
|
|
|
**Files:**
|
|
- Modify: `README.md:66-82` — show email and password for every demo account.
|
|
- Modify: `server/index.ts:38-43` — use the new default owner email.
|
|
- Modify: `server/scripts/seed.ts:315-334` — use the new admin and owner emails for fresh databases.
|
|
- Modify: `src/pages/LoginPage.tsx:8-15` — use the new default development login.
|
|
- Modify: `admin-test.mjs`, `e2e-test.mjs`, `e2e-full.mjs`, `server/scripts/booking-e2e.mjs`, `visual-audit.mjs`, and any other active file returned by `rg -l 'agendapro\.demo'` — keep automated login fixtures aligned.
|
|
- Modify: active plan/report documents containing executable login examples; keep old-domain examples only as migration-history documentation or explicit negative-login assertions.
|
|
|
|
**Interfaces:**
|
|
- Fresh seed continues to create users with the existing schema and password.
|
|
- The login page continues to load `/api/auth/demo-users` and quick-login users; only the initial email constant changes.
|
|
|
|
- [ ] **Step 1: Record the current active references and credentials**
|
|
|
|
Run:
|
|
|
|
```powershell
|
|
rg -n --glob '!node_modules' --glob '!data/**' 'agendapro\.demo|demo1234' .
|
|
```
|
|
|
|
Expected: references are limited to the known seed, login defaults, tests, README, explicit negative-login assertions, and migration-history documentation; no secret token values are printed.
|
|
|
|
- [ ] **Step 2: Update the README credentials table first**
|
|
|
|
The resulting table must explicitly show:
|
|
|
|
```markdown
|
|
| **Admin**| `[email protected]` | `demo1234` | Administrador |
|
|
| Dueño | `[email protected]` | `demo1234` | Daniela Reyes |
|
|
```
|
|
|
|
Every employee row must also include the same password in its password column. Keep the note that the login screen offers quick access.
|
|
|
|
- [ ] **Step 3: Update fresh-seed and UI constants**
|
|
|
|
Use these exact values:
|
|
|
|
```ts
|
|
// server/index.ts and server/scripts/seed.ts
|
|
ownerEmail: "[email protected]"
|
|
|
|
// server/scripts/seed.ts
|
|
VALUES (NULL, '[email protected]', 'demo1234', 'Administrador', 'admin', '#0f172a')
|
|
|
|
// src/pages/LoginPage.tsx
|
|
useState(DEMO ? "[email protected]" : "")
|
|
```
|
|
|
|
- [ ] **Step 4: Update all active automated fixtures**
|
|
|
|
Replace only the email domain in existing login assertions and fixtures. Do not change employee domains or passwords.
|
|
|
|
- [ ] **Step 5: Verify the source tree has no stale active domain**
|
|
|
|
Run:
|
|
|
|
```powershell
|
|
rg -n --glob '!node_modules' --glob '!data/**' --glob '!docs/superpowers/specs/**' 'agendapro\.demo' .
|
|
```
|
|
|
|
Expected: no output from application code or positive fixtures; the intentional old-domain negative-login assertions and migration-history documents are the only allowed matches.
|
|
|
|
### Task 2: Migrate the Local SQLite Demo Users
|
|
|
|
**Files:**
|
|
- Modify: local ignored database `data/agendapro.db` only; no tracked database files.
|
|
|
|
**Interfaces:**
|
|
- Uses the existing `server/db.ts` connection and `users` table.
|
|
- Produces updated exact admin/owner email rows while preserving all row counts outside `users.email`.
|
|
|
|
- [ ] **Step 1: Confirm the local database and take a count snapshot**
|
|
|
|
Run from the repository root:
|
|
|
|
```powershell
|
|
Test-Path -LiteralPath .\data\agendapro.db
|
|
node --import tsx --input-type=module -e "import { db } from './server/db.ts'; console.log(JSON.stringify({users: db.prepare('SELECT id,email,role FROM users ORDER BY id').all(), appointments: db.prepare('SELECT COUNT(*) AS c FROM appointments').get(), clients: db.prepare('SELECT COUNT(*) AS c FROM clients').get()}));"
|
|
```
|
|
|
|
Expected: the local database exists or the command reports that a fresh seed is required; existing appointment/client counts are recorded before mutation.
|
|
|
|
Before updating, inspect the exact old/new admin and owner rows from the `users`
|
|
snapshot. For either role, if its target email already exists while its matching
|
|
old-domain row also exists, stop and resolve that unique-email collision instead
|
|
of running the update. If no old-domain rows exist and the new rows are present,
|
|
record a safe no-op and skip the update.
|
|
|
|
- [ ] **Step 2: Apply the idempotent local email update**
|
|
|
|
Run:
|
|
|
|
```powershell
|
|
node --% --import tsx --input-type=module -e "import { db } from './server/db.ts'; const r = db.prepare(\"UPDATE users SET email = CASE email WHEN '[email protected]' THEN '[email protected]' WHEN '[email protected]' THEN '[email protected]' END WHERE (email = '[email protected]' AND role = 'admin') OR (email = '[email protected]' AND role = 'owner')\").run(); console.log(JSON.stringify(r));"
|
|
```
|
|
|
|
Expected: only the exact email/role pairs are changed; an old-domain employee or
|
|
other role is untouched. Running the same command again reports zero additional
|
|
changes.
|
|
|
|
- [ ] **Step 3: Verify local credentials and preservation**
|
|
|
|
Run:
|
|
|
|
```powershell
|
|
node --% --import tsx --input-type=module -e "import { db } from './server/db.ts'; console.log(JSON.stringify({demo: db.prepare(\"SELECT email,role,password FROM users WHERE email IN ('[email protected]','[email protected]') ORDER BY role\").all(), appointments: db.prepare('SELECT COUNT(*) AS c FROM appointments').get(), clients: db.prepare('SELECT COUNT(*) AS c FROM clients').get()}));"
|
|
```
|
|
|
|
Expected: `[email protected]` and `[email protected]` use `demo1234`; appointment and client counts match the snapshot.
|
|
|
|
### Task 3: Run Local Regression Checks
|
|
|
|
**Files:**
|
|
- No additional files; validate Task 1 and Task 2 together.
|
|
|
|
**Interfaces:**
|
|
- Existing package scripts are the regression contract: typecheck, build, unit tests, and API tests.
|
|
|
|
- [ ] **Step 1: Run typecheck and build**
|
|
|
|
```powershell
|
|
npm run typecheck
|
|
npm run build
|
|
```
|
|
|
|
Expected: both commands exit with code 0.
|
|
|
|
- [ ] **Step 2: Run unit and admin/API tests**
|
|
|
|
```powershell
|
|
npm run test:unit
|
|
npm run test:admin
|
|
npm run test:e2e
|
|
```
|
|
|
|
Expected: typecheck, build, unit, admin, and non-slot authentication checks pass. On the
|
|
already-migrated local database, `businesses.working_hours` and employee
|
|
`working_hours` are null, so the slot-dependent e2e/booking assertions may fail
|
|
before booking (including their existing downstream dereference); record this
|
|
pre-existing limitation as unrelated to the email change and do not change
|
|
scheduling data for this task.
|
|
|
|
- [ ] **Step 3: Inspect the final diff**
|
|
|
|
```powershell
|
|
git diff --check
|
|
git status --short
|
|
```
|
|
|
|
Expected: only intended source, test, README, and specification/plan files are changed; `.env.local.ps1` and `data/agendapro.db` remain ignored.
|
|
|
|
### Task 4: Deploy AgendaMax Through Coolify
|
|
|
|
**Files:**
|
|
- Modify: the tracked AgendaPro source files from Tasks 1-3 through a normal commit.
|
|
- Do not modify: `.env.local.ps1`, local SQLite files, or Coolify credentials in Git.
|
|
|
|
**Interfaces:**
|
|
- Source mirror: the existing GitHub remote used by Coolify (`urieljarethbusiness-cpu/agendamax`).
|
|
- Coolify application: UUID `s30f7egdlkx4wyjp59o1iunc`, FQDN `https://agendamax.urieljareth.org`.
|
|
|
|
- [ ] **Step 1: Commit only the intended tracked changes**
|
|
|
|
```powershell
|
|
git add README.md server/index.ts server/scripts/seed.ts src/pages/LoginPage.tsx admin-test.mjs e2e-test.mjs e2e-full.mjs server/scripts/booking-e2e.mjs visual-audit.mjs docs/superpowers/specs/2026-07-27-demo-email-domain-design.md docs/superpowers/plans/2026-07-27-demo-email-domain.md
|
|
git diff --cached --check
|
|
git commit -m "docs: standardize AgendaMax demo access"
|
|
```
|
|
|
|
Add any additional active fixture files found by Task 1 explicitly; never use `git add .`.
|
|
|
|
- [ ] **Step 2: Push the source mirror used by Coolify**
|
|
|
|
```powershell
|
|
git push github main
|
|
```
|
|
|
|
Expected: the new commit is available in the GitHub mirror without exposing credentials in output.
|
|
|
|
- [ ] **Step 3: Trigger and monitor the Coolify deploy**
|
|
|
|
```powershell
|
|
. .\.env.local.ps1
|
|
$h = @{ Authorization = "Bearer $env:COOLIFY_TOKEN" }
|
|
$result = Invoke-RestMethod "$env:COOLIFY_API_URL/deploy?uuid=s30f7egdlkx4wyjp59o1iunc&force=true" -Headers $h
|
|
$deployment = $result.deployments[0].deployment_uuid
|
|
Invoke-RestMethod "$env:COOLIFY_API_URL/deployments/$deployment" -Headers $h
|
|
```
|
|
|
|
Poll until the status is `finished`; do not run the production SQL migration while it is queued, in progress, or failed.
|
|
|
|
### Task 5: Migrate and Verify Production Data
|
|
|
|
**Files:**
|
|
- Modify: persistent SQLite database inside the running AgendaMax Coolify app container only.
|
|
- Do not modify: source files or unrelated production tables.
|
|
|
|
**Interfaces:**
|
|
- Production app data path: `/app/data/agendapro.db`.
|
|
- Proxmox access: host `192.168.0.200`, LXC `102`; use the existing `scripts/Invoke-ProxmoxSsh.ps1`/`ProxmoxAgent.ps1` helpers and environment variables.
|
|
|
|
- [ ] **Step 1: Identify the running AgendaMax app container and verify the old rows**
|
|
|
|
Use the existing Proxmox SSH chain to run inside LXC 102:
|
|
|
|
```sh
|
|
container=$(docker ps --format '{{.ID}} {{.Names}}' | awk '$2 ~ /agendamax|s30f7/ {print $1; exit}')
|
|
test -n "$container" || { echo 'AgendaMax container not found' >&2; exit 1; }
|
|
docker exec "$container" node -e "const {DatabaseSync}=require('node:sqlite'); const db=new DatabaseSync('/app/data/agendapro.db'); console.log(db.prepare(\"SELECT id,email,role FROM users WHERE email IN ('[email protected]','[email protected]','[email protected]','[email protected]') ORDER BY id\").all())"
|
|
```
|
|
|
|
Expected: the app is running the new image and the current exact admin/owner
|
|
rows are visible before mutation. If an old row and its matching new row both
|
|
exist, stop and resolve the unique-email collision before updating.
|
|
|
|
- [ ] **Step 2: Apply the production email update**
|
|
|
|
```sh
|
|
docker exec "$container" node -e "const {DatabaseSync}=require('node:sqlite'); const db=new DatabaseSync('/app/data/agendapro.db'); const r=db.prepare(\"UPDATE users SET email=CASE email WHEN '[email protected]' THEN '[email protected]' WHEN '[email protected]' THEN '[email protected]' END WHERE (email='[email protected]' AND role='admin') OR (email='[email protected]' AND role='owner')\").run(); console.log(r)"
|
|
```
|
|
|
|
Expected: only the matching admin and owner rows move to `@agendamax.demo`; no
|
|
old-domain employee or other role is rewritten, and no business or appointment
|
|
rows are deleted.
|
|
|
|
If the preflight query shows an old row alongside its matching new row, stop and
|
|
resolve the unique-email collision. If it shows no old-domain rows because the
|
|
deployment seeded a fresh database with the new domain, record the update as a
|
|
safe no-op and skip it.
|
|
|
|
- [ ] **Step 3: Verify live health and credentials**
|
|
|
|
```powershell
|
|
$health = Invoke-RestMethod https://agendamax.urieljareth.org/api/health
|
|
$admin = Invoke-RestMethod https://agendamax.urieljareth.org/api/auth/login -Method Post -ContentType 'application/json' -Body '{"email":"[email protected]","password":"demo1234"}'
|
|
$owner = Invoke-RestMethod https://agendamax.urieljareth.org/api/auth/login -Method Post -ContentType 'application/json' -Body '{"email":"[email protected]","password":"demo1234"}'
|
|
Write-Host "health_ok=$($health.ok) admin_role=$($admin.user.role) admin_token_present=$([bool]$admin.token) owner_role=$($owner.user.role) owner_token_present=$([bool]$owner.token)"
|
|
```
|
|
|
|
Expected: health returns `ok: true`; both logins return the correct roles. Only boolean token-presence values are printed, never token contents.
|
|
|
|
- [ ] **Step 4: Verify the deployed page and repository state**
|
|
|
|
Run the existing browser/HTTP post-deploy check if available, then:
|
|
|
|
```powershell
|
|
git status --short
|
|
```
|
|
|
|
Expected: the deployed AgendaMax page loads, no uncaught frontend errors are reported, and only intentional local follow-up changes remain.
|