Skip to content

Commit e300ad6

Browse files
authored
Merge pull request #2 from Brohammad/cursor/fix-production-hardening-issues
Production readiness: security, E2E, checklist, docs
2 parents df93fd2 + 1c7b031 commit e300ad6

82 files changed

Lines changed: 5491 additions & 1547 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
name: Bug report
2+
description: Something broken in the decision engine, API, or deploy
3+
title: "[bug] "
4+
labels: ["bug"]
5+
body:
6+
- type: markdown
7+
attributes:
8+
value: |
9+
Thanks for filing a bug. For security vulnerabilities, use [SECURITY.md](../../SECURITY.md) instead.
10+
- type: textarea
11+
id: summary
12+
attributes:
13+
label: Summary
14+
description: What went wrong in one or two sentences?
15+
validations:
16+
required: true
17+
- type: textarea
18+
id: steps
19+
attributes:
20+
label: Steps to reproduce
21+
placeholder: |
22+
1. Sign in…
23+
2. Upload a photo…
24+
3. …
25+
validations:
26+
required: true
27+
- type: textarea
28+
id: expected
29+
attributes:
30+
label: Expected behavior
31+
validations:
32+
required: true
33+
- type: textarea
34+
id: actual
35+
attributes:
36+
label: Actual behavior
37+
validations:
38+
required: true
39+
- type: input
40+
id: environment
41+
attributes:
42+
label: Environment
43+
description: "e.g. paintcrm.brohammad.tech, Docker Compose, local Node 20"
44+
validations:
45+
required: true
46+
- type: textarea
47+
id: logs
48+
attributes:
49+
label: Logs / screenshots
50+
description: Redact tokens and PII.

.github/ISSUE_TEMPLATE/config.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
blank_issues_enabled: false
2+
contact_links:
3+
- name: Security vulnerability
4+
url: https://github.com/Brohammad/PaintCRM/blob/main/SECURITY.md
5+
about: Report security issues privately — do not file a public issue.
6+
- name: Improvement backlog
7+
url: https://github.com/Brohammad/PaintCRM/blob/main/BACKLOG.md
8+
about: See prioritized P0–P3 work before opening a duplicate request.
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
name: Feature request
2+
description: Propose a high-impact product or engineering improvement
3+
title: "[feat] "
4+
labels: ["enhancement"]
5+
body:
6+
- type: markdown
7+
attributes:
8+
value: |
9+
Prefer items that strengthen engineering quality, UX, reliability, security, or startup readiness.
10+
Check [BACKLOG.md](../../BACKLOG.md) first.
11+
- type: textarea
12+
id: problem
13+
attributes:
14+
label: Problem
15+
description: Who hurts today, and how?
16+
validations:
17+
required: true
18+
- type: textarea
19+
id: proposal
20+
attributes:
21+
label: Proposed change
22+
description: Smallest useful version.
23+
validations:
24+
required: true
25+
- type: dropdown
26+
id: impact
27+
attributes:
28+
label: Primary impact area
29+
options:
30+
- Product / UX
31+
- Reliability
32+
- Security
33+
- Performance
34+
- Developer experience
35+
- Deployment / ops
36+
- Open-source readiness
37+
validations:
38+
required: true
39+
- type: textarea
40+
id: why
41+
attributes:
42+
label: Why this is objectively stronger
43+
description: Would a dealer, recruiter, or maintainer care? Why?
44+
validations:
45+
required: true

.github/PULL_REQUEST_TEMPLATE.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
## Summary
2+
3+
<!-- 1–3 bullets: what changed and why -->
4+
5+
## Impact
6+
7+
- [ ] Engineering quality
8+
- [ ] Product / UX
9+
- [ ] Reliability
10+
- [ ] Security
11+
- [ ] Performance
12+
- [ ] Deployment / DX
13+
- [ ] Docs / OSS
14+
15+
## Test plan
16+
17+
- [ ] `cd server && npm test`
18+
- [ ] `cd paint-preview-app && npm test`
19+
- [ ] Manual check (describe):
20+
21+
## Notes
22+
23+
<!-- Breaking changes, follow-ups, BACKLOG IDs -->

.github/workflows/ci.yml

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,10 +113,107 @@ jobs:
113113
working-directory: ./paint-preview-app
114114
run: npm run build
115115

116+
e2e:
117+
name: E2E (Playwright)
118+
runs-on: ubuntu-latest
119+
needs: [test, frontend]
120+
services:
121+
postgres:
122+
image: postgres:16-alpine
123+
env:
124+
POSTGRES_USER: ${{ env.POSTGRES_USER }}
125+
POSTGRES_PASSWORD: ${{ env.POSTGRES_PASSWORD }}
126+
POSTGRES_DB: ${{ env.POSTGRES_DB }}
127+
options: >-
128+
--health-cmd pg_isready
129+
--health-interval 10s
130+
--health-timeout 5s
131+
--health-retries 5
132+
ports:
133+
- 5432:5432
134+
135+
steps:
136+
- uses: actions/checkout@v4
137+
138+
- name: Setup Node.js
139+
uses: actions/setup-node@v4
140+
with:
141+
node-version: ${{ env.NODE_VERSION }}
142+
cache: 'npm'
143+
cache-dependency-path: |
144+
server/package-lock.json
145+
paint-preview-app/package-lock.json
146+
e2e/package-lock.json
147+
148+
- name: Install server dependencies
149+
working-directory: ./server
150+
run: npm ci
151+
152+
- name: Run database migrations
153+
working-directory: ./server
154+
env:
155+
DATABASE_URL: postgresql://${{ env.POSTGRES_USER }}:${{ env.POSTGRES_PASSWORD }}@localhost:5432/${{ env.POSTGRES_DB }}
156+
run: npm run migrate:up
157+
158+
- name: Install and build frontend
159+
working-directory: ./paint-preview-app
160+
run: |
161+
npm ci
162+
npm run build
163+
164+
- name: Start server
165+
working-directory: ./server
166+
env:
167+
NODE_ENV: test
168+
PORT: 3001
169+
DATABASE_URL: postgresql://${{ env.POSTGRES_USER }}:${{ env.POSTGRES_PASSWORD }}@localhost:5432/${{ env.POSTGRES_DB }}
170+
JWT_SECRET: test-secret-for-ci-at-least-32-chars-long
171+
ENABLE_RATE_LIMITING: 'false'
172+
run: |
173+
node index.js &
174+
echo $! > ../e2e/server.pid
175+
for i in $(seq 1 60); do
176+
if curl -sf http://localhost:3001/api/health > /dev/null; then
177+
echo "Server is ready"
178+
exit 0
179+
fi
180+
sleep 2
181+
done
182+
echo "Server failed to start"
183+
exit 1
184+
185+
- name: Install Playwright dependencies
186+
working-directory: ./e2e
187+
run: |
188+
npm ci
189+
npx playwright install --with-deps chromium
190+
191+
- name: Run Playwright tests
192+
working-directory: ./e2e
193+
env:
194+
CI: true
195+
PLAYWRIGHT_BASE_URL: http://localhost:3001
196+
run: npm test
197+
198+
- name: Upload Playwright report
199+
if: failure()
200+
uses: actions/upload-artifact@v4
201+
with:
202+
name: playwright-report
203+
path: e2e/playwright-report/
204+
retention-days: 7
205+
206+
- name: Stop server
207+
if: always()
208+
run: |
209+
if [ -f e2e/server.pid ]; then
210+
kill "$(cat e2e/server.pid)" || true
211+
fi
212+
116213
build:
117214
name: Build Docker Image
118215
runs-on: ubuntu-latest
119-
needs: [lint, test, frontend]
216+
needs: [lint, test, frontend, e2e]
120217
if: github.ref == 'refs/heads/main'
121218

122219
steps:

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ server/node_modules/
22
server/coverage/
33
paint-preview-app/node_modules/
44
paint-preview-app/dist/
5+
e2e/node_modules/
6+
e2e/playwright-report/
7+
e2e/test-results/
8+
e2e/server.pid
59
server/paintcrm.db
610
server/paintcrm.db-shm
711
server/paintcrm.db-wal

ARCHITECTURE.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1205,12 +1205,13 @@ Intersection is preferred: DeepLab's "wall" semantic label is precise, and the h
12051205
|---------|---------------|
12061206
| Password storage | bcrypt, cost factor 12 (~150ms per hash — increased for production) |
12071207
| Token transport | Bearer JWT in `Authorization` header (never in URL or cookie) |
1208-
| Token TTL | 30 days; refresh tokens in roadmap for Phase 6 |
1208+
| Token TTL | Access JWT ~15m (`ACCESS_TOKEN_TTL`); rotating refresh ~30d (`REFRESH_TOKEN_TTL_DAYS`), SHA-256 hashed at rest with reuse detection |
1209+
| Password reset | Single-use SHA-256 tokens (`password_reset_tokens`), TTL via `PASSWORD_RESET_TTL_MINUTES`; generic API responses; SMTP optional (see `lib/mail.js`) |
12091210
| JWT secret | Environment variable `JWT_SECRET` validated at startup; refuses to start if missing in production |
12101211
| SQL injection | 100% parameterised queries via `pg` prepared statements; no string concatenation |
1211-
| Tenant isolation | Every leads/events query filters by `tenant_id = req.tenant.id` |
1212-
| CORS | Configurable via `ALLOWED_ORIGINS` env var; defaults to permissive only in development |
1213-
| Rate limiting | `express-rate-limit`: 100 req/15min per IP; auth endpoints stricter: 10 req/hour |
1212+
| Tenant isolation | Application-level: every tenant-owned query filters by `tenant_id = req.tenant.id` (RLS path documented in `docs/TENANCY.md`) |
1213+
| CORS | `ALLOWED_ORIGINS` allowlist (comma-separated). Production refuses to boot if unset and denies unlisted cross-origin requests; development remains permissive |
1214+
| Rate limiting | `express-rate-limit`: 100 req/15min per IP; auth + password-reset endpoints stricter: 10 req/hour |
12141215
| Security headers | Helmet.js: CSP, HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy |
12151216
| Input validation | Manual field validation in each route; `eventType` whitelisted against a `Set` |
12161217
| XSS protection | CSP directives restrict script sources; user content (snapshots) stored as base64, not rendered as HTML |

BACKLOG.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# PaintCRM — Prioritized Improvement Backlog
2+
3+
Last reviewed: **2026-07-16** (post P0/P1 completion cycle).
4+
5+
**Status: no open P0 or P1 items.** Remaining work is P2/P3 only.
6+
7+
Scoring: Impact / Difficulty / Risk = Low · Med · High
8+
9+
---
10+
11+
## Completed this cycle (closed P0 / P1)
12+
13+
| ID | Item | Outcome |
14+
|----|------|---------|
15+
| P0-1 | Production CORS harden | Done |
16+
| P0-2 | Docs honesty | Done |
17+
| P0-3 | OSS scaffolding (LICENSE, SECURITY, CONTRIBUTING, templates) | Done |
18+
| P0-4 | Finish/hide WIP (AI palette, reminders, cron honesty) | Done |
19+
| P0-5 | Tenant filter defense-in-depth | Done |
20+
| P1-1 | Frontend view extraction (customers, quotes, inventory, ledger) | Done |
21+
| P1-2 | Playwright E2E in CI | Done |
22+
| P1-3 | Password reset | Done |
23+
| P1-4 | Tenancy audit + docs + RLS path | Done (docs; RLS deferred P2) |
24+
| P1-5 | Pure Jest without Postgres | Done |
25+
| P1-7 | Render branch policy | Done |
26+
27+
See [`docs/PRODUCTION_READINESS.md`](docs/PRODUCTION_READINESS.md) for the full audit.
28+
29+
---
30+
31+
## P2 — Nice improvements
32+
33+
| ID | Improvement | Why | Impact | Diff | Risk |
34+
|----|-------------|-----|--------|------|------|
35+
| P2-1 | Multi-user per tenant (owner / staff) with basic RBAC | Real shops have more than one counter person | High | High | Med |
36+
| P2-2 | WhatsApp Business Cloud API (true outbound) | Beyond click-to-chat | High | High | Med |
37+
| P2-3 | HttpOnly cookie session / BFF | Reduce XSS token theft | Med | High | Med |
38+
| P2-4 | CHANGELOG + semver release tags | OSS / portfolio hygiene | Med | Low | Low |
39+
| P2-5 | Postgres RLS rollout (per `docs/TENANCY.md`) | DB-enforced isolation | High | High | Med |
40+
| P2-6 | Frontend ESLint in CI | Catch XSS/`innerHTML` early | Med | Low | Low |
41+
| P2-7 | Further extract canvas engine from `script.js` | Maintainability | Med | Med | Med |
42+
| P2-8 | Deduplicate client `palette.js` ↔ server `heuristic.js` | One ranking model | Low | Med | Low |
43+
| P2-9 | Neon/Fly backup restore drill documented + scheduled | Operator confidence | Med | Low | Low |
44+
| P2-10 | In-app first-run checklist (photo → lead → quote) | Reduce support | Med | Med | Low | **Done** |
45+
| P2-11 | OpenAPI `/api/docs` | Integrator DX | Med | Med | Low |
46+
| P2-12 | Remove or archive legacy Python `test-scripts/` | Less confusion vs `e2e/` | Low | Low | Low |
47+
| P2-13 | India DLT / MSG91 template compliance for SMS | Legal SMS in production | High | Med | Med |
48+
49+
---
50+
51+
## P3 — Future (after retention / payment proof)
52+
53+
| ID | Idea | Gate |
54+
|----|------|------|
55+
| P3-1 | Contractor assignment / job tracking | Dealers use quotes+ledger weekly |
56+
| P3-2 | Customer-facing share link (view-only preview) | Share-rate metric justifies it |
57+
| P3-3 | Marketplace / multi-dealer ranking | Post-monetization |
58+
| P3-4 | True 3D / VR preview | Masking accuracy “good enough” in-store |
59+
| P3-5 | MFA / passkeys | Enterprise pilot demand |
60+
| P3-6 | Dealer AI assistant for quoting copy | After palette recommend proves usage |
61+
62+
---
63+
64+
## Explicitly out of scope until evidence
65+
66+
- Full CRM suite rewrite
67+
- Speculative microservices
68+
- Kubernetes manifests (Compose / Render / Fly are the supported paths)
69+
- Fake “enterprise” features that over-promise

CHANGELOG.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Changelog
2+
3+
All notable changes to PaintCRM are documented here.
4+
5+
## [1.2.0] — 2026-07-16
6+
7+
### Product
8+
- First-run checklist: photo → lead → quote (dismissible, local)
9+
- Smart palette picks for guests (heuristic); OpenAI optional when signed in + configured
10+
- Ledger reminder UI labels honest about WhatsApp / SMS / log-only channels
11+
- Forgot-password + reset flows on `/login`
12+
13+
### Security
14+
- Production requires `ALLOWED_ORIGINS`; CORS no longer silently allows `*`
15+
- Password reset: hashed single-use tokens, expiry, rate limits, generic responses, session revocation
16+
- Tenant defense-in-depth on lead fetch and quote convert updates
17+
- Reminder cron no longer counts WhatsApp click-to-chat as delivered
18+
19+
### Engineering
20+
- Frontend views: `customers`, `quotes`, `inventory`, `ledger`
21+
- Playwright E2E suite under `e2e/` wired into CI (fails build on regression)
22+
- Pure Jest helpers run without Postgres
23+
- MIT license, SECURITY.md, CONTRIBUTING, issue/PR templates
24+
- Docs: tenancy model, security audit, production readiness
25+
26+
### Ops
27+
- Render blueprint tracks `main` after merge
28+
- Env example documents SMTP, password reset, AI, MSG91, cron defaults

0 commit comments

Comments
 (0)