import type express from 'express'; import { translate as $t, assert, asyncErr, KError } from '../helpers'; import { getTranslator } from '../lib/translator'; import { Access, Budget, Category, Setting } from '../models'; import DefaultCategories from '../shared/default-categories.json'; import { DEMO_MODE } from '../shared/settings'; import { type CreateAndRetrieveDataResult, createAndRetrieveData as createAndRetrieveAccessData, destroyWithData as destroyAccessWithData, } from './accesses'; import { isDemoEnabled, isDemoForced } from './instance'; import type { IdentifiedRequest } from './routes'; export async function setupDemoMode(userId: number): Promise { const i18n = await getTranslator(userId); // Create default categories, unless they already existed. const existingCategories = new Set((await Category.all(userId)).map(cat => cat.label)); for (const category of DefaultCategories) { if (existingCategories.has($t(i18n, category.label))) { continue; } await Category.create(userId, { label: $t(i18n, category.label), color: category.color, }); } const response = await createAndRetrieveAccessData(userId, { vendorId: 'demo', fields: [ { name: 'login', value: 'mylogin' }, { name: 'password', value: 'couldnotcareless' }, ], customLabel: 'Demo bank', }); assert(response.kind === 'value', "demo account shouldn't require a user action"); const data = response.value; // Set the demo mode to true only if other actions succeeded. const isEnabled = await Setting.findOrCreateByKey(userId, DEMO_MODE, 'true'); if (isEnabled.value !== 'true') { // The setting already existed and has the wrong value. await Setting.updateByKey(userId, DEMO_MODE, 'true'); } return data; } export async function enable(req: IdentifiedRequest, res: express.Response) { try { const { id: userId } = req.user; const isEnabled = await isDemoEnabled(userId); if (isEnabled) { throw new KError('Demo mode is already enabled, not enabling it.', 400); } const accesses = await Access.all(userId); if (accesses.length > 0) { throw new KError('Demo mode cannot be enabled if there already are accesses', 400); } const data = await setupDemoMode(userId); res.status(201).json(data); } catch (err) { asyncErr(res, err, 'when enabling demo mode'); } } export async function disable(req: IdentifiedRequest, res: express.Response) { try { const { id: userId } = req.user; if (isDemoForced()) { throw new KError('Demo mode is forced at the server level, not disabling it.', 400); } const isEnabled = await isDemoEnabled(userId); if (!isEnabled) { throw new KError('Demo mode was not enabled, not disabling it.', 400); } const accesses = await Access.all(userId); for (const acc of accesses) { await destroyAccessWithData(userId, acc); } // Keep the categories (and rules), in case the user created // interesting ones. Delete all the budgets, though. await Budget.destroyAll(userId); // Only reset the setting value if all the destroy actions succeeded. await Setting.updateByKey(userId, DEMO_MODE, 'false'); res.status(200).end(); } catch (err) { asyncErr(res, err, 'when disabling demo mode'); } }