Spaces:
Sleeping
Sleeping
assafvayner HF Staff
fix(app): 503 on storage failures, validated currency, lock release on shutdown, per-IP login limit, unit-only npm test
e830ad4 | import { error, isActionFailure, isHttpError, isRedirect, type Action } from '@sveltejs/kit'; | |
| import { ReadOnlyError } from './ledger/errors'; | |
| const STORAGE_MESSAGE = 'Could not save — the storage backend is unavailable. Please try again.'; | |
| const READ_ONLY_MESSAGE = 'The app is temporarily read-only; try again in a couple of minutes.'; | |
| /** | |
| * Wraps every action so an unexpected throw (bucket down, disk read-only, scrypt overload) | |
| * surfaces as a 503 the user can act on instead of a bare 500. Redirects, HttpErrors and | |
| * `fail()` results are the action's own control flow and pass through untouched. | |
| */ | |
| export function guarded<T extends Record<string, Action<any, any, any>>>(actions: T): T { | |
| const wrapped: Record<string, Action<any, any, any>> = {}; | |
| for (const [name, action] of Object.entries(actions)) { | |
| wrapped[name] = async (event) => { | |
| try { | |
| return await action(event); | |
| } catch (e) { | |
| if (isRedirect(e) || isHttpError(e) || isActionFailure(e)) throw e; | |
| if (e instanceof ReadOnlyError) error(503, READ_ONLY_MESSAGE); | |
| console.error('[action] storage failure', e); | |
| error(503, STORAGE_MESSAGE); | |
| } | |
| }; | |
| } | |
| return wrapped as T; | |
| } | |