BillTracker/routes/user.cts

123 lines
4.8 KiB
TypeScript
Raw Normal View History

// Fully type-checked (tsconfig.server.json); leading import type keeps Node type-stripping active.
import type { Req, Res, Next } from '../types/http';
('use strict');
2026-05-09 13:03:36 -05:00
const express = require('express');
const router = express.Router();
const { getDb } = require('../db/database.cts');
const { seedDemoData } = require('../scripts/seedDemoData.cts');
const { demoDataLimiter } = require('../middleware/rateLimiter.cts');
feat(demo): full "take it for a ride" seed + surgical, correct removal The demo seeder created bills + categories only, so nearly every page rendered empty; clear-demo had a data-loss bug and error-protection gaps. Seed now populates every user-facing surface: bills with autopay states, 3-6 months of payment history (with drift + debt paydown), monthly skips/overrides/snoozes/notes, a demo bank source + accounts + matched/ unmatched/subscription transactions, categorised spending + budgets + rules, planning income/starting-amounts, category groups, an active snowball plan (+ extra-payment), merchant rules, and a calendar feed — so a new user can explore Tracker (incl. overdue/drift), Analytics, Summary, Spending, Snowball, Payoff, Transactions, Matches, Subscriptions, Banking and Calendar with realistic data. Correct removal (the emphasis): - Migration v1.07 adds an is_seeded marker to the 12 user-scoped, non-cascading demo tables (bill-children cascade with their seeded bill). - clearSeededDemoData() (new, in userDataService alongside eraseUserData) removes ONLY seeded rows in one transaction, child->parent, and keeps a seeded category if a user's own bill still references it. - Fixes the category collision: seed marks ONLY categories it creates, so clear no longer deletes the user's default categories / uncategorises their real bills. Error protection: idempotency guard on seeded (not all) bills -> 409 instead of a fake "created 0"; whole seed wrapped in one transaction (atomic); rate-limit + audit + standardizeError on all three routes; seeded-status reports payments/transactions counts; dead --force removed. Client copy/counts corrected. Verified: typecheck/check clean; server suite 232/232 (+6 new); client typecheck/lint/build clean; e2e probe 17/17; on a COPY of the real prod DB the v1.07 migration applies (integrity ok) and a seed->clear round-trip on the data-rich user restores their real data byte-for-byte with no orphans. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 13:06:47 -05:00
const { eraseUserData, clearSeededDemoData } = require('../services/userDataService.cts');
const { ApiError, ValidationError } = require('../utils/apiError.cts');
// 4xx service errors keep their message/code on the wire; anything else is
// rethrown raw so the terminal handler masks + logs it as a 5xx.
function toUserOpError(err: any, fallbackCode: string) {
if (err.status && err.status < 500) {
return new ApiError(err.code || fallbackCode, err.message, err.status);
}
return err;
}
2026-05-09 13:03:36 -05:00
feat(demo): full "take it for a ride" seed + surgical, correct removal The demo seeder created bills + categories only, so nearly every page rendered empty; clear-demo had a data-loss bug and error-protection gaps. Seed now populates every user-facing surface: bills with autopay states, 3-6 months of payment history (with drift + debt paydown), monthly skips/overrides/snoozes/notes, a demo bank source + accounts + matched/ unmatched/subscription transactions, categorised spending + budgets + rules, planning income/starting-amounts, category groups, an active snowball plan (+ extra-payment), merchant rules, and a calendar feed — so a new user can explore Tracker (incl. overdue/drift), Analytics, Summary, Spending, Snowball, Payoff, Transactions, Matches, Subscriptions, Banking and Calendar with realistic data. Correct removal (the emphasis): - Migration v1.07 adds an is_seeded marker to the 12 user-scoped, non-cascading demo tables (bill-children cascade with their seeded bill). - clearSeededDemoData() (new, in userDataService alongside eraseUserData) removes ONLY seeded rows in one transaction, child->parent, and keeps a seeded category if a user's own bill still references it. - Fixes the category collision: seed marks ONLY categories it creates, so clear no longer deletes the user's default categories / uncategorises their real bills. Error protection: idempotency guard on seeded (not all) bills -> 409 instead of a fake "created 0"; whole seed wrapped in one transaction (atomic); rate-limit + audit + standardizeError on all three routes; seeded-status reports payments/transactions counts; dead --force removed. Client copy/counts corrected. Verified: typecheck/check clean; server suite 232/232 (+6 new); client typecheck/lint/build clean; e2e probe 17/17; on a COPY of the real prod DB the v1.07 migration applies (integrity ok) and a seed->clear round-trip on the data-rich user restores their real data byte-for-byte with no orphans. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 13:06:47 -05:00
// GET /api/user/seeded-status — whether the current user has demo data, with counts
router.get('/seeded-status', (req: Req, res: Res) => {
try {
const db = getDb();
const userId = req.user.id;
feat(demo): full "take it for a ride" seed + surgical, correct removal The demo seeder created bills + categories only, so nearly every page rendered empty; clear-demo had a data-loss bug and error-protection gaps. Seed now populates every user-facing surface: bills with autopay states, 3-6 months of payment history (with drift + debt paydown), monthly skips/overrides/snoozes/notes, a demo bank source + accounts + matched/ unmatched/subscription transactions, categorised spending + budgets + rules, planning income/starting-amounts, category groups, an active snowball plan (+ extra-payment), merchant rules, and a calendar feed — so a new user can explore Tracker (incl. overdue/drift), Analytics, Summary, Spending, Snowball, Payoff, Transactions, Matches, Subscriptions, Banking and Calendar with realistic data. Correct removal (the emphasis): - Migration v1.07 adds an is_seeded marker to the 12 user-scoped, non-cascading demo tables (bill-children cascade with their seeded bill). - clearSeededDemoData() (new, in userDataService alongside eraseUserData) removes ONLY seeded rows in one transaction, child->parent, and keeps a seeded category if a user's own bill still references it. - Fixes the category collision: seed marks ONLY categories it creates, so clear no longer deletes the user's default categories / uncategorises their real bills. Error protection: idempotency guard on seeded (not all) bills -> 409 instead of a fake "created 0"; whole seed wrapped in one transaction (atomic); rate-limit + audit + standardizeError on all three routes; seeded-status reports payments/transactions counts; dead --force removed. Client copy/counts corrected. Verified: typecheck/check clean; server suite 232/232 (+6 new); client typecheck/lint/build clean; e2e probe 17/17; on a COPY of the real prod DB the v1.07 migration applies (integrity ok) and a seed->clear round-trip on the data-rich user restores their real data byte-for-byte with no orphans. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 13:06:47 -05:00
const one = (sql: string) => db.prepare(sql).get(userId).count as number;
const seededBills = one(
'SELECT COUNT(*) as count FROM bills WHERE user_id = ? AND is_seeded = 1',
);
const seededCategories = one(
'SELECT COUNT(*) as count FROM categories WHERE user_id = ? AND is_seeded = 1',
);
feat(demo): full "take it for a ride" seed + surgical, correct removal The demo seeder created bills + categories only, so nearly every page rendered empty; clear-demo had a data-loss bug and error-protection gaps. Seed now populates every user-facing surface: bills with autopay states, 3-6 months of payment history (with drift + debt paydown), monthly skips/overrides/snoozes/notes, a demo bank source + accounts + matched/ unmatched/subscription transactions, categorised spending + budgets + rules, planning income/starting-amounts, category groups, an active snowball plan (+ extra-payment), merchant rules, and a calendar feed — so a new user can explore Tracker (incl. overdue/drift), Analytics, Summary, Spending, Snowball, Payoff, Transactions, Matches, Subscriptions, Banking and Calendar with realistic data. Correct removal (the emphasis): - Migration v1.07 adds an is_seeded marker to the 12 user-scoped, non-cascading demo tables (bill-children cascade with their seeded bill). - clearSeededDemoData() (new, in userDataService alongside eraseUserData) removes ONLY seeded rows in one transaction, child->parent, and keeps a seeded category if a user's own bill still references it. - Fixes the category collision: seed marks ONLY categories it creates, so clear no longer deletes the user's default categories / uncategorises their real bills. Error protection: idempotency guard on seeded (not all) bills -> 409 instead of a fake "created 0"; whole seed wrapped in one transaction (atomic); rate-limit + audit + standardizeError on all three routes; seeded-status reports payments/transactions counts; dead --force removed. Client copy/counts corrected. Verified: typecheck/check clean; server suite 232/232 (+6 new); client typecheck/lint/build clean; e2e probe 17/17; on a COPY of the real prod DB the v1.07 migration applies (integrity ok) and a seed->clear round-trip on the data-rich user restores their real data byte-for-byte with no orphans. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 13:06:47 -05:00
// Bill-children (cascade with the seeded bill) counted via their bill.
const seededPayments = one(
'SELECT COUNT(*) as count FROM payments WHERE bill_id IN (SELECT id FROM bills WHERE user_id = ? AND is_seeded = 1)',
);
const seededTransactions = one(
'SELECT COUNT(*) as count FROM transactions WHERE user_id = ? AND is_seeded = 1',
);
feat(demo): full "take it for a ride" seed + surgical, correct removal The demo seeder created bills + categories only, so nearly every page rendered empty; clear-demo had a data-loss bug and error-protection gaps. Seed now populates every user-facing surface: bills with autopay states, 3-6 months of payment history (with drift + debt paydown), monthly skips/overrides/snoozes/notes, a demo bank source + accounts + matched/ unmatched/subscription transactions, categorised spending + budgets + rules, planning income/starting-amounts, category groups, an active snowball plan (+ extra-payment), merchant rules, and a calendar feed — so a new user can explore Tracker (incl. overdue/drift), Analytics, Summary, Spending, Snowball, Payoff, Transactions, Matches, Subscriptions, Banking and Calendar with realistic data. Correct removal (the emphasis): - Migration v1.07 adds an is_seeded marker to the 12 user-scoped, non-cascading demo tables (bill-children cascade with their seeded bill). - clearSeededDemoData() (new, in userDataService alongside eraseUserData) removes ONLY seeded rows in one transaction, child->parent, and keeps a seeded category if a user's own bill still references it. - Fixes the category collision: seed marks ONLY categories it creates, so clear no longer deletes the user's default categories / uncategorises their real bills. Error protection: idempotency guard on seeded (not all) bills -> 409 instead of a fake "created 0"; whole seed wrapped in one transaction (atomic); rate-limit + audit + standardizeError on all three routes; seeded-status reports payments/transactions counts; dead --force removed. Client copy/counts corrected. Verified: typecheck/check clean; server suite 232/232 (+6 new); client typecheck/lint/build clean; e2e probe 17/17; on a COPY of the real prod DB the v1.07 migration applies (integrity ok) and a seed->clear round-trip on the data-rich user restores their real data byte-for-byte with no orphans. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 13:06:47 -05:00
res.json({
feat(demo): full "take it for a ride" seed + surgical, correct removal The demo seeder created bills + categories only, so nearly every page rendered empty; clear-demo had a data-loss bug and error-protection gaps. Seed now populates every user-facing surface: bills with autopay states, 3-6 months of payment history (with drift + debt paydown), monthly skips/overrides/snoozes/notes, a demo bank source + accounts + matched/ unmatched/subscription transactions, categorised spending + budgets + rules, planning income/starting-amounts, category groups, an active snowball plan (+ extra-payment), merchant rules, and a calendar feed — so a new user can explore Tracker (incl. overdue/drift), Analytics, Summary, Spending, Snowball, Payoff, Transactions, Matches, Subscriptions, Banking and Calendar with realistic data. Correct removal (the emphasis): - Migration v1.07 adds an is_seeded marker to the 12 user-scoped, non-cascading demo tables (bill-children cascade with their seeded bill). - clearSeededDemoData() (new, in userDataService alongside eraseUserData) removes ONLY seeded rows in one transaction, child->parent, and keeps a seeded category if a user's own bill still references it. - Fixes the category collision: seed marks ONLY categories it creates, so clear no longer deletes the user's default categories / uncategorises their real bills. Error protection: idempotency guard on seeded (not all) bills -> 409 instead of a fake "created 0"; whole seed wrapped in one transaction (atomic); rate-limit + audit + standardizeError on all three routes; seeded-status reports payments/transactions counts; dead --force removed. Client copy/counts corrected. Verified: typecheck/check clean; server suite 232/232 (+6 new); client typecheck/lint/build clean; e2e probe 17/17; on a COPY of the real prod DB the v1.07 migration applies (integrity ok) and a seed->clear round-trip on the data-rich user restores their real data byte-for-byte with no orphans. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 13:06:47 -05:00
seeded: seededBills > 0 || seededCategories > 0,
seededBills,
seededCategories,
seededPayments,
seededTransactions,
});
} catch (err) {
throw toUserOpError(err, 'SEEDED_STATUS_ERROR');
}
});
feat(demo): full "take it for a ride" seed + surgical, correct removal The demo seeder created bills + categories only, so nearly every page rendered empty; clear-demo had a data-loss bug and error-protection gaps. Seed now populates every user-facing surface: bills with autopay states, 3-6 months of payment history (with drift + debt paydown), monthly skips/overrides/snoozes/notes, a demo bank source + accounts + matched/ unmatched/subscription transactions, categorised spending + budgets + rules, planning income/starting-amounts, category groups, an active snowball plan (+ extra-payment), merchant rules, and a calendar feed — so a new user can explore Tracker (incl. overdue/drift), Analytics, Summary, Spending, Snowball, Payoff, Transactions, Matches, Subscriptions, Banking and Calendar with realistic data. Correct removal (the emphasis): - Migration v1.07 adds an is_seeded marker to the 12 user-scoped, non-cascading demo tables (bill-children cascade with their seeded bill). - clearSeededDemoData() (new, in userDataService alongside eraseUserData) removes ONLY seeded rows in one transaction, child->parent, and keeps a seeded category if a user's own bill still references it. - Fixes the category collision: seed marks ONLY categories it creates, so clear no longer deletes the user's default categories / uncategorises their real bills. Error protection: idempotency guard on seeded (not all) bills -> 409 instead of a fake "created 0"; whole seed wrapped in one transaction (atomic); rate-limit + audit + standardizeError on all three routes; seeded-status reports payments/transactions counts; dead --force removed. Client copy/counts corrected. Verified: typecheck/check clean; server suite 232/232 (+6 new); client typecheck/lint/build clean; e2e probe 17/17; on a COPY of the real prod DB the v1.07 migration applies (integrity ok) and a seed->clear round-trip on the data-rich user restores their real data byte-for-byte with no orphans. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 13:06:47 -05:00
// POST /api/user/clear-demo-data — surgically remove ONLY seeded demo data for the
// requesting user (in one transaction, child → parent). Marker tables are removed
// by is_seeded; bill-children cascade with their seeded bill. A seeded category is
// removed only if no remaining (non-seeded) bill references it, so a user's own bill
// that reused a demo category keeps its category. Mirrors eraseUserData().
router.post('/clear-demo-data', demoDataLimiter, (req: Req, res: Res) => {
2026-05-09 13:03:36 -05:00
try {
feat(demo): full "take it for a ride" seed + surgical, correct removal The demo seeder created bills + categories only, so nearly every page rendered empty; clear-demo had a data-loss bug and error-protection gaps. Seed now populates every user-facing surface: bills with autopay states, 3-6 months of payment history (with drift + debt paydown), monthly skips/overrides/snoozes/notes, a demo bank source + accounts + matched/ unmatched/subscription transactions, categorised spending + budgets + rules, planning income/starting-amounts, category groups, an active snowball plan (+ extra-payment), merchant rules, and a calendar feed — so a new user can explore Tracker (incl. overdue/drift), Analytics, Summary, Spending, Snowball, Payoff, Transactions, Matches, Subscriptions, Banking and Calendar with realistic data. Correct removal (the emphasis): - Migration v1.07 adds an is_seeded marker to the 12 user-scoped, non-cascading demo tables (bill-children cascade with their seeded bill). - clearSeededDemoData() (new, in userDataService alongside eraseUserData) removes ONLY seeded rows in one transaction, child->parent, and keeps a seeded category if a user's own bill still references it. - Fixes the category collision: seed marks ONLY categories it creates, so clear no longer deletes the user's default categories / uncategorises their real bills. Error protection: idempotency guard on seeded (not all) bills -> 409 instead of a fake "created 0"; whole seed wrapped in one transaction (atomic); rate-limit + audit + standardizeError on all three routes; seeded-status reports payments/transactions counts; dead --force removed. Client copy/counts corrected. Verified: typecheck/check clean; server suite 232/232 (+6 new); client typecheck/lint/build clean; e2e probe 17/17; on a COPY of the real prod DB the v1.07 migration applies (integrity ok) and a seed->clear round-trip on the data-rich user restores their real data byte-for-byte with no orphans. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 13:06:47 -05:00
const result = clearSeededDemoData(getDb(), req.user.id);
2026-05-09 13:03:36 -05:00
res.json({
success: true,
feat(demo): full "take it for a ride" seed + surgical, correct removal The demo seeder created bills + categories only, so nearly every page rendered empty; clear-demo had a data-loss bug and error-protection gaps. Seed now populates every user-facing surface: bills with autopay states, 3-6 months of payment history (with drift + debt paydown), monthly skips/overrides/snoozes/notes, a demo bank source + accounts + matched/ unmatched/subscription transactions, categorised spending + budgets + rules, planning income/starting-amounts, category groups, an active snowball plan (+ extra-payment), merchant rules, and a calendar feed — so a new user can explore Tracker (incl. overdue/drift), Analytics, Summary, Spending, Snowball, Payoff, Transactions, Matches, Subscriptions, Banking and Calendar with realistic data. Correct removal (the emphasis): - Migration v1.07 adds an is_seeded marker to the 12 user-scoped, non-cascading demo tables (bill-children cascade with their seeded bill). - clearSeededDemoData() (new, in userDataService alongside eraseUserData) removes ONLY seeded rows in one transaction, child->parent, and keeps a seeded category if a user's own bill still references it. - Fixes the category collision: seed marks ONLY categories it creates, so clear no longer deletes the user's default categories / uncategorises their real bills. Error protection: idempotency guard on seeded (not all) bills -> 409 instead of a fake "created 0"; whole seed wrapped in one transaction (atomic); rate-limit + audit + standardizeError on all three routes; seeded-status reports payments/transactions counts; dead --force removed. Client copy/counts corrected. Verified: typecheck/check clean; server suite 232/232 (+6 new); client typecheck/lint/build clean; e2e probe 17/17; on a COPY of the real prod DB the v1.07 migration applies (integrity ok) and a seed->clear round-trip on the data-rich user restores their real data byte-for-byte with no orphans. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 13:06:47 -05:00
removed: result.removed,
billsDeleted: result.counts.bills || 0,
categoriesDeleted: result.counts.categories || 0,
counts: result.counts,
2026-05-09 13:03:36 -05:00
});
} catch (err) {
throw toUserOpError(err, 'CLEAR_DEMO_ERROR');
2026-05-09 13:03:36 -05:00
}
});
feat(demo): full "take it for a ride" seed + surgical, correct removal The demo seeder created bills + categories only, so nearly every page rendered empty; clear-demo had a data-loss bug and error-protection gaps. Seed now populates every user-facing surface: bills with autopay states, 3-6 months of payment history (with drift + debt paydown), monthly skips/overrides/snoozes/notes, a demo bank source + accounts + matched/ unmatched/subscription transactions, categorised spending + budgets + rules, planning income/starting-amounts, category groups, an active snowball plan (+ extra-payment), merchant rules, and a calendar feed — so a new user can explore Tracker (incl. overdue/drift), Analytics, Summary, Spending, Snowball, Payoff, Transactions, Matches, Subscriptions, Banking and Calendar with realistic data. Correct removal (the emphasis): - Migration v1.07 adds an is_seeded marker to the 12 user-scoped, non-cascading demo tables (bill-children cascade with their seeded bill). - clearSeededDemoData() (new, in userDataService alongside eraseUserData) removes ONLY seeded rows in one transaction, child->parent, and keeps a seeded category if a user's own bill still references it. - Fixes the category collision: seed marks ONLY categories it creates, so clear no longer deletes the user's default categories / uncategorises their real bills. Error protection: idempotency guard on seeded (not all) bills -> 409 instead of a fake "created 0"; whole seed wrapped in one transaction (atomic); rate-limit + audit + standardizeError on all three routes; seeded-status reports payments/transactions counts; dead --force removed. Client copy/counts corrected. Verified: typecheck/check clean; server suite 232/232 (+6 new); client typecheck/lint/build clean; e2e probe 17/17; on a COPY of the real prod DB the v1.07 migration applies (integrity ok) and a seed->clear round-trip on the data-rich user restores their real data byte-for-byte with no orphans. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 13:06:47 -05:00
// POST /api/user/seed-demo-data — seed the full demo dataset for the requesting user
router.post('/seed-demo-data', demoDataLimiter, (req: Req, res: Res) => {
2026-05-09 13:03:36 -05:00
try {
feat(demo): full "take it for a ride" seed + surgical, correct removal The demo seeder created bills + categories only, so nearly every page rendered empty; clear-demo had a data-loss bug and error-protection gaps. Seed now populates every user-facing surface: bills with autopay states, 3-6 months of payment history (with drift + debt paydown), monthly skips/overrides/snoozes/notes, a demo bank source + accounts + matched/ unmatched/subscription transactions, categorised spending + budgets + rules, planning income/starting-amounts, category groups, an active snowball plan (+ extra-payment), merchant rules, and a calendar feed — so a new user can explore Tracker (incl. overdue/drift), Analytics, Summary, Spending, Snowball, Payoff, Transactions, Matches, Subscriptions, Banking and Calendar with realistic data. Correct removal (the emphasis): - Migration v1.07 adds an is_seeded marker to the 12 user-scoped, non-cascading demo tables (bill-children cascade with their seeded bill). - clearSeededDemoData() (new, in userDataService alongside eraseUserData) removes ONLY seeded rows in one transaction, child->parent, and keeps a seeded category if a user's own bill still references it. - Fixes the category collision: seed marks ONLY categories it creates, so clear no longer deletes the user's default categories / uncategorises their real bills. Error protection: idempotency guard on seeded (not all) bills -> 409 instead of a fake "created 0"; whole seed wrapped in one transaction (atomic); rate-limit + audit + standardizeError on all three routes; seeded-status reports payments/transactions counts; dead --force removed. Client copy/counts corrected. Verified: typecheck/check clean; server suite 232/232 (+6 new); client typecheck/lint/build clean; e2e probe 17/17; on a COPY of the real prod DB the v1.07 migration applies (integrity ok) and a seed->clear round-trip on the data-rich user restores their real data byte-for-byte with no orphans. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 13:06:47 -05:00
const db = getDb();
2026-05-09 13:03:36 -05:00
const result = seedDemoData(req.user.id);
feat(demo): full "take it for a ride" seed + surgical, correct removal The demo seeder created bills + categories only, so nearly every page rendered empty; clear-demo had a data-loss bug and error-protection gaps. Seed now populates every user-facing surface: bills with autopay states, 3-6 months of payment history (with drift + debt paydown), monthly skips/overrides/snoozes/notes, a demo bank source + accounts + matched/ unmatched/subscription transactions, categorised spending + budgets + rules, planning income/starting-amounts, category groups, an active snowball plan (+ extra-payment), merchant rules, and a calendar feed — so a new user can explore Tracker (incl. overdue/drift), Analytics, Summary, Spending, Snowball, Payoff, Transactions, Matches, Subscriptions, Banking and Calendar with realistic data. Correct removal (the emphasis): - Migration v1.07 adds an is_seeded marker to the 12 user-scoped, non-cascading demo tables (bill-children cascade with their seeded bill). - clearSeededDemoData() (new, in userDataService alongside eraseUserData) removes ONLY seeded rows in one transaction, child->parent, and keeps a seeded category if a user's own bill still references it. - Fixes the category collision: seed marks ONLY categories it creates, so clear no longer deletes the user's default categories / uncategorises their real bills. Error protection: idempotency guard on seeded (not all) bills -> 409 instead of a fake "created 0"; whole seed wrapped in one transaction (atomic); rate-limit + audit + standardizeError on all three routes; seeded-status reports payments/transactions counts; dead --force removed. Client copy/counts corrected. Verified: typecheck/check clean; server suite 232/232 (+6 new); client typecheck/lint/build clean; e2e probe 17/17; on a COPY of the real prod DB the v1.07 migration applies (integrity ok) and a seed->clear round-trip on the data-rich user restores their real data byte-for-byte with no orphans. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 13:06:47 -05:00
db.prepare(
`INSERT INTO import_history (user_id, imported_at, source_filename, file_type, rows_parsed, rows_created, rows_updated, rows_skipped, rows_ambiguous, rows_errored, options_json, summary_json)
VALUES (?, datetime('now'), 'seed-demo-data', 'seed-demo', ?, ?, 0, 0, 0, 0, ?, ?)`,
feat(demo): full "take it for a ride" seed + surgical, correct removal The demo seeder created bills + categories only, so nearly every page rendered empty; clear-demo had a data-loss bug and error-protection gaps. Seed now populates every user-facing surface: bills with autopay states, 3-6 months of payment history (with drift + debt paydown), monthly skips/overrides/snoozes/notes, a demo bank source + accounts + matched/ unmatched/subscription transactions, categorised spending + budgets + rules, planning income/starting-amounts, category groups, an active snowball plan (+ extra-payment), merchant rules, and a calendar feed — so a new user can explore Tracker (incl. overdue/drift), Analytics, Summary, Spending, Snowball, Payoff, Transactions, Matches, Subscriptions, Banking and Calendar with realistic data. Correct removal (the emphasis): - Migration v1.07 adds an is_seeded marker to the 12 user-scoped, non-cascading demo tables (bill-children cascade with their seeded bill). - clearSeededDemoData() (new, in userDataService alongside eraseUserData) removes ONLY seeded rows in one transaction, child->parent, and keeps a seeded category if a user's own bill still references it. - Fixes the category collision: seed marks ONLY categories it creates, so clear no longer deletes the user's default categories / uncategorises their real bills. Error protection: idempotency guard on seeded (not all) bills -> 409 instead of a fake "created 0"; whole seed wrapped in one transaction (atomic); rate-limit + audit + standardizeError on all three routes; seeded-status reports payments/transactions counts; dead --force removed. Client copy/counts corrected. Verified: typecheck/check clean; server suite 232/232 (+6 new); client typecheck/lint/build clean; e2e probe 17/17; on a COPY of the real prod DB the v1.07 migration applies (integrity ok) and a seed->clear round-trip on the data-rich user restores their real data byte-for-byte with no orphans. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 13:06:47 -05:00
).run(
req.user.id,
result.billsCreated + result.paymentsCreated + result.transactionsCreated,
result.billsCreated,
JSON.stringify({ action: 'seed-demo-data' }),
JSON.stringify(result.counts),
);
2026-05-09 13:03:36 -05:00
res.json({
success: true,
feat(demo): full "take it for a ride" seed + surgical, correct removal The demo seeder created bills + categories only, so nearly every page rendered empty; clear-demo had a data-loss bug and error-protection gaps. Seed now populates every user-facing surface: bills with autopay states, 3-6 months of payment history (with drift + debt paydown), monthly skips/overrides/snoozes/notes, a demo bank source + accounts + matched/ unmatched/subscription transactions, categorised spending + budgets + rules, planning income/starting-amounts, category groups, an active snowball plan (+ extra-payment), merchant rules, and a calendar feed — so a new user can explore Tracker (incl. overdue/drift), Analytics, Summary, Spending, Snowball, Payoff, Transactions, Matches, Subscriptions, Banking and Calendar with realistic data. Correct removal (the emphasis): - Migration v1.07 adds an is_seeded marker to the 12 user-scoped, non-cascading demo tables (bill-children cascade with their seeded bill). - clearSeededDemoData() (new, in userDataService alongside eraseUserData) removes ONLY seeded rows in one transaction, child->parent, and keeps a seeded category if a user's own bill still references it. - Fixes the category collision: seed marks ONLY categories it creates, so clear no longer deletes the user's default categories / uncategorises their real bills. Error protection: idempotency guard on seeded (not all) bills -> 409 instead of a fake "created 0"; whole seed wrapped in one transaction (atomic); rate-limit + audit + standardizeError on all three routes; seeded-status reports payments/transactions counts; dead --force removed. Client copy/counts corrected. Verified: typecheck/check clean; server suite 232/232 (+6 new); client typecheck/lint/build clean; e2e probe 17/17; on a COPY of the real prod DB the v1.07 migration applies (integrity ok) and a seed->clear round-trip on the data-rich user restores their real data byte-for-byte with no orphans. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 13:06:47 -05:00
message: `Created ${result.billsCreated} bills, ${result.paymentsCreated} payments and ${result.transactionsCreated} transactions`,
2026-05-09 13:03:36 -05:00
billsCreated: result.billsCreated,
categoriesCreated: result.categoriesCreated,
feat(demo): full "take it for a ride" seed + surgical, correct removal The demo seeder created bills + categories only, so nearly every page rendered empty; clear-demo had a data-loss bug and error-protection gaps. Seed now populates every user-facing surface: bills with autopay states, 3-6 months of payment history (with drift + debt paydown), monthly skips/overrides/snoozes/notes, a demo bank source + accounts + matched/ unmatched/subscription transactions, categorised spending + budgets + rules, planning income/starting-amounts, category groups, an active snowball plan (+ extra-payment), merchant rules, and a calendar feed — so a new user can explore Tracker (incl. overdue/drift), Analytics, Summary, Spending, Snowball, Payoff, Transactions, Matches, Subscriptions, Banking and Calendar with realistic data. Correct removal (the emphasis): - Migration v1.07 adds an is_seeded marker to the 12 user-scoped, non-cascading demo tables (bill-children cascade with their seeded bill). - clearSeededDemoData() (new, in userDataService alongside eraseUserData) removes ONLY seeded rows in one transaction, child->parent, and keeps a seeded category if a user's own bill still references it. - Fixes the category collision: seed marks ONLY categories it creates, so clear no longer deletes the user's default categories / uncategorises their real bills. Error protection: idempotency guard on seeded (not all) bills -> 409 instead of a fake "created 0"; whole seed wrapped in one transaction (atomic); rate-limit + audit + standardizeError on all three routes; seeded-status reports payments/transactions counts; dead --force removed. Client copy/counts corrected. Verified: typecheck/check clean; server suite 232/232 (+6 new); client typecheck/lint/build clean; e2e probe 17/17; on a COPY of the real prod DB the v1.07 migration applies (integrity ok) and a seed->clear round-trip on the data-rich user restores their real data byte-for-byte with no orphans. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 13:06:47 -05:00
paymentsCreated: result.paymentsCreated,
transactionsCreated: result.transactionsCreated,
counts: result.counts,
2026-05-09 13:03:36 -05:00
});
} catch (err) {
throw toUserOpError(err, 'SEED_ERROR');
2026-05-09 13:03:36 -05:00
}
});
// POST /api/user/erase-data — permanently wipe the requesting user's financial +
// imported data (bills, payments, transactions, connections, categories, imports).
// Preserves the account, login, 2FA, and preferences. Requires a type-to-confirm token.
router.post('/erase-data', demoDataLimiter, (req: Req, res: Res) => {
const confirm = String(req.body?.confirm ?? '')
.trim()
.toUpperCase();
if (confirm !== 'ERASE') {
throw ValidationError('Type ERASE to confirm.', 'confirm', 'ERASE_CONFIRM_REQUIRED');
}
try {
const result = eraseUserData(getDb(), req.user.id);
res.json({ success: true, ...result });
} catch (err) {
throw toUserOpError(err, 'ERASE_ERROR');
}
});
2026-05-09 13:03:36 -05:00
module.exports = router;