Ctrl K
ComunidadGuías…

server/models/index.ts

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

Archivo original ↗Preguntar a KiBot
import * as path from 'path';
import {
    DataSource,
    type DataSourceOptions,
    type EntityManager,
    type EntityTarget,
    type ObjectLiteral,
    type Repository,
} from 'typeorm';

import { assert, makeLogger, panic } from '../helpers';
import AccessField from './entities/access-fields';
import Access from './entities/accesses';
import Account from './entities/accounts';
import Alert from './entities/alerts';
import AppliedRecurringTransaction from './entities/applied-recurring-transactions';
import Budget from './entities/budgets';
import Category from './entities/categories';
import DuplicatesIgnored from './entities/duplicates-ignored';
import RecurringTransaction from './entities/recurring-transactions';
import Setting from './entities/settings';
import TransactionRule from './entities/transaction-rule';
import TransactionRuleAction from './entities/transaction-rule-action';
import TransactionRuleCondition from './entities/transaction-rule-condition';
import Transaction from './entities/transactions';
import User from './entities/users';
import View from './entities/views';

type MinimalTransaction = Partial<Transaction> &
    Pick<Transaction, 'date' | 'importDate' | 'amount' | 'rawLabel'>;

export {
    Access,
    AccessField,
    Account,
    Alert,
    AppliedRecurringTransaction,
    Budget,
    Category,
    DuplicatesIgnored,
    type MinimalTransaction,
    RecurringTransaction,
    Setting,
    Transaction,
    TransactionRule,
    TransactionRuleAction,
    TransactionRuleCondition,
    User,
    View,
};

const log = makeLogger('models/index');
let dataSource: DataSource | null;

function makeOrmConfig(): DataSourceOptions {
    let ormConfig: DataSourceOptions;

    // Keep this switch in sync with ../config.ts!
    switch (process.kresus.dbType) {
        case 'sqlite':
            assert(process.kresus.sqlitePath !== null, 'missing db path in server/models');
            ormConfig = {
                type: 'better-sqlite3',
                database: process.kresus.sqlitePath,
                logging: process.kresus.dbLog,
            };
            break;

        case 'postgres':
            assert(typeof process.kresus.dbHost === 'string', 'missing db host in server/models');
            assert(typeof process.kresus.dbPort === 'number', 'missing db port in server/models');
            assert(
                typeof process.kresus.dbUsername === 'string',
                'missing db username in server/models'
            );
            assert(
                typeof process.kresus.dbPassword === 'string',
                'missing db password in server/models'
            );
            assert(typeof process.kresus.dbName === 'string', 'missing db name in server/models');
            ormConfig = {
                type: process.kresus.dbType,
                host: process.kresus.dbHost,
                port: process.kresus.dbPort,
                username: process.kresus.dbUsername,
                password: process.kresus.dbPassword,
                database: process.kresus.dbName,
                logging: process.kresus.dbLog,
            };
            break;

        default:
            panic('unexpected db type in server/models');
    }

    return ormConfig;
}

export async function setupOrm(): Promise<DataSource> {
    const ormConfig = Object.assign(makeOrmConfig(), {
        // Automatically run migrations.
        migrationsRun: true,

        // Entity models.
        entities: [path.join(__dirname, 'entities/*')],

        // Subscribers.
        subscribers: [path.join(__dirname, 'subscribers/*')],

        // Migration files.
        migrations: [path.join(__dirname, 'migrations/*')],

        // Use one transaction by migration, avoiding issues on promises hanging after alter table.
        migrationsTransactionMode: 'each',

        // Automatically synchronize the database schema on startup. Very
        // unsafe, use only to look at queries generated by the ORM.
        synchronize: false,
    });

    dataSource = new DataSource(ormConfig);
    await dataSource.initialize();
    log.info('database is ready');
    return dataSource;
}

export function getRepository<T extends ObjectLiteral>(x: EntityTarget<T>): Repository<T> {
    if (dataSource === null || typeof dataSource === 'undefined') {
        panic('Expected data source to be initialized');
    }
    return dataSource.getRepository(x);
}

export function getManager(): EntityManager {
    if (dataSource === null || typeof dataSource === 'undefined') {
        panic('Expected data source to be initialized');
    }
    return dataSource.manager;
}

export async function initModels() {
    const { userLoginHttpHeader } = process.kresus;

    dataSource = await setupOrm();

    // Create default user.
    let defaultUser: User | null;
    if (process.kresus.providedUserId !== null) {
        const userId = process.kresus.providedUserId;
        // Check that the user actually exists already.
        defaultUser = await User.find(userId);
        if (!defaultUser) {
            throw new Error(
                `The user with provided ID ${userId} doesn't exist. Did you run "kresus create:user" first?`
            );
        }
    } else {
        const users: User[] = await User.all();
        if (!users.length) {
            const { login } = process.kresus.defaultUser;
            assert(!!login, 'There should be a default login set!');
            log.info('Creating default user as administrator...');
            defaultUser = await User.create({ login, isAdmin: true });
        } else if (users.length > 1 && !userLoginHttpHeader) {
            throw new Error(
                'Several users in database but "userLoginHttpHeader" was not configured. Please configure the userLoginHttpHeader variable in config.ini and setup a reverse proxy accordingly.'
            );
        } else {
            defaultUser = users[0];
        }
    }

    process.kresus.defaultUser = defaultUser;
    log.info(
        `Default user “${defaultUser.login}“ is set with id ${defaultUser.id} (admin: ${defaultUser.isAdmin})`
    );
}