From 988c6c31a9dccca00e95bba533e5dfed29c247a5 Mon Sep 17 00:00:00 2001 From: Stan Runge Date: Sat, 24 Oct 2026 02:37:07 +0000 Subject: [PATCH] Money: move /projects/money-tracker to /trackers/money, rename to Money, 308 redirects from old paths (bank-callback query kept) --- README.md | 4 +-- scripts/bank-link.ts | 2 +- src/lib/components/ProjectIcon.svelte | 2 +- src/lib/projects.ts | 4 +-- src/lib/title.ts | 4 +-- src/routes/privacy/+page.svelte | 4 +-- .../money-tracker/[...rest]/+server.ts | 10 +++++++ .../money-tracker/[...rest]/redirect.test.ts | 30 +++++++++++++++++++ src/routes/terms/+page.svelte | 7 +++-- .../money}/+page.server.ts | 2 +- .../money}/+page.svelte | 8 ++--- .../money}/bank-callback/+page.server.ts | 0 .../money}/bank-callback/+page.svelte | 7 ++--- .../money}/money-tracker.css | 0 .../money}/transactions/+page.server.ts | 0 .../money}/transactions/+page.svelte | 10 +++---- tests/game-tracker.test.ts | 2 +- 17 files changed, 67 insertions(+), 29 deletions(-) create mode 100644 src/routes/projects/money-tracker/[...rest]/+server.ts create mode 100644 src/routes/projects/money-tracker/[...rest]/redirect.test.ts rename src/routes/{projects/money-tracker => trackers/money}/+page.server.ts (99%) rename src/routes/{projects/money-tracker => trackers/money}/+page.svelte (99%) rename src/routes/{projects/money-tracker => trackers/money}/bank-callback/+page.server.ts (100%) rename src/routes/{projects/money-tracker => trackers/money}/bank-callback/+page.svelte (88%) rename src/routes/{projects/money-tracker => trackers/money}/money-tracker.css (100%) rename src/routes/{projects/money-tracker => trackers/money}/transactions/+page.server.ts (100%) rename src/routes/{projects/money-tracker => trackers/money}/transactions/+page.svelte (94%) diff --git a/README.md b/README.md index 3b92f2f..bb886b7 100644 --- a/README.md +++ b/README.md @@ -260,9 +260,9 @@ bind-mounts the host's `BOOKS_HOST_DIR` (default `/home/stan/books`) read-only a `BOOKS_ROOT=/books`. Without a `library` directory there the deploy still succeeds, but files can't be opened. -## Money tracker +## Money -`/projects/money-tracker` is restricted to administrator accounts. Its tables +`/trackers/money` (formerly `/projects/money-tracker`, which now redirects) is restricted to administrator accounts. Its tables are created by `npm run db:migrate:money-tracker`; apply that migration to each environment before deploying the new route. The deployment preflight checks for all nine tracker tables and the projection columns. diff --git a/scripts/bank-link.ts b/scripts/bank-link.ts index 33009c2..d0fbe52 100644 --- a/scripts/bank-link.ts +++ b/scripts/bank-link.ts @@ -69,7 +69,7 @@ const aspsp = { const defaultAccount = value('--account') ?? preset?.account ?? aspsp.name; const redirectUrl = process.env.ENABLEBANKING_REDIRECT_URL ?? - 'https://stanrunge.dev/projects/money-tracker/bank-callback'; + 'https://stanrunge.dev/projects/money-tracker/bank-callback'; // registered with Enable Banking; 308s to /trackers/money/bank-callback with the query kept const app = await client.application(); console.log( diff --git a/src/lib/components/ProjectIcon.svelte b/src/lib/components/ProjectIcon.svelte index 7d7d37e..60b49ce 100644 --- a/src/lib/components/ProjectIcon.svelte +++ b/src/lib/components/ProjectIcon.svelte @@ -33,7 +33,7 @@ 'derby lens': ChartNoAxesCombined, 'world opinions': Globe2, 'language learner': Languages, - 'money tracker': Wallet, + money: Wallet, 'cs prices': CandlestickChart, 'house tracker': House, lists: ListChecks, diff --git a/src/lib/projects.ts b/src/lib/projects.ts index 5b95b4f..308785a 100644 --- a/src/lib/projects.ts +++ b/src/lib/projects.ts @@ -41,9 +41,9 @@ export const trackers = [ external: false }, { - name: 'money tracker', + name: 'money', subtitle: 'a manual ledger for accounts, bills and utilities', - href: '/projects/money-tracker', + href: '/trackers/money', external: false }, { diff --git a/src/lib/title.ts b/src/lib/title.ts index 22e53b4..828ba57 100644 --- a/src/lib/title.ts +++ b/src/lib/title.ts @@ -34,10 +34,10 @@ export function getTitle(path: string, environment: SiteEnvironment = null): str '/projects/energy': 'Energy', '/projects/game-tracker': 'Game Tracker', '/projects/language-learner': 'Language Learner', - '/projects/money-tracker': 'Money Tracker', '/projects/spotify-beatmap-finder': 'osu! map finder', '/projects/progress': 'Progress', - '/projects/world-opinions': 'World Opinions' + '/projects/world-opinions': 'World Opinions', + '/trackers/money': 'Money' }; const cleaned = path.replace(/\/$/, '') || '/'; const title = diff --git a/src/routes/privacy/+page.svelte b/src/routes/privacy/+page.svelte index 141433d..6bdf6c7 100644 --- a/src/routes/privacy/+page.svelte +++ b/src/routes/privacy/+page.svelte @@ -14,9 +14,9 @@ stored. Session cookies keep you signed in; there are no advertising or third-party tracking cookies.

-

Bank data (money tracker)

+

Bank data (Money)

- The money tracker is a private tool used only by the site owner. With the account holders' + Money (/trackers/money) is a private tool used only by the site owner. With the account holders' explicit consent, it reads account details, balances and booked transactions from Revolut through Enable Banking Oy, a licensed account information service provider. This data is used only to show the account holders their own finances. It is stored in a database on a server diff --git a/src/routes/projects/money-tracker/[...rest]/+server.ts b/src/routes/projects/money-tracker/[...rest]/+server.ts new file mode 100644 index 0000000..4f452f2 --- /dev/null +++ b/src/routes/projects/money-tracker/[...rest]/+server.ts @@ -0,0 +1,10 @@ +import { redirect } from '@sveltejs/kit'; +import type { RequestHandler } from './$types'; + +// Money moved from /projects/money-tracker to /trackers/money. Old links, and the Enable Banking +// redirect URL registered as /projects/money-tracker/bank-callback, keep working: 308 keeps the +// method, the subpath and the query string (?range=, ?code=&state=). +export const fallback: RequestHandler = ({ params, url }) => { + redirect(308, `/trackers/money${params.rest ? `/${params.rest}` : ''}${url.search}`); +}; +export const GET = fallback; diff --git a/src/routes/projects/money-tracker/[...rest]/redirect.test.ts b/src/routes/projects/money-tracker/[...rest]/redirect.test.ts new file mode 100644 index 0000000..466c205 --- /dev/null +++ b/src/routes/projects/money-tracker/[...rest]/redirect.test.ts @@ -0,0 +1,30 @@ +import { isRedirect } from '@sveltejs/kit'; +import { describe, expect, it } from 'vitest'; +import { fallback } from './+server'; + +function target(path: string, rest: string) { + try { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + fallback({ params: { rest }, url: new URL(`https://stanrunge.dev${path}`) } as any); + } catch (thrown) { + if (isRedirect(thrown)) return [thrown.status, thrown.location]; + throw thrown; + } + return null; +} + +describe('old money tracker URLs', () => { + it('redirects to /trackers/money, keeping subpath and query', () => { + expect(target('/projects/money-tracker?range=3y', '')).toEqual([ + 308, + '/trackers/money?range=3y' + ]); + expect(target('/projects/money-tracker/transactions?page=2', 'transactions')).toEqual([ + 308, + '/trackers/money/transactions?page=2' + ]); + expect( + target('/projects/money-tracker/bank-callback?state=s&code=abc-123', 'bank-callback') + ).toEqual([308, '/trackers/money/bank-callback?state=s&code=abc-123']); + }); +}); diff --git a/src/routes/terms/+page.svelte b/src/routes/terms/+page.svelte index 01e1045..6d8ee4b 100644 --- a/src/routes/terms/+page.svelte +++ b/src/routes/terms/+page.svelte @@ -22,10 +22,11 @@ You're responsible for activity on your account. Accounts that abuse the site may be removed. You can ask for your account to be deleted at any time.

-

Money tracker

+

Money

- The money tracker is a private tool for the site owner and household. Its bank connection is - read-only (account information via Enable Banking) and nothing shown in it is financial advice. + Money (/trackers/money) is a private tool for the site owner and household. Its bank connection + is read-only (account information via Enable Banking) and nothing shown in it is financial + advice.

Liability

diff --git a/src/routes/projects/money-tracker/+page.server.ts b/src/routes/trackers/money/+page.server.ts similarity index 99% rename from src/routes/projects/money-tracker/+page.server.ts rename to src/routes/trackers/money/+page.server.ts index 1df001e..0d51217 100644 --- a/src/routes/projects/money-tracker/+page.server.ts +++ b/src/routes/trackers/money/+page.server.ts @@ -31,7 +31,7 @@ import { error, redirect } from '@sveltejs/kit'; import type { Actions, PageServerLoad } from './$types'; export const load: PageServerLoad = async (event) => { - if (!event.locals.user) redirect(303, '/login?returnTo=/projects/money-tracker'); + if (!event.locals.user) redirect(303, '/login?returnTo=/trackers/money'); const user = requireAdmin(event); const userId = user.id; const [ diff --git a/src/routes/projects/money-tracker/+page.svelte b/src/routes/trackers/money/+page.svelte similarity index 99% rename from src/routes/projects/money-tracker/+page.svelte rename to src/routes/trackers/money/+page.svelte index a7b4d10..9463b79 100644 --- a/src/routes/projects/money-tracker/+page.svelte +++ b/src/routes/trackers/money/+page.svelte @@ -250,7 +250,7 @@ ); const link = document.createElement('a'); link.href = url; - link.download = 'money-tracker-transactions.csv'; + link.download = 'money-transactions.csv'; link.click(); setTimeout(() => URL.revokeObjectURL(url), 1000); } @@ -276,7 +276,7 @@

Personal finance

-

Money tracker

+

Money

- - Manage your money tracker records. + Manage your money records. {#if showActionError && form?.message}{/if} diff --git a/src/routes/projects/money-tracker/bank-callback/+page.server.ts b/src/routes/trackers/money/bank-callback/+page.server.ts similarity index 100% rename from src/routes/projects/money-tracker/bank-callback/+page.server.ts rename to src/routes/trackers/money/bank-callback/+page.server.ts diff --git a/src/routes/projects/money-tracker/bank-callback/+page.svelte b/src/routes/trackers/money/bank-callback/+page.svelte similarity index 88% rename from src/routes/projects/money-tracker/bank-callback/+page.svelte rename to src/routes/trackers/money/bank-callback/+page.svelte index ae2119c..6676b02 100644 --- a/src/routes/projects/money-tracker/bank-callback/+page.svelte +++ b/src/routes/trackers/money/bank-callback/+page.svelte @@ -14,10 +14,7 @@ Bank connection · Money trackerBank connection · Money
@@ -48,6 +45,6 @@

{/if}

- Back to the money tracker + Back to Money

diff --git a/src/routes/projects/money-tracker/money-tracker.css b/src/routes/trackers/money/money-tracker.css similarity index 100% rename from src/routes/projects/money-tracker/money-tracker.css rename to src/routes/trackers/money/money-tracker.css diff --git a/src/routes/projects/money-tracker/transactions/+page.server.ts b/src/routes/trackers/money/transactions/+page.server.ts similarity index 100% rename from src/routes/projects/money-tracker/transactions/+page.server.ts rename to src/routes/trackers/money/transactions/+page.server.ts diff --git a/src/routes/projects/money-tracker/transactions/+page.svelte b/src/routes/trackers/money/transactions/+page.svelte similarity index 94% rename from src/routes/projects/money-tracker/transactions/+page.svelte rename to src/routes/trackers/money/transactions/+page.svelte index d5350ba..ade6f9e 100644 --- a/src/routes/projects/money-tracker/transactions/+page.svelte +++ b/src/routes/trackers/money/transactions/+page.svelte @@ -36,7 +36,7 @@ else params.set(key, String(value)); } const query = params.toString(); - return resolve('/projects/money-tracker/transactions') + (query ? '?' + query : ''); + return resolve('/trackers/money/transactions') + (query ? '?' + query : ''); } function submit(event: SubmitEvent) { event.preventDefault(); @@ -45,7 +45,7 @@ for (const [key, value] of form) if (typeof value === 'string' && value) params.append(key, value); const query = params.toString(); - goto(resolve('/projects/money-tracker/transactions') + (query ? '?' + query : ''), { + goto(resolve('/trackers/money/transactions') + (query ? '?' + query : ''), { keepFocus: true, noScroll: true }); @@ -55,14 +55,14 @@ } -Transactions · Money tracker +Transactions · Money
Money tracker Money

Transactions

@@ -138,7 +138,7 @@

Reset
diff --git a/tests/game-tracker.test.ts b/tests/game-tracker.test.ts index 26e6b51..8ed27df 100644 --- a/tests/game-tracker.test.ts +++ b/tests/game-tracker.test.ts @@ -365,7 +365,7 @@ test('shared dialogs have focus, Escape dismissal and mobile layouts without ove await expect(dialog).toHaveCount(0); await expect(page.getByRole('button', { name: label, exact: true })).toBeFocused(); } - await page.goto('/projects/money-tracker'); + await page.goto('/trackers/money'); await page.getByRole('heading', { name: 'Accounts', exact: true }).waitFor(); await page.locator('#accounts').getByRole('button', { name: 'Add', exact: true }).click(); const moneyDialog = page.getByRole('dialog');