diff --git a/src/data-source.ts b/src/data-source.ts index ec98baf..51d6659 100644 --- a/src/data-source.ts +++ b/src/data-source.ts @@ -46,6 +46,7 @@ import { webapiPermission } from "./entity/user/webapi_permission"; import { salutation } from "./entity/settings/salutation"; import { BackupAndResetDatabase1738166124200 } from "./migrations/1738166124200-BackupAndResetDatabase"; +import { CreateSchema1738166167472 } from "./migrations/1738166167472-CreateSchema"; const dataSource = new DataSource({ type: DB_TYPE as any, @@ -99,47 +100,10 @@ const dataSource = new DataSource({ webapi, webapiPermission, ], - migrations: [BackupAndResetDatabase1738166124200], + migrations: [BackupAndResetDatabase1738166124200, CreateSchema1738166167472], migrationsRun: true, migrationsTransactionMode: "each", subscribers: [], }); -type ORMType = "int" | "bigint" | "boolean" | "date" | "datetime" | "text" | "varchar"; -function getTypeByORM(type: ORMType): string { - const dbType = process.env.DB_TYPE; - - const typeMap: Record> = { - mysql: { - int: "int", - bigint: "bigint", - boolean: "tinyint", - date: "date", - datetime: "datetime", - text: "text", - varchar: "varchar", - }, - postgres: { - int: "integer", - bigint: "bigint", - boolean: "boolean", - date: "date", - datetime: "timestamp", - text: "text", - varchar: "varchar", - }, - sqlite: { - int: "integer", - bigint: "integer", - boolean: "integer", - date: "text", - datetime: "text", - text: "text", - varchar: "text", - }, - }; - - return typeMap[dbType]?.[type] || type; -} - -export { dataSource, getTypeByORM }; +export { dataSource }; diff --git a/src/helpers/backupHelper.ts b/src/helpers/backupHelper.ts index 6d18f7a..a7fbc1d 100644 --- a/src/helpers/backupHelper.ts +++ b/src/helpers/backupHelper.ts @@ -150,7 +150,7 @@ export default abstract class BackupHelper { (a, b) => new Date(b.split(".")[0]).getTime() - new Date(a.split(".")[0]).getTime() )[0]; - if (!newestFile) { + if (newestFile) { console.log(`${new Date().toISOString()}: auto-restoring ${newestFile}`); await this.loadBackup({ filename: newestFile }); console.log(`${new Date().toISOString()}: finished auto-restore`); @@ -652,7 +652,7 @@ export default abstract class BackupHelper { .insert() .into("template_usage") .values(data?.["template_usage"] ?? []) - .orIgnore() + .orUpdate(["headerId", "bodyId", "footerId", "headerHeight", "footerHeight"], ["scope"]) .execute(); } private static async setUser(data: { [key: string]: Array }): Promise { diff --git a/src/migrations/1738166124200-BackupAndResetDatabase.ts b/src/migrations/1738166124200-BackupAndResetDatabase.ts index 930d7cc..f394fc8 100644 --- a/src/migrations/1738166124200-BackupAndResetDatabase.ts +++ b/src/migrations/1738166124200-BackupAndResetDatabase.ts @@ -1,18 +1,19 @@ import { MigrationInterface, QueryRunner, Table } from "typeorm"; import BackupHelper from "../helpers/backupHelper"; -import { getTypeByORM } from "../data-source"; +import { getTypeByORM } from "./ormHelper"; import InternalException from "../exceptions/internalException"; export class BackupAndResetDatabase1738166124200 implements MigrationInterface { name = "BackupAndResetDatabase1738166124200"; public async up(queryRunner: QueryRunner): Promise { - if ((await queryRunner.hasTable("user")) && !(await queryRunner.hasTable("salutation"))) + if ((await queryRunner.hasTable("user")) && !(await queryRunner.hasTable("salutation"))) { throw new InternalException("Cannot update due to skiped version, resulting in data loss"); + } - if (await queryRunner.hasTable("user")) - await BackupHelper.createBackup({ filename: "schema change", path: "migration", collectIds: false }); - + if (await queryRunner.hasTable("user")) { + await BackupHelper.createBackup({ collectIds: false }); + } await queryRunner.clearDatabase(); await queryRunner.createTable( diff --git a/src/migrations/1738166167472-CreateSchema.ts b/src/migrations/1738166167472-CreateSchema.ts new file mode 100644 index 0000000..0eafa18 --- /dev/null +++ b/src/migrations/1738166167472-CreateSchema.ts @@ -0,0 +1,160 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; +import { + invite_table, + refresh_table, + reset_table, + role_permission_table, + role_table, + user_permission_table, + user_roles_table, + user_table, + webapi_permission_table, + webapi_table, +} from "./baseSchemaTables/admin"; +import { templateUsage } from "../entity/settings/templateUsage"; +import { + award_table, + communication_type_table, + executive_position_table, + member_awards_table, + member_communication_table, + member_executive_positions_table, + member_executive_positions_view, + member_qualifications_table, + member_qualifications_view, + member_table, + member_view, + membership_status_table, + membership_table, + membership_view, + qualification_table, + salutation_table, +} from "./baseSchemaTables/member"; +import { query_table, template_table, template_usage_table } from "./baseSchemaTables/query_template"; +import { + protocol_agenda_table, + protocol_decision_table, + protocol_presence_table, + protocol_printout_table, + protocol_table, + protocol_voting_table, +} from "./baseSchemaTables/protocol"; +import { calendar_table, calendar_type_table } from "./baseSchemaTables/calendar"; +import { + newsletter_config_table, + newsletter_dates_table, + newsletter_recipients_table, + newsletter_table, +} from "./baseSchemaTables/newsletter"; + +export class CreateSchema1738166167472 implements MigrationInterface { + name = "CreateSchema1738166167472"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.createTable(reset_table, true, true, true); + await queryRunner.createTable(invite_table, true, true, true); + await queryRunner.createTable(role_table, true, true, true); + await queryRunner.createTable(role_permission_table, true, true, true); + await queryRunner.createTable(user_table, true, true, true); + await queryRunner.createTable(user_roles_table, true, true, true); + await queryRunner.createTable(user_permission_table, true, true, true); + await queryRunner.createTable(refresh_table, true, true, true); + await queryRunner.createTable(webapi_table, true, true, true); + await queryRunner.createTable(webapi_permission_table, true, true, true); + + await queryRunner.createTable(salutation_table, true, true, true); + await queryRunner.createTable(award_table, true, true, true); + await queryRunner.createTable(communication_type_table, true, true, true); + await queryRunner.createTable(membership_status_table, true, true, true); + await queryRunner.createTable(executive_position_table, true, true, true); + await queryRunner.createTable(qualification_table, true, true, true); + await queryRunner.createTable(member_table, true, true, true); + await queryRunner.createTable(member_awards_table, true, true, true); + await queryRunner.createTable(member_communication_table, true, true, true); + await queryRunner.createTable(membership_table, true, true, true); + await queryRunner.createTable(member_executive_positions_table, true, true, true); + await queryRunner.createTable(member_qualifications_table, true, true, true); + + await queryRunner.createView(member_view, true); + await queryRunner.createView(membership_view, true); + await queryRunner.createView(member_qualifications_view, true); + await queryRunner.createView(member_executive_positions_view, true); + + await queryRunner.createTable(query_table, true, true, true); + await queryRunner.createTable(template_table, true, true, true); + await queryRunner.createTable(template_usage_table, true, true, true); + + await queryRunner.manager + .createQueryBuilder() + .insert() + .into(templateUsage) + .values([{ scope: "newsletter" }, { scope: "protocol" }, { scope: "member.list" }]) + .orUpdate(["headerId", "bodyId", "footerId", "headerHeight", "footerHeight"], ["scope"]) + .execute(); + + await queryRunner.createTable(protocol_table, true, true, true); + await queryRunner.createTable(protocol_agenda_table, true, true, true); + await queryRunner.createTable(protocol_decision_table, true, true, true); + await queryRunner.createTable(protocol_presence_table, true, true, true); + await queryRunner.createTable(protocol_voting_table, true, true, true); + await queryRunner.createTable(protocol_printout_table, true, true, true); + + await queryRunner.createTable(calendar_type_table, true, true, true); + await queryRunner.createTable(calendar_table, true, true, true); + + await queryRunner.createTable(newsletter_config_table, true, true, true); + await queryRunner.createTable(newsletter_table, true, true, true); + await queryRunner.createTable(newsletter_dates_table, true, true, true); + await queryRunner.createTable(newsletter_recipients_table, true, true, true); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.dropTable("newsletter_dates", true, true, true); + await queryRunner.dropTable("newsletter_recipients", true, true, true); + await queryRunner.dropTable("newsletter", true, true, true); + await queryRunner.dropTable("newsletter_config", true, true, true); + + await queryRunner.dropTable("calendar", true, true, true); + await queryRunner.dropTable("calendar_type", true, true, true); + + await queryRunner.dropTable("protocol_agenda", true, true, true); + await queryRunner.dropTable("protocol_decision", true, true, true); + await queryRunner.dropTable("protocol_presence", true, true, true); + await queryRunner.dropTable("protocol_voting", true, true, true); + await queryRunner.dropTable("protocol_printout", true, true, true); + await queryRunner.dropTable("protocol", true, true, true); + + await queryRunner.dropTable("template_usage", true, true, true); + await queryRunner.dropTable("template", true, true, true); + await queryRunner.dropTable("query", true, true, true); + + await queryRunner.dropView("member_view"); + await queryRunner.dropView("membership_view"); + await queryRunner.dropView("member_qualifications_view"); + await queryRunner.dropView("member_executive_positions_view"); + + await queryRunner.dropTable("member_awards", true, true, true); + await queryRunner.dropTable("communication", true, true, true); + await queryRunner.dropTable("membership", true, true, true); + await queryRunner.dropTable("member_executive_positions", true, true, true); + await queryRunner.dropTable("member_qualifications", true, true, true); + await queryRunner.dropTable("member", true, true, true); + await queryRunner.dropTable("salutation", true, true, true); + await queryRunner.dropTable("award", true, true, true); + await queryRunner.dropTable("communication_type", true, true, true); + await queryRunner.dropTable("membership_status", true, true, true); + await queryRunner.dropTable("executive_position", true, true, true); + await queryRunner.dropTable("qualification", true, true, true); + + await queryRunner.dropTable("webapi_permission", true, true, true); + await queryRunner.dropTable("webapi", true, true, true); + await queryRunner.dropTable("refresh", true, true, true); + await queryRunner.dropTable("user_permission", true, true, true); + await queryRunner.dropTable("user_roles", true, true, true); + await queryRunner.dropTable("user", true, true, true); + await queryRunner.dropTable("role_permission", true, true, true); + await queryRunner.dropTable("role", true, true, true); + await queryRunner.dropTable("invite", true, true, true); + await queryRunner.dropTable("reset", true, true, true); + } +} diff --git a/src/migrations/baseSchemaTables/admin.ts b/src/migrations/baseSchemaTables/admin.ts new file mode 100644 index 0000000..23dbca9 --- /dev/null +++ b/src/migrations/baseSchemaTables/admin.ts @@ -0,0 +1,168 @@ +import { Table, TableForeignKey } from "typeorm"; +import { getTypeByORM } from "../ormHelper"; + +export const invite_table = new Table({ + name: "invite", + columns: [ + { name: "mail", type: getTypeByORM("varchar"), length: "255", isPrimary: true }, + { name: "token", type: getTypeByORM("varchar"), length: "255", isNullable: false }, + { name: "username", type: getTypeByORM("varchar"), length: "255", isNullable: false }, + { name: "firstname", type: getTypeByORM("varchar"), length: "255", isNullable: false }, + { name: "lastname", type: getTypeByORM("varchar"), length: "255", isNullable: false }, + { name: "secret", type: getTypeByORM("varchar"), length: "255", isNullable: false }, + ], +}); +export const role_table = new Table({ + name: "role", + columns: [ + { + name: "id", + type: getTypeByORM("int"), + isPrimary: true, + isGenerated: true, + generationStrategy: "increment", + }, + { name: "role", type: getTypeByORM("varchar"), length: "255", isNullable: false, isUnique: true }, + ], +}); + +export const role_permission_table = new Table({ + name: "role_permission", + columns: [ + { name: "roleId", type: getTypeByORM("int"), isPrimary: true }, + { name: "permission", type: getTypeByORM("varchar"), length: "255", isPrimary: true }, + ], + foreignKeys: [ + new TableForeignKey({ + columnNames: ["roleId"], + referencedColumnNames: ["id"], + referencedTableName: "role", + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }), + ], +}); + +export const user_table = new Table({ + name: "user", + columns: [ + { + name: "id", + type: getTypeByORM("varchar"), + length: "36", + isPrimary: true, + isGenerated: true, + generationStrategy: "uuid", + }, + { name: "mail", type: getTypeByORM("varchar"), length: "255", isNullable: false, isUnique: true }, + { name: "username", type: getTypeByORM("varchar"), length: "255", isNullable: false, isUnique: true }, + { name: "firstname", type: getTypeByORM("varchar"), length: "255", isNullable: false }, + { name: "lastname", type: getTypeByORM("varchar"), length: "255", isNullable: false }, + { name: "secret", type: getTypeByORM("varchar"), length: "255", isNullable: false }, + { name: "isOwner", type: getTypeByORM("boolean"), isNullable: false, default: false }, + ], +}); + +export const user_roles_table = new Table({ + name: "user_roles", + columns: [ + { name: "userId", type: getTypeByORM("varchar"), isPrimary: true }, + { name: "roleId", type: getTypeByORM("int"), isPrimary: true }, + ], + foreignKeys: [ + new TableForeignKey({ + columnNames: ["userId"], + referencedColumnNames: ["id"], + referencedTableName: "user", + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }), + new TableForeignKey({ + columnNames: ["roleId"], + referencedColumnNames: ["id"], + referencedTableName: "role", + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }), + ], +}); + +export const user_permission_table = new Table({ + name: "user_permission", + columns: [ + { name: "userId", type: getTypeByORM("varchar"), isPrimary: true }, + { name: "permission", type: getTypeByORM("varchar"), length: "255", isPrimary: true }, + ], + foreignKeys: [ + new TableForeignKey({ + columnNames: ["userId"], + referencedColumnNames: ["id"], + referencedTableName: "user", + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }), + ], +}); + +export const refresh_table = new Table({ + name: "refresh", + columns: [ + { name: "token", type: getTypeByORM("varchar"), length: "255", isPrimary: true, isNullable: false }, + { name: "expiry", type: getTypeByORM("datetime"), isNullable: false }, + { name: "userId", type: getTypeByORM("varchar"), isPrimary: true, isNullable: false }, + ], + foreignKeys: [ + new TableForeignKey({ + columnNames: ["userId"], + referencedColumnNames: ["id"], + referencedTableName: "user", + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }), + ], +}); + +export const webapi_table = new Table({ + name: "webapi", + columns: [ + { + name: "id", + type: getTypeByORM("int"), + isPrimary: true, + isGenerated: true, + generationStrategy: "increment", + }, + { name: "token", type: getTypeByORM("varchar"), length: "255", isNullable: false, isUnique: true }, + { name: "title", type: getTypeByORM("varchar"), length: "255", isNullable: false, isUnique: true }, + { name: "createdAt", type: getTypeByORM("datetime"), isNullable: false, default: "CURRENT_TIMESTAMP(6)" }, + { name: "lastUsage", type: getTypeByORM("datetime"), isNullable: true, default: null }, + { name: "expiry", type: getTypeByORM("date"), isNullable: true, default: null }, + ], +}); + +export const webapi_permission_table = new Table({ + name: "webapi_permission", + columns: [ + { name: "webapiId", type: getTypeByORM("int"), isPrimary: true }, + { name: "permission", type: getTypeByORM("varchar"), length: "255", isPrimary: true }, + ], + foreignKeys: [ + new TableForeignKey({ + columnNames: ["webapiId"], + referencedColumnNames: ["id"], + referencedTableName: "webapi", + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }), + ], +}); + +export const reset_table = new Table({ + name: "reset", + columns: [ + { name: "mail", type: getTypeByORM("varchar"), length: "255", isPrimary: true }, + { name: "token", type: getTypeByORM("varchar"), length: "255", isNullable: false }, + { name: "username", type: getTypeByORM("varchar"), length: "255", isNullable: false }, + { name: "secret", type: getTypeByORM("varchar"), length: "255", isNullable: false }, + ], +}); diff --git a/src/migrations/baseSchemaTables/calendar.ts b/src/migrations/baseSchemaTables/calendar.ts new file mode 100644 index 0000000..ed49571 --- /dev/null +++ b/src/migrations/baseSchemaTables/calendar.ts @@ -0,0 +1,59 @@ +import { Table, TableForeignKey } from "typeorm"; +import { getTypeByORM } from "../ormHelper"; + +export const calendar_type_table = new Table({ + name: "calendar_type", + columns: [ + { name: "id", type: getTypeByORM("int"), isPrimary: true, isGenerated: true, generationStrategy: "increment" }, + { name: "type", type: getTypeByORM("varchar"), length: "255", isUnique: true, isNullable: false }, + { name: "nscdr", type: getTypeByORM("boolean"), isNullable: false }, + { name: "color", type: getTypeByORM("varchar"), length: "255", isNullable: false }, + { name: "passphrase", type: getTypeByORM("varchar"), length: "255", isNullable: true }, + ], +}); + +export const calendar_table = new Table({ + name: "calendar", + columns: [ + { + name: "id", + type: getTypeByORM("varchar"), + length: "36", + isPrimary: true, + isGenerated: true, + generationStrategy: "uuid", + }, + { name: "starttime", type: getTypeByORM("datetime"), isNullable: false }, + { name: "endtime", type: getTypeByORM("datetime"), isNullable: false }, + { name: "title", type: getTypeByORM("varchar"), length: "255", isNullable: false }, + { name: "content", type: getTypeByORM("text"), isNullable: true }, + { name: "allDay", type: getTypeByORM("boolean"), isNullable: false, default: false }, + { name: "location", type: getTypeByORM("text"), isNullable: true }, + { name: "sequence", type: getTypeByORM("int"), default: 1 }, + { + name: "createdAt", + type: getTypeByORM("datetime"), + precision: 6, + isNullable: false, + default: "CURRENT_TIMESTAMP(6)", + }, + { + name: "updatedAt", + type: getTypeByORM("datetime"), + precision: 6, + isNullable: false, + default: "CURRENT_TIMESTAMP(6)", + onUpdate: "CURRENT_TIMESTAMP(6)", + }, + { name: "typeId", type: getTypeByORM("int"), isNullable: false }, + ], + foreignKeys: [ + new TableForeignKey({ + columnNames: ["typeId"], + referencedColumnNames: ["id"], + referencedTableName: "calendar_type", + onDelete: "RESTRICT", + onUpdate: "RESTRICT", + }), + ], +}); diff --git a/src/migrations/baseSchemaTables/member.ts b/src/migrations/baseSchemaTables/member.ts new file mode 100644 index 0000000..ead90b5 --- /dev/null +++ b/src/migrations/baseSchemaTables/member.ts @@ -0,0 +1,322 @@ +import { Table, TableForeignKey, View } from "typeorm"; +import { getTypeByORM } from "../ormHelper"; + +export const salutation_table = new Table({ + name: "salutation", + columns: [ + { name: "id", type: getTypeByORM("int"), isPrimary: true, isGenerated: true, generationStrategy: "increment" }, + { name: "salutation", type: getTypeByORM("varchar"), length: "255", isUnique: true, isNullable: false }, + ], +}); + +export const award_table = new Table({ + name: "award", + columns: [ + { name: "id", type: getTypeByORM("int"), isPrimary: true, isGenerated: true, generationStrategy: "increment" }, + { name: "award", type: getTypeByORM("varchar"), length: "255", isUnique: true, isNullable: false }, + ], +}); + +export const communication_type_table = new Table({ + name: "communication_type", + columns: [ + { name: "id", type: getTypeByORM("int"), isPrimary: true, isGenerated: true, generationStrategy: "increment" }, + { name: "type", type: getTypeByORM("varchar"), length: "255", isUnique: true, isNullable: false }, + { name: "useColumns", type: getTypeByORM("varchar"), length: "255", isNullable: false, default: "''" }, + ], +}); + +export const executive_position_table = new Table({ + name: "executive_position", + columns: [ + { name: "id", type: getTypeByORM("int"), isPrimary: true, isGenerated: true, generationStrategy: "increment" }, + { name: "position", type: getTypeByORM("varchar"), length: "255", isUnique: true, isNullable: false }, + ], +}); + +export const membership_status_table = new Table({ + name: "membership_status", + columns: [ + { name: "id", type: getTypeByORM("int"), isPrimary: true, isGenerated: true, generationStrategy: "increment" }, + { name: "status", type: getTypeByORM("varchar"), length: "255", isUnique: true, isNullable: false }, + ], +}); + +export const qualification_table = new Table({ + name: "qualification", + columns: [ + { name: "id", type: getTypeByORM("int"), isPrimary: true, isGenerated: true, generationStrategy: "increment" }, + { name: "qualification", type: getTypeByORM("varchar"), length: "255", isUnique: true, isNullable: false }, + { name: "description", type: getTypeByORM("varchar"), length: "255", isNullable: true }, + ], +}); + +/** member and relations */ +export const member_table = new Table({ + name: "member", + columns: [ + { + name: "id", + type: getTypeByORM("varchar"), + length: "36", + isPrimary: true, + isGenerated: true, + generationStrategy: "uuid", + }, + { name: "salutationId", type: getTypeByORM("int"), isNullable: false }, + { + name: "internalId", + type: getTypeByORM("varchar"), + length: "255", + default: null, + isNullable: true, + isUnique: true, + }, + { name: "firstname", type: getTypeByORM("varchar"), length: "255", isNullable: false }, + { name: "lastname", type: getTypeByORM("varchar"), length: "255", isNullable: false }, + { name: "nameaffix", type: getTypeByORM("varchar"), length: "255", isNullable: false }, + { name: "birthdate", type: getTypeByORM("date"), isNullable: false }, + ], + foreignKeys: [ + new TableForeignKey({ + columnNames: ["salutationId"], + referencedColumnNames: ["id"], + referencedTableName: "salutation", + onDelete: "RESTRICT", + onUpdate: "RESTRICT", + }), + ], +}); + +export const membership_table = new Table({ + name: "membership", + columns: [ + { name: "id", type: getTypeByORM("int"), isPrimary: true, isGenerated: true, generationStrategy: "increment" }, + { name: "start", type: getTypeByORM("date"), isNullable: false }, + { name: "end", type: getTypeByORM("date"), isNullable: true }, + { name: "terminationReason", type: getTypeByORM("varchar"), length: "255", isNullable: true }, + { name: "memberId", type: getTypeByORM("varchar"), isNullable: false }, + { name: "statusId", type: getTypeByORM("int"), isNullable: false }, + ], + foreignKeys: [ + new TableForeignKey({ + columnNames: ["memberId"], + referencedTableName: "member", + referencedColumnNames: ["id"], + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }), + new TableForeignKey({ + columnNames: ["statusId"], + referencedTableName: "membership_status", + referencedColumnNames: ["id"], + onDelete: "RESTRICT", + onUpdate: "RESTRICT", + }), + ], +}); + +export const member_qualifications_table = new Table({ + name: "member_qualifications", + columns: [ + { name: "id", type: getTypeByORM("int"), isPrimary: true, isGenerated: true, generationStrategy: "increment" }, + { name: "note", type: getTypeByORM("varchar"), length: "255", isNullable: true }, + { name: "start", type: getTypeByORM("date"), isNullable: false }, + { name: "end", type: getTypeByORM("date"), isNullable: true }, + { name: "terminationReason", type: getTypeByORM("varchar"), length: "255", isNullable: true }, + { name: "memberId", type: getTypeByORM("varchar"), isNullable: false }, + { name: "qualificationId", type: getTypeByORM("int"), isNullable: false }, + ], + foreignKeys: [ + new TableForeignKey({ + columnNames: ["memberId"], + referencedTableName: "member", + referencedColumnNames: ["id"], + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }), + new TableForeignKey({ + columnNames: ["qualificationId"], + referencedTableName: "qualification", + referencedColumnNames: ["id"], + onDelete: "RESTRICT", + onUpdate: "RESTRICT", + }), + ], +}); + +export const member_executive_positions_table = new Table({ + name: "member_executive_positions", + columns: [ + { name: "id", type: getTypeByORM("int"), isPrimary: true, isGenerated: true, generationStrategy: "increment" }, + { name: "note", type: getTypeByORM("varchar"), length: "255", isNullable: true }, + { name: "start", type: getTypeByORM("date"), isNullable: false }, + { name: "end", type: getTypeByORM("date"), isNullable: true }, + { name: "memberId", type: getTypeByORM("varchar"), isNullable: false }, + { name: "executivePositionId", type: getTypeByORM("int"), isNullable: false }, + ], + foreignKeys: [ + new TableForeignKey({ + columnNames: ["memberId"], + referencedTableName: "member", + referencedColumnNames: ["id"], + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }), + new TableForeignKey({ + columnNames: ["executivePositionId"], + referencedTableName: "executive_position", + referencedColumnNames: ["id"], + onDelete: "RESTRICT", + onUpdate: "RESTRICT", + }), + ], +}); + +export const member_awards_table = new Table({ + name: "member_awards", + columns: [ + { name: "id", type: getTypeByORM("int"), isPrimary: true, isGenerated: true, generationStrategy: "increment" }, + { name: "given", type: getTypeByORM("boolean"), default: false, isNullable: false }, + { name: "note", type: getTypeByORM("varchar"), length: "255", isNullable: true }, + { name: "date", type: getTypeByORM("date"), isNullable: false }, + { name: "memberId", type: getTypeByORM("varchar"), isNullable: false }, + { name: "awardId", type: getTypeByORM("int"), isNullable: false }, + ], + foreignKeys: [ + new TableForeignKey({ + columnNames: ["memberId"], + referencedTableName: "member", + referencedColumnNames: ["id"], + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }), + new TableForeignKey({ + columnNames: ["awardId"], + referencedTableName: "award", + referencedColumnNames: ["id"], + onDelete: "RESTRICT", + onUpdate: "RESTRICT", + }), + ], +}); + +export const member_communication_table = new Table({ + name: "communication", + columns: [ + { name: "id", type: getTypeByORM("int"), isPrimary: true, isGenerated: true, generationStrategy: "increment" }, + { name: "preferred", type: getTypeByORM("boolean"), isNullable: false, default: false }, + { name: "isSendNewsletter", type: getTypeByORM("boolean"), isNullable: false, default: false }, + { name: "isSMSAlarming", type: getTypeByORM("boolean"), isNullable: false, default: false }, + { name: "mobile", type: getTypeByORM("varchar"), length: "255", isNullable: true }, + { name: "email", type: getTypeByORM("varchar"), length: "255", isNullable: true }, + { name: "postalCode", type: getTypeByORM("varchar"), length: "255", default: null, isNullable: true }, + { name: "city", type: getTypeByORM("varchar"), length: "255", isNullable: true }, + { name: "street", type: getTypeByORM("varchar"), length: "255", isNullable: true }, + { name: "streetNumber", type: getTypeByORM("int"), isNullable: true }, + { name: "streetNumberAddition", type: getTypeByORM("varchar"), length: "255", isNullable: true }, + { name: "memberId", type: getTypeByORM("varchar"), isNullable: false }, + { name: "typeId", type: getTypeByORM("int"), isNullable: false }, + ], + foreignKeys: [ + new TableForeignKey({ + columnNames: ["memberId"], + referencedTableName: "member", + referencedColumnNames: ["id"], + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }), + new TableForeignKey({ + columnNames: ["typeId"], + referencedColumnNames: ["id"], + referencedTableName: "communication_type", + onDelete: "RESTRICT", + onUpdate: "RESTRICT", + }), + ], +}); + +/** views */ +export const member_view = new View({ + name: "member_view", + expression: ` + SELECT + \`member\`.\`id\` AS \`id\`, + \`member\`.\`firstname\` AS \`firstname\`, + \`member\`.\`lastname\` AS \`lastname\`, + \`member\`.\`nameaffix\` AS \`nameaffix\`, + \`member\`.\`birthdate\` AS \`birthdate\`, + \`salutation\`.\`salutation\` AS \`salutation\`, + TIMESTAMPDIFF(YEAR, \`member\`.\`birthdate\`, CURDATE()) AS \`todayAge\`, YEAR(CURDATE()) - YEAR(\`member\`.\`birthdate\`) AS \`ageThisYear\`, + CONCAT('_', FROM_DAYS(TIMESTAMPDIFF(DAY, \`member\`.\`birthdate\`, CURDATE()))) AS \`exactAge\` + FROM \`member\` \`member\` + LEFT JOIN \`salutation\` \`salutation\` ON \`salutation\`.\`id\`=\`member\`.\`salutationId\` + `, +}); + +export const member_executive_positions_view = new View({ + name: "member_executive_positions_view", + expression: ` + SELECT + \`executivePosition\`.\`id\` AS \`positionId\`, + \`executivePosition\`.\`position\` AS \`position\`, + \`member\`.\`id\` AS \`memberId\`, + \`member\`.\`firstname\` AS \`memberFirstname\`, + \`member\`.\`lastname\` AS \`memberLastname\`, + \`member\`.\`nameaffix\` AS \`memberNameaffix\`, + \`member\`.\`birthdate\` AS \`memberBirthdate\`, + \`salutation\`.\`salutation\` AS \`memberSalutation\`, + SUM(TIMESTAMPDIFF(DAY, \`memberExecutivePositions\`.\`start\`, + COALESCE(\`memberExecutivePositions\`.\`end\`, CURRENT_DATE))) AS \`durationInDays\` + FROM \`member_executive_positions\` \`memberExecutivePositions\` + LEFT JOIN \`executive_position\` \`executivePosition\` ON \`executivePosition\`.\`id\`=\`memberExecutivePositions\`.\`executivePositionId\` + LEFT JOIN \`member\` \`member\` ON \`member\`.\`id\`=\`memberExecutivePositions\`.\`memberId\` + LEFT JOIN \`salutation\` \`salutation\` ON \`salutation\`.\`id\`=\`member\`.\`salutationId\` + GROUP BY \`executivePosition\`.\`id\`, \`member\`.\`id\` + `, +}); + +export const member_qualifications_view = new View({ + name: "member_qualifications_view", + expression: ` + SELECT + \`qualification\`.\`id\` AS \`qualificationId\`, + \`qualification\`.\`qualification\` AS \`qualification\`, + \`member\`.\`id\` AS \`memberId\`, + \`member\`.\`firstname\` AS \`memberFirstname\`, + \`member\`.\`lastname\` AS \`memberLastname\`, + \`member\`.\`nameaffix\` AS \`memberNameaffix\`, + \`member\`.\`birthdate\` AS \`memberBirthdate\`, + \`salutation\`.\`salutation\` AS \`memberSalutation\`, + SUM(TIMESTAMPDIFF(DAY, \`memberQualifications\`.\`start\`, + COALESCE(\`memberQualifications\`.\`end\`, CURRENT_DATE))) AS \`durationInDays\` + FROM \`member_qualifications\` \`memberQualifications\` + LEFT JOIN \`qualification\` \`qualification\` ON \`qualification\`.\`id\`=\`memberQualifications\`.\`qualificationId\` + LEFT JOIN \`member\` \`member\` ON \`member\`.\`id\`=\`memberQualifications\`.\`memberId\` + LEFT JOIN \`salutation\` \`salutation\` ON \`salutation\`.\`id\`=\`member\`.\`salutationId\` + GROUP BY \`qualification\`.\`id\`, \`member\`.\`id\` + `, +}); + +export const membership_view = new View({ + name: "membership_view", + expression: ` + SELECT + \`status\`.\`id\` AS \`statusId\`, + \`status\`.\`status\` AS \`status\`, + \`member\`.\`id\` AS \`memberId\`, + \`member\`.\`firstname\` AS \`memberFirstname\`, + \`member\`.\`lastname\` AS \`memberLastname\`, + \`member\`.\`nameaffix\` AS \`memberNameaffix\`, + \`member\`.\`birthdate\` AS \`memberBirthdate\`, + \`salutation\`.\`salutation\` AS \`memberSalutation\`, + SUM(TIMESTAMPDIFF(DAY, \`membership\`.\`start\`, + COALESCE(\`membership\`.\`end\`, CURRENT_DATE))) AS \`durationInDays\`, + CONCAT('_', FROM_DAYS(SUM(TIMESTAMPDIFF(DAY, \`membership\`.\`start\`, COALESCE(\`membership\`.\`end\`, CURRENT_DATE))))) AS \`durationInYears\` + FROM \`membership\` \`membership\` + LEFT JOIN \`membership_status\` \`status\` ON \`status\`.\`id\`=\`membership\`.\`statusId\` + LEFT JOIN \`member\` \`member\` ON \`member\`.\`id\`=\`membership\`.\`memberId\` + LEFT JOIN \`salutation\` \`salutation\` ON \`salutation\`.\`id\`=\`member\`.\`salutationId\` + GROUP BY \`status\`.\`id\`, \`member\`.\`id\` + `, +}); diff --git a/src/migrations/baseSchemaTables/newsletter.ts b/src/migrations/baseSchemaTables/newsletter.ts new file mode 100644 index 0000000..5e39fcc --- /dev/null +++ b/src/migrations/baseSchemaTables/newsletter.ts @@ -0,0 +1,92 @@ +import { Table, TableForeignKey } from "typeorm"; +import { getTypeByORM } from "../ormHelper"; + +export const newsletter_table = new Table({ + name: "newsletter", + columns: [ + { name: "id", type: getTypeByORM("int"), isPrimary: true, isGenerated: true, generationStrategy: "increment" }, + { name: "title", type: getTypeByORM("varchar"), length: "255" }, + { name: "description", type: getTypeByORM("varchar"), length: "255", default: "''" }, + { name: "newsletterTitle", type: getTypeByORM("varchar"), length: "255", default: "''" }, + { name: "newsletterText", type: getTypeByORM("text"), default: "''" }, + { name: "newsletterSignatur", type: getTypeByORM("varchar"), length: "255", default: "''" }, + { name: "isSent", type: getTypeByORM("boolean"), default: false }, + { name: "recipientsByQueryId", type: getTypeByORM("int"), isNullable: true }, + ], + foreignKeys: [ + new TableForeignKey({ + columnNames: ["recipientsByQueryId"], + referencedColumnNames: ["id"], + referencedTableName: "query", + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }), + ], +}); + +export const newsletter_dates_table = new Table({ + name: "newsletter_dates", + columns: [ + { name: "newsletterId", type: getTypeByORM("int"), isPrimary: true }, + { name: "calendarId", type: getTypeByORM("varchar"), length: "255", isPrimary: true }, + { name: "diffTitle", type: getTypeByORM("varchar"), length: "255", isNullable: true }, + { name: "diffDescription", type: getTypeByORM("text"), isNullable: true }, + ], + foreignKeys: [ + new TableForeignKey({ + columnNames: ["newsletterId"], + referencedColumnNames: ["id"], + referencedTableName: "newsletter", + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }), + new TableForeignKey({ + columnNames: ["calendarId"], + referencedColumnNames: ["id"], + referencedTableName: "calendar", + onDelete: "RESTRICT", + onUpdate: "RESTRICT", + }), + ], +}); + +export const newsletter_recipients_table = new Table({ + name: "newsletter_recipients", + columns: [ + { name: "newsletterId", type: getTypeByORM("int"), isPrimary: true }, + { name: "memberId", type: getTypeByORM("varchar"), isPrimary: true }, + ], + foreignKeys: [ + new TableForeignKey({ + columnNames: ["newsletterId"], + referencedColumnNames: ["id"], + referencedTableName: "newsletter", + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }), + new TableForeignKey({ + columnNames: ["memberId"], + referencedColumnNames: ["id"], + referencedTableName: "member", + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }), + ], +}); + +export const newsletter_config_table = new Table({ + name: "newsletter_config", + columns: [ + { name: "comTypeId", type: getTypeByORM("int"), isPrimary: true, isNullable: false }, + { name: "config", type: getTypeByORM("varchar"), length: "255", isNullable: false }, + ], + foreignKeys: [ + new TableForeignKey({ + columnNames: ["comTypeId"], + referencedColumnNames: ["id"], + referencedTableName: "communication_type", + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }), + ], +}); diff --git a/src/migrations/baseSchemaTables/protocol.ts b/src/migrations/baseSchemaTables/protocol.ts new file mode 100644 index 0000000..2dee098 --- /dev/null +++ b/src/migrations/baseSchemaTables/protocol.ts @@ -0,0 +1,121 @@ +import { Table, TableForeignKey } from "typeorm"; +import { getTypeByORM } from "../ormHelper"; + +export const protocol_table = new Table({ + name: "protocol", + columns: [ + { name: "id", type: getTypeByORM("int"), isPrimary: true, isGenerated: true, generationStrategy: "increment" }, + { name: "title", type: getTypeByORM("varchar"), length: "255", isNullable: false }, + { name: "date", type: getTypeByORM("date"), isNullable: false }, + { name: "starttime", type: getTypeByORM("time"), isNullable: true }, + { name: "endtime", type: getTypeByORM("time"), isNullable: true }, + { name: "summary", type: getTypeByORM("text"), isNullable: true }, + ], +}); + +export const protocol_agenda_table = new Table({ + name: "protocol_agenda", + columns: [ + { name: "id", type: getTypeByORM("int"), isPrimary: true, isGenerated: true, generationStrategy: "increment" }, + { name: "topic", type: getTypeByORM("varchar"), length: "255", isNullable: false }, + { name: "context", type: getTypeByORM("text"), default: "''", isNullable: false }, + { name: "protocolId", type: getTypeByORM("int"), isNullable: false }, + ], + foreignKeys: [ + new TableForeignKey({ + columnNames: ["protocolId"], + referencedColumnNames: ["id"], + referencedTableName: "protocol", + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }), + ], +}); + +export const protocol_decision_table = new Table({ + name: "protocol_decision", + columns: [ + { name: "id", type: getTypeByORM("int"), isPrimary: true, isGenerated: true, generationStrategy: "increment" }, + { name: "topic", type: getTypeByORM("varchar"), length: "255", isNullable: false }, + { name: "context", type: getTypeByORM("text"), default: "''", isNullable: false }, + { name: "protocolId", type: getTypeByORM("int"), isNullable: false }, + ], + foreignKeys: [ + new TableForeignKey({ + columnNames: ["protocolId"], + referencedColumnNames: ["id"], + referencedTableName: "protocol", + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }), + ], +}); + +export const protocol_presence_table = new Table({ + name: "protocol_presence", + columns: [ + { name: "memberId", type: getTypeByORM("varchar"), isPrimary: true }, + { name: "protocolId", type: getTypeByORM("int"), isPrimary: true }, + { name: "absent", type: getTypeByORM("boolean"), default: false, isNullable: false }, + { name: "excused", type: getTypeByORM("boolean"), default: false, isNullable: false }, + ], + foreignKeys: [ + new TableForeignKey({ + columnNames: ["protocolId"], + referencedColumnNames: ["id"], + referencedTableName: "protocol", + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }), + new TableForeignKey({ + columnNames: ["memberId"], + referencedColumnNames: ["id"], + referencedTableName: "member", + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }), + ], +}); + +export const protocol_voting_table = new Table({ + name: "protocol_voting", + columns: [ + { name: "id", type: getTypeByORM("int"), isPrimary: true, isGenerated: true, generationStrategy: "increment" }, + { name: "topic", type: getTypeByORM("varchar"), length: "255", isNullable: false }, + { name: "context", type: getTypeByORM("text"), default: "''", isNullable: false }, + { name: "favour", type: getTypeByORM("int"), default: 0, isNullable: false }, + { name: "abstain", type: getTypeByORM("int"), default: 0, isNullable: false }, + { name: "against", type: getTypeByORM("int"), default: 0, isNullable: false }, + { name: "protocolId", type: getTypeByORM("int"), isNullable: false }, + ], + foreignKeys: [ + new TableForeignKey({ + columnNames: ["protocolId"], + referencedColumnNames: ["id"], + referencedTableName: "protocol", + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }), + ], +}); + +export const protocol_printout_table = new Table({ + name: "protocol_printout", + columns: [ + { name: "id", type: getTypeByORM("int"), isPrimary: true, isGenerated: true, generationStrategy: "increment" }, + { name: "title", type: getTypeByORM("varchar"), length: "255", isNullable: false }, + { name: "iteration", type: getTypeByORM("int"), default: 1, isNullable: false }, + { name: "filename", type: getTypeByORM("varchar"), length: "255", isNullable: false }, + { name: "createdAt", type: getTypeByORM("datetime"), isNullable: false, default: "CURRENT_TIMESTAMP(6)" }, + { name: "protocolId", type: getTypeByORM("int"), isNullable: false }, + ], + foreignKeys: [ + new TableForeignKey({ + columnNames: ["protocolId"], + referencedColumnNames: ["id"], + referencedTableName: "protocol", + onDelete: "CASCADE", + onUpdate: "RESTRICT", + }), + ], +}); diff --git a/src/migrations/baseSchemaTables/query_template.ts b/src/migrations/baseSchemaTables/query_template.ts new file mode 100644 index 0000000..399b34b --- /dev/null +++ b/src/migrations/baseSchemaTables/query_template.ts @@ -0,0 +1,57 @@ +import { Table, TableForeignKey } from "typeorm"; +import { getTypeByORM } from "../ormHelper"; + +export const query_table = new Table({ + name: "query", + columns: [ + { name: "id", type: getTypeByORM("int"), isPrimary: true, isGenerated: true, generationStrategy: "increment" }, + { name: "title", type: getTypeByORM("varchar"), length: "255", isNullable: false, isUnique: true }, + { name: "query", type: getTypeByORM("text"), isNullable: false, default: "''" }, + ], +}); + +export const template_table = new Table({ + name: "template", + columns: [ + { name: "id", type: getTypeByORM("int"), isPrimary: true, isGenerated: true, generationStrategy: "increment" }, + { name: "template", type: getTypeByORM("varchar"), length: "255", isNullable: false, isUnique: true }, + { name: "description", type: getTypeByORM("varchar"), length: "255", isNullable: true }, + { name: "design", type: getTypeByORM("text"), isNullable: false, default: "'{}'" }, + { name: "html", type: getTypeByORM("text"), isNullable: false, default: "''" }, + ], +}); + +export const template_usage_table = new Table({ + name: "template_usage", + columns: [ + { name: "scope", type: getTypeByORM("varchar"), length: "255", isPrimary: true }, + { name: "headerId", type: getTypeByORM("int"), isNullable: true }, + { name: "bodyId", type: getTypeByORM("int"), isNullable: true }, + { name: "footerId", type: getTypeByORM("int"), isNullable: true }, + { name: "headerHeight", type: getTypeByORM("int"), default: null, isNullable: true }, + { name: "footerHeight", type: getTypeByORM("int"), default: null, isNullable: true }, + ], + foreignKeys: [ + new TableForeignKey({ + columnNames: ["headerId"], + referencedColumnNames: ["id"], + referencedTableName: "template", + onDelete: "RESTRICT", + onUpdate: "RESTRICT", + }), + new TableForeignKey({ + columnNames: ["bodyId"], + referencedColumnNames: ["id"], + referencedTableName: "template", + onDelete: "RESTRICT", + onUpdate: "RESTRICT", + }), + new TableForeignKey({ + columnNames: ["footerId"], + referencedColumnNames: ["id"], + referencedTableName: "template", + onDelete: "RESTRICT", + onUpdate: "RESTRICT", + }), + ], +}); diff --git a/src/migrations/ormHelper.ts b/src/migrations/ormHelper.ts new file mode 100644 index 0000000..d4c7f43 --- /dev/null +++ b/src/migrations/ormHelper.ts @@ -0,0 +1,40 @@ +export type ORMType = "int" | "bigint" | "boolean" | "date" | "datetime" | "time" | "text" | "varchar"; + +export function getTypeByORM(type: ORMType): string { + const dbType = process.env.DB_TYPE; + + const typeMap: Record> = { + mysql: { + int: "int", + bigint: "bigint", + boolean: "tinyint", + date: "date", + datetime: "datetime", + time: "time", + text: "text", + varchar: "varchar", + }, + postgres: { + int: "integer", + bigint: "bigint", + boolean: "boolean", + date: "date", + datetime: "timestamp", + time: "time", + text: "text", + varchar: "varchar", + }, + sqlite: { + int: "integer", + bigint: "integer", + boolean: "integer", + date: "text", + datetime: "text", + time: "text", + text: "text", + varchar: "text", + }, + }; + + return typeMap[dbType]?.[type] || type; +}