Ctrl K
ComunidadGuías…

server/controllers/views.ts

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import type express from 'express';
import { asyncErr, KError } from '../helpers';
import { Account, View } from '../models';

import { isDemoEnabled } from './instance';
import type { IdentifiedRequest, PreloadedRequest } from './routes';

// Prefills the @view field with a queried bank account.
export async function preloadView(
    req: IdentifiedRequest<View>,
    res: express.Response,
    nextHandler: () => void,
    viewId: number
) {
    try {
        const { id: userId } = req.user;
        const view = await View.find(userId, viewId);
        if (!view) {
            throw new KError('View not found', 404);
        }

        if (!req.preloaded) {
            req.preloaded = {};
        }

        req.preloaded.view = view;
        nextHandler();
    } catch (err) {
        asyncErr(res, err, 'when preloading a view');
    }
}

async function checkViewAccounts(userId: number, accounts: any): Promise<void> {
    const accountIds: number[] = [];
    if (accounts instanceof Array) {
        for (const viewAccount of accounts) {
            if (typeof viewAccount?.accountId !== 'number') {
                throw new KError('a view account must have a numeric accountId', 400);
            }

            if (accountIds.includes(viewAccount.accountId)) {
                throw new KError('a view cannot include an account several times', 400);
            }

            accountIds.push(viewAccount.accountId);
        }
    }

    if (!accountIds.length) {
        throw new KError('a view should have at least one account', 400);
    }

    const accountsFromIds = await Account.findMany(userId, accountIds);
    if (accountsFromIds.length !== accountIds.length) {
        throw new KError('some view accounts could not be found', 404);
    }

    const currencies = new Set(
        await Promise.all(accountsFromIds.map(account => account.getCurrency()))
    );
    if (currencies.size > 1) {
        throw new KError('a view cannot contain accounts of different currencies', 400);
    }
}

export async function create(req: IdentifiedRequest<any>, res: express.Response) {
    try {
        const { id: userId } = req.user;

        const newView = req.body;
        if (!newView || typeof newView.label !== 'string') {
            throw new KError('missing parameters', 400);
        }

        await checkViewAccounts(userId, newView.accounts);

        const view = await View.create(userId, newView);
        res.status(201).json(view);
    } catch (err) {
        asyncErr(res, err, 'when creating a view');
    }
}

export async function update(req: PreloadedRequest<View>, res: express.Response) {
    try {
        const { id: userId } = req.user;

        const newFields = req.body;
        const view = req.preloaded.view;

        // Always validate, even for a plain rename: a view which already mixes currencies (it
        // may predate this restriction, or an account's currency may have changed on the bank's
        // side) must be fixed by the user before it can be modified at all.
        const accounts =
            newFields && typeof newFields.accounts !== 'undefined'
                ? newFields.accounts
                : view.accounts;
        await checkViewAccounts(userId, accounts);

        const newView = await View.update(userId, view.id, newFields);
        res.status(200).json(newView);
    } catch (err) {
        asyncErr(res, err, 'when updating a view');
    }
}

export async function destroy(req: PreloadedRequest<View>, res: express.Response) {
    try {
        const { id: userId } = req.user;

        if (await isDemoEnabled(userId)) {
            throw new KError("view deletion isn't allowed in demo mode", 400);
        }

        const view = req.preloaded.view;
        await View.destroy(userId, view.id);
        res.status(204).end();
    } catch (err) {
        asyncErr(res, err, 'when destroying a view');
    }
}