|
5 | 5 |
|
6 | 6 | --- |
7 | 7 |
|
8 | | -## ✅ Completed in PR #4 |
| 8 | +## Product context |
| 9 | + |
| 10 | +Interview Lab is a **free companion** to [Project Amazon PH Academy](https://projectamazon.ph). All features are available to all users — no paid tiers, no subscription gating. |
| 11 | + |
| 12 | +--- |
| 13 | + |
| 14 | +## ✅ Completed (PR #4 + follow-up) |
9 | 15 |
|
10 | 16 | | Finding | Fix | |
11 | 17 | |---|---| |
12 | 18 | | FieldButton missing `outline` variant | Added outline variant to `fieldButtonVariants` | |
13 | | -| Subscription checkout bypass | Returns 503 "Paid plans not available" | |
14 | | -| Subscription manage `change` action | Blocked for paid tier upgrades | |
| 19 | +| Subscription checkout bypass | Removed subscription API endpoints entirely | |
| 20 | +| Subscription manage `change` action | Removed subscription API endpoints entirely | |
15 | 21 | | JWT fallback secret | Requires 32+ char `JWT_SECRET` at startup | |
16 | 22 | | Questions API unauthenticated | Server-side auth + tier checks; strips premium fields for free tier | |
17 | 23 | | Guides API unauthenticated | Server-side auth + tier checks; locks content behind entitlement | |
18 | 24 | | Verification token logged | Removed `console.log`; async/await DB calls | |
19 | 25 | | Rate limiter non-atomic | Wrapped in `db.$transaction`; fail-closed | |
20 | 26 | | Fabricated aggregate rating | Removed from structured data | |
21 | 27 | | Pre-existing FieldBadge/Button type errors | Added missing variants | |
| 28 | +| Subscription tier gating | `subscription-guard.ts` always returns `allowed: true` | |
| 29 | +| Subscription endpoints | Removed `src/app/api/subscription/` entirely | |
| 30 | +| Pricing page / UpgradeModal / SubscriptionBanner | Stubbed to no-op (kept imports compiling) | |
| 31 | +| README pricing table | Replaced with "Free, always" notice | |
22 | 32 |
|
23 | 33 | --- |
24 | 34 |
|
25 | | -## 🔴 Phase 1 — Must fix before paid launch |
| 35 | +## 🔴 Phase 1 — Must fix before public launch |
26 | 36 |
|
27 | 37 | ### P1.1 — Server AI adapter |
28 | 38 | **Files:** `src/lib/browser-llm-integration.ts`, `src/app/api/ai/*/route.ts` (4 routes) |
|
38 | 48 | - Replace all `BrowserLLMIntegration` imports in API routes |
39 | 49 | - Add privacy/provider disclosure to UI |
40 | 50 |
|
41 | | -### P1.2 — Real payment integration |
42 | | -**Files:** `src/app/api/subscription/webhook/route.ts`, `src/app/api/subscription/checkout/route.ts` |
43 | | -**Problem:** Webhook is a placeholder (no signature verification, no subscription sync). Price IDs are empty strings. |
44 | | -**Fix:** |
45 | | -- Configure Stripe price IDs in environment |
46 | | -- Implement signed webhook handler |
47 | | -- Add `checkout.session.completed`, `customer.subscription.updated`, `customer.subscription.deleted` events |
48 | | -- Use webhook as the sole subscription activation path |
49 | | -- Add `idempotencyKey` to payment records |
50 | | -- Wrap billing mutations in `db.$transaction` |
51 | | - |
52 | | -### P1.3 — Subscription record consistency |
53 | | -**Files:** `src/lib/pricing.ts`, `prisma/schema.prisma` |
54 | | -**Problem:** `User.subscriptionTier` and `Subscription.tier` are dual sources of truth; mutations aren't transactional. |
| 51 | +### P1.2 — Client auth from server session |
| 52 | +**Files:** `src/lib/auth-context.tsx` |
| 53 | +**Problem:** Auth state restored from `localStorage` (modifiable); no server validation on startup. |
55 | 54 | **Fix:** |
56 | | -- Designate `Subscription.tier` as the single source of truth |
57 | | -- Derive `User.subscriptionTier` via a view or computed field |
58 | | -- Wrap all billing mutations in `db.$transaction` |
59 | | -- Add Prisma enums for `tier`, `status`, `billingPeriod` |
| 55 | +- Add `GET /api/auth/session` endpoint returning authenticated user from cookie |
| 56 | +- On app mount, validate session via server endpoint instead of reading localStorage |
| 57 | +- Keep localStorage as a cache layer with server re-validation |
| 58 | +- Ensure logout clears both cookie and localStorage atomically |
60 | 59 |
|
61 | | -### P1.4 — ESLint fixes & re-enablement |
| 60 | +### P1.3 — ESLint fixes & re-enablement |
62 | 61 | **Files:** `eslint.config.mjs`, `src/app/page.tsx`, `src/components/interview-lab/AdminPanel.tsx`, `PricingPage.tsx`, `QuestionBank.tsx` |
63 | 62 | **Problem:** 35 rules disabled; 9 pre-existing ESLint errors block CI. |
64 | 63 | **Fix:** |
|
67 | 66 | - Remove blanket `off` overrides |
68 | 67 | - Add `lint-staged` pre-commit hook |
69 | 68 |
|
70 | | -### P1.5 — Client auth from server session |
71 | | -**Files:** `src/lib/auth-context.tsx` |
72 | | -**Problem:** Auth state restored from `localStorage` (modifiable); no server validation on startup. |
73 | | -**Fix:** |
74 | | -- Add `GET /api/auth/session` endpoint returning authenticated user from cookie |
75 | | -- On app mount, validate session via server endpoint instead of reading localStorage |
76 | | -- Keep localStorage as a cache layer with server re-validation |
77 | | -- Ensure logout clears both cookie and localStorage atomically |
78 | | - |
79 | 69 | --- |
80 | 70 |
|
81 | 71 | ## 🟡 Phase 2 — Required within next development cycle |
|
108 | 98 | **Files:** `src/app/api/export/route.ts` |
109 | 99 | **Problem:** No input size validation; PDF silently truncates at page bottom. |
110 | 100 | **Fix:** |
111 | | -- Add content length limits matching subscription tier |
| 101 | +- Add content length limits |
112 | 102 | - Replace handcrafted PDF with proper pagination (e.g., `pdf-lib` or `pdfkit` with page break support) |
113 | 103 | - Add request body size validation middleware |
114 | 104 |
|
|
118 | 108 | **Fix:** |
119 | 109 | - Set per-file coverage thresholds (e.g., 60% lines, 50% branches) |
120 | 110 | - Remove blanket excludes for components |
121 | | -- Add integration tests for auth flows, onboarding, interviews, resume gen, admin, and plan enforcement |
| 111 | +- Add integration tests for auth flows, onboarding, interviews, resume gen, admin |
122 | 112 | - Add browser tests for critical user journeys |
123 | 113 |
|
124 | 114 | ### P2.6 — Operational documentation |
|
132 | 122 |
|
133 | 123 | --- |
134 | 124 |
|
135 | | -## ⚪ Phase 3 — Before paid launch gate |
| 125 | +## ⚪ Phase 3 — Before public launch gate |
136 | 126 |
|
137 | 127 | ### P3.1 — Privacy & legal |
138 | 128 | - Add privacy policy page with data retention and account deletion |
|
141 | 131 | - Add cookie consent banner |
142 | 132 | - Add terms of service page |
143 | 133 |
|
144 | | -### P3.2 — Honest metadata |
145 | | -- Remove `offers.price: "0"` from structured data if paid tiers exist |
146 | | -- Add proper pricing schema if billing is active |
| 134 | +### P3.2 — Honest structured data |
| 135 | +- Remove `offers.price: "0"` from structured data if product is truly free (or add proper "Free" offer) |
147 | 136 | - Add real user review/rating system before claiming ratings |
148 | 137 |
|
149 | 138 | ### P3.3 — Security hardening |
|
167 | 156 |
|
168 | 157 | | Phase | Items | Estimated effort | |
169 | 158 | |---|---|---| |
170 | | -| 🔴 Phase 1 (blockers) | 5 items | 3–4 sprints | |
| 159 | +| 🔴 Phase 1 (blockers) | 3 items | 2–3 sprints | |
171 | 160 | | 🟡 Phase 2 (cycle) | 6 items | 4–6 sprints | |
172 | 161 | | ⚪ Phase 3 (launch gate) | 4 items | 2–3 sprints | |
173 | 162 |
|
|
177 | 166 |
|
178 | 167 | ### Current branch state |
179 | 168 | - `main` at commit `190b3be` with PR #4 merged |
180 | | -- The PR branch `fix/critical-security-and-build-issues` can be deleted |
181 | | - |
182 | | -### Known CI state |
183 | | -- ESLint: 9 pre-existing errors (4 files untouched by PR #4) |
184 | | -- Vercel: fails on pre-existing analytics route runtime error |
185 | | -- Neither is a regression from PR #4 |
| 169 | +- Subscription system stubbed (not removed) to keep imports compiling |
| 170 | +- 3 stub files created: `PricingPage.tsx`, `UpgradeModal.tsx`, `SubscriptionBanner.tsx` |
186 | 171 |
|
187 | 172 | ### Key architecture decisions to carry forward |
188 | 173 | 1. **Auth:** JWT in HttpOnly cookies with DB re-verification on every request (keep this pattern) |
189 | | -2. **Tier enforcement:** Server-side `subscription-guard.ts` helpers are the right pattern — extend them, don't duplicate |
| 174 | +2. **Tier enforcement:** All subscription guard functions return `allowed: true` — product is free |
190 | 175 | 3. **Rate limiting:** The `db.$transaction` pattern is correct for persistent storage; middleware needs Redis/Upstash for serverless |
191 | 176 | 4. **AI:** Build a proper server adapter rather than trying to fix the client-side `BrowserLLMIntegration` |
192 | 177 |
|
193 | 178 | ### Files most likely to conflict with future work |
194 | 179 | - `src/lib/browser-llm-integration.ts` — will be replaced entirely by P1.1 |
195 | 180 | - `src/app/api/downloads/[id]/route.ts` — needs full decomposition (P2.3) |
196 | 181 | - `prisma/schema.prisma` — needs migration (P2.2) |
197 | | -- `eslint.config.mjs` — needs rules re-enabled (P1.4) |
198 | | -- `src/lib/auth-context.tsx` — needs session endpoint (P1.5) |
| 182 | +- `eslint.config.mjs` — needs rules re-enabled (P1.3) |
| 183 | +- `src/lib/auth-context.tsx` — needs session endpoint (P1.2) |
| 184 | + |
| 185 | +### Stub files (to be removed when components are refactored) |
| 186 | +- `src/components/interview-lab/PricingPage.tsx` |
| 187 | +- `src/components/interview-lab/UpgradeModal.tsx` |
| 188 | +- `src/components/interview-lab/SubscriptionBanner.tsx` |
| 189 | +- `src/lib/use-subscription.ts` |
0 commit comments