/* * server component for the TimeLimit App * Copyright (C) 2019 - 2022 Jonas Lochmann * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ import * as Sequelize from 'sequelize' import { AddDeviceTokenModelStatic, createAddDeviceTokenModel } from './adddevicetoken' import { AuthTokenModelStatic, createAuthtokenModel } from './authtoken' import { CategoryModelStatic, createCategoryModel } from './category' import { CategoryAppModelStatic, createCategoryAppModel } from './categoryapp' import { CategoryNetworkIdModelStatic, createCategoryNetworkIdModel } from './categorynetworkid' import { CategoryTimeWarningModelStatic, createCategoryTimeWarningModel } from './categorytimewarning' import { ChildTaskModelStatic, createChildTaskModel } from './childtask' import { ConfigModelStatic, createConfigModel } from './config' import { createDeviceModel, DeviceModelStatic } from './device' import { createDeviceDhKey, DeviceDhKeyModelStatic } from './devicedhkey' import { createEncryptedAppListModel, EncryptedAppListModelStatic } from './encryptedapplist' import { createFamilyModel, FamilyModelStatic } from './family' import { createKeyRequestModel, KeyRequestModelStatic } from './keyrequest' import { createKeyResponseModel, KeyResponseModelStatic } from './keyresponse' import { createMailLoginTokenModel, MailLoginTokenModelStatic } from './maillogintoken' import { createUmzug } from './migration/umzug' import { createOldDeviceModel, OldDeviceModelStatic } from './olddevice' import { createPurchaseModel, PurchaseModelStatic } from './purchase' import { createSessionDurationModel, SessionDurationModelStatic } from './sessionduration' import { createTimelimitRuleModel, TimelimitRuleModelStatic } from './timelimitrule' import { createU2fKeyModel, U2fKeyModelStatic } from './u2fkey' import { createUsedTimeModel, UsedTimeModelStatic } from './usedtime' import { createUserModel, UserModelStatic } from './user' import { createUserLimitLoginCategoryModel, UserLimitLoginCategoryModelStatic } from './userlimitlogincategory' import { shouldRetryWithException } from './utils/serialized' export type Transaction = Sequelize.Transaction export interface Database { addDeviceToken: AddDeviceTokenModelStatic authtoken: AuthTokenModelStatic category: CategoryModelStatic categoryApp: CategoryAppModelStatic categoryNetworkId: CategoryNetworkIdModelStatic categoryTimeWarning: CategoryTimeWarningModelStatic childTask: ChildTaskModelStatic config: ConfigModelStatic device: DeviceModelStatic deviceDhKey: DeviceDhKeyModelStatic encryptedAppList: EncryptedAppListModelStatic family: FamilyModelStatic keyRequest: KeyRequestModelStatic keyResponse: KeyResponseModelStatic mailLoginToken: MailLoginTokenModelStatic oldDevice: OldDeviceModelStatic purchase: PurchaseModelStatic sessionDuration: SessionDurationModelStatic timelimitRule: TimelimitRuleModelStatic u2fKey: U2fKeyModelStatic usedTime: UsedTimeModelStatic user: UserModelStatic userLimitLoginCategory: UserLimitLoginCategoryModelStatic transaction: ( autoCallback: (t: Transaction) => Promise, options?: TransactionOptions ) => Promise dialect: string } interface TransactionOptions { transaction?: Transaction disableRetry?: boolean } const createDatabase = (sequelize: Sequelize.Sequelize): Database => ({ addDeviceToken: createAddDeviceTokenModel(sequelize), authtoken: createAuthtokenModel(sequelize), category: createCategoryModel(sequelize), categoryApp: createCategoryAppModel(sequelize), childTask: createChildTaskModel(sequelize), categoryNetworkId: createCategoryNetworkIdModel(sequelize), categoryTimeWarning: createCategoryTimeWarningModel(sequelize), config: createConfigModel(sequelize), device: createDeviceModel(sequelize), deviceDhKey: createDeviceDhKey(sequelize), encryptedAppList: createEncryptedAppListModel(sequelize), family: createFamilyModel(sequelize), keyRequest: createKeyRequestModel(sequelize), keyResponse: createKeyResponseModel(sequelize), mailLoginToken: createMailLoginTokenModel(sequelize), oldDevice: createOldDeviceModel(sequelize), purchase: createPurchaseModel(sequelize), sessionDuration: createSessionDurationModel(sequelize), timelimitRule: createTimelimitRuleModel(sequelize), u2fKey: createU2fKeyModel(sequelize), usedTime: createUsedTimeModel(sequelize), user: createUserModel(sequelize), userLimitLoginCategory: createUserLimitLoginCategoryModel(sequelize), async transaction( autoCallback: (transaction: Transaction) => Promise, options?: TransactionOptions ): Promise { const runAttempt = () => sequelize.transaction({ isolationLevel: Sequelize.Transaction.ISOLATION_LEVELS.SERIALIZABLE, transaction: options?.transaction }, autoCallback) const delay = (time: number) => new Promise((resolve) => setTimeout(resolve, time)) try { return await runAttempt() } catch (ex) { if ( options?.disableRetry || options?.transaction || !shouldRetryWithException(this, ex) ) throw ex } await delay(10 * (1 + Math.random())) try { return await runAttempt() } catch (ex) { if (!shouldRetryWithException(this, ex)) throw ex } await delay(100 * (1 + Math.random())) return await runAttempt() }, dialect: sequelize.getDialect() }) export const sequelize = new Sequelize.Sequelize(process.env.DATABASE_URL || 'sqlite://test.db', { define: { timestamps: false }, logging: false }) export const defaultDatabase = createDatabase(sequelize) export const defaultUmzug = createUmzug(sequelize)