Files
RecipesCam/docker/backend/package.json
T
3dtours d2e2ebe108 feat(immich): read Immich through a per-user read-only proxy
The browser cannot talk to Immich directly: the key must stay out of it,
COEP blocks the origin, and the app has no place to keep a key per user.
So the backend keeps it. `src/immich.ts` holds the whole surface — the
user's servers live in a JSON column on `users` (additive migration), and
every route reads the key from there and never takes a URL from the
browser except when probing one.

Albums, a page of assets, a thumbnail and an original, all behind the
normal session check. `probe` is the only route that touches a URL the
client named, and it validates it first (http/https only, no credentials,
no path, no query, no hash) so the browser cannot turn the backend into a
proxy to an arbitrary host. The key is masked down to its last four
characters everywhere it comes back out, and no log line carries it.

The share-link path is the same routes with `type: 'share'`, whose key
travels as `?key=`, so there is one code path per call rather than two.

test/immich.mjs runs a fake Immich on loopback — two keys with different
albums, one of them without `asset.download` — and checks 59 things
including that neither the responses nor the log leak a key.
2026-10-10 15:52:42 +07:00

27 lines
686 B
JSON

{
"name": "recipescam-api",
"version": "1.0.0",
"private": true,
"description": "RecipesCam web API - accounts + user recipes (server never receives photos)",
"main": "dist/server.js",
"scripts": {
"build": "tsc",
"start": "node dist/server.js",
"dev": "tsx watch src/server.ts",
"test": "node test/security.mjs",
"test:immich": "node test/immich.mjs"
},
"dependencies": {
"better-sqlite3": "^12.11.1",
"fastify": "^5.12.5",
"nodemailer": "^7.0.13"
},
"devDependencies": {
"@types/better-sqlite3": "^9.6.0",
"@types/node": "^22.20.3",
"@types/nodemailer": "^8.0.2",
"tsx": "^4.23.13",
"typescript": "^5.9.3"
}
}