Ship-Ready Review: invoice-tracker
Verdict
Not ready for real users. The app works well for a single user, but two issues would expose customer data or run up costs as soon as more people sign up. Both are quick to fix. Everything else can wait until after launch.
Scorecard
| Area | Status | Summary |
|---|---|---|
| Security & access | Must-fix | Missing ownership check; AI key in the browser |
| Data | Fix soon | Backups never tested |
| Reliability | Fix soon | No error alerts |
| Costs | Fix soon | AI endpoint has no rate limit |
| Deployment | Solid | Right deployment type, domain and HTTPS |
Must-fix before launch
Must-fix 1. Any logged-in user can open any invoice
- What
GET /api/invoices/:idchecks that a user is logged in, but not that the invoice belongs to them. Changing the ID in the URL shows another customer's invoice.- Why it matters
- Customer names, amounts and addresses are exposed to every other user. This is the kind of issue that ends trust in a product overnight.
- How to fix
- Scope every invoice query to the current user (
where: { id, ownerId: req.user.id }) and return 404 otherwise. Apply the same check to update and delete routes. Test with two accounts. - Effort
- Small, under an hour
Must-fix 2. The OpenAI key is shipped to the browser
- What
- The invoice-summary feature calls OpenAI directly from React. The key is inlined in the JavaScript bundle and visible in DevTools.
- Why it matters
- Anyone can copy the key and use it at your expense until the account is drained or blocked.
- How to fix
- Move the call to a backend route (
POST /api/summarize) that reads the key from Replit Secrets. Then rotate the key, since the old one must be considered public. - Effort
- Small, about an hour
Fix soon
Fix soon 3. No rate limit on /api/generate
- What
- One user can trigger unlimited AI calls.
- Why it matters
- A script or a curious user can burn the month's AI budget in an afternoon.
- How to fix
- Add a per-user rate limit (for example 20 calls per hour) and a spending cap at the provider.
- Effort
- Small
Fix soon 4. Backups were never restored
- What
- Backups exist, but nobody has tried restoring one.
- Why it matters
- You only find out a backup is broken when you need it.
- How to fix
- Restore the latest backup into a copy once, and note the steps and the time it took.
- Effort
- Small, 30 minutes
Fix soon 5. No error alerts
- What
- Errors only show up in the logs.
- Why it matters
- Customers find outages before you do.
- How to fix
- Add an uptime monitor on a
/healthroute and an error tracker that emails you. - Effort
- Small
What's solid
- Login uses Replit Auth, and sessions expire.
- Other API keys are stored in Replit Secrets and only read on the server.
- The published app uses its own production database.
- Autoscale fits the traffic, and the custom domain serves HTTPS.
Next steps
Option A: fix it yourself with this list. Each item above has the exact change to make.
Option B: the Ship-It Sprint. I fix every must-fix and fix-soon item and deploy to production in 5 days, for $990, with the $199 review credited. The first month of Care is included.