From 46ad96c470739d2246fc3f75b4b5b52b210f27a5 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Sat, 25 Jan 2025 16:37:52 +0100 Subject: [PATCH 01/22] move sendNewsletter flag to communication table --- .../club/member/communicationCommand.ts | 2 + .../member/communicationCommandHandler.ts | 98 ++++++++++++------- src/command/club/member/memberCommand.ts | 5 - .../club/member/memberCommandHandler.ts | 52 +--------- src/controller/admin/club/memberController.ts | 27 +---- src/data-source.ts | 2 + src/entity/club/member/communication.ts | 3 + src/entity/club/member/member.ts | 9 +- .../admin/club/member/communication.ts | 2 +- .../1737816852011-moveSendNewsletterFlag.ts | 71 ++++++++++++++ .../club/member/communicationService.ts | 2 - src/service/club/member/memberService.ts | 28 ++++-- 12 files changed, 171 insertions(+), 130 deletions(-) create mode 100644 src/migrations/1737816852011-moveSendNewsletterFlag.ts diff --git a/src/command/club/member/communicationCommand.ts b/src/command/club/member/communicationCommand.ts index f0fdd5a..cc80442 100644 --- a/src/command/club/member/communicationCommand.ts +++ b/src/command/club/member/communicationCommand.ts @@ -1,6 +1,7 @@ export interface CreateCommunicationCommand { preferred: boolean; isSMSAlarming: boolean; + isSendNewsletter: boolean; mobile: string; email: string; postalCode: string; @@ -16,6 +17,7 @@ export interface UpdateCommunicationCommand { id: number; preferred: boolean; isSMSAlarming: boolean; + isSendNewsletter: boolean; mobile: string; email: string; postalCode: string; diff --git a/src/command/club/member/communicationCommandHandler.ts b/src/command/club/member/communicationCommandHandler.ts index ad6341c..24402fc 100644 --- a/src/command/club/member/communicationCommandHandler.ts +++ b/src/command/club/member/communicationCommandHandler.ts @@ -14,26 +14,44 @@ export default abstract class CommunicationCommandHandler { * @returns {Promise} */ static async create(createCommunication: CreateCommunicationCommand): Promise { + let insertId = -1; return await dataSource - .createQueryBuilder() - .insert() - .into(communication) - .values({ - preferred: createCommunication.preferred, - isSMSAlarming: createCommunication.isSMSAlarming, - mobile: createCommunication.mobile, - email: createCommunication.email, - postalCode: createCommunication.postalCode, - city: createCommunication.city, - street: createCommunication.street, - streetNumber: createCommunication.streetNumber, - streetNumberAddition: createCommunication.streetNumberAddition, - memberId: createCommunication.memberId, - typeId: createCommunication.typeId, + .transaction(async (manager) => { + await manager + .createQueryBuilder() + .insert() + .into(communication) + .values({ + preferred: createCommunication.preferred, + isSMSAlarming: createCommunication.isSMSAlarming, + isSendNewsletter: createCommunication.isSendNewsletter, + mobile: createCommunication.mobile, + email: createCommunication.email, + postalCode: createCommunication.postalCode, + city: createCommunication.city, + street: createCommunication.street, + streetNumber: createCommunication.streetNumber, + streetNumberAddition: createCommunication.streetNumberAddition, + memberId: createCommunication.memberId, + typeId: createCommunication.typeId, + }) + .execute() + .then((result) => { + insertId = result.identifiers[0].id; + }); + + await manager + .createQueryBuilder() + .update(communication) + .set({ + isSendNewsletter: false, + }) + .where("memberId = :memberId", { memberId: createCommunication.memberId }) + .andWhere("id <> :id", { id: insertId }) + .execute(); }) - .execute() - .then((result) => { - return result.identifiers[0].id; + .then(() => { + return insertId; }) .catch((err) => { throw new InternalException("Failed creating communication", err); @@ -47,22 +65,36 @@ export default abstract class CommunicationCommandHandler { */ static async update(updateCommunication: UpdateCommunicationCommand): Promise { return await dataSource - .createQueryBuilder() - .update(communication) - .set({ - preferred: updateCommunication.preferred, - isSMSAlarming: updateCommunication.isSMSAlarming, - mobile: updateCommunication.mobile, - email: updateCommunication.email, - postalCode: updateCommunication.postalCode, - city: updateCommunication.city, - street: updateCommunication.street, - streetNumber: updateCommunication.streetNumber, - streetNumberAddition: updateCommunication.streetNumberAddition, + .transaction(async (manager) => { + await manager + .createQueryBuilder() + .update(communication) + .set({ + preferred: updateCommunication.preferred, + isSMSAlarming: updateCommunication.isSMSAlarming, + isSendNewsletter: updateCommunication.isSendNewsletter, + mobile: updateCommunication.mobile, + email: updateCommunication.email, + postalCode: updateCommunication.postalCode, + city: updateCommunication.city, + street: updateCommunication.street, + streetNumber: updateCommunication.streetNumber, + streetNumberAddition: updateCommunication.streetNumberAddition, + }) + .where("id = :id", { id: updateCommunication.id }) + .andWhere("memberId = :memberId", { memberId: updateCommunication.memberId }) + .execute(); + + await manager + .createQueryBuilder() + .update(communication) + .set({ + isSendNewsletter: false, + }) + .where("memberId = :memberId", { memberId: updateCommunication.memberId }) + .andWhere("id <> :id", { id: updateCommunication.id }) + .execute(); }) - .where("id = :id", { id: updateCommunication.id }) - .andWhere("memberId = :memberId", { memberId: updateCommunication.memberId }) - .execute() .then(() => {}) .catch((err) => { throw new InternalException("Failed updating communication", err); diff --git a/src/command/club/member/memberCommand.ts b/src/command/club/member/memberCommand.ts index 126b87d..90ecee0 100644 --- a/src/command/club/member/memberCommand.ts +++ b/src/command/club/member/memberCommand.ts @@ -17,11 +17,6 @@ export interface UpdateMemberCommand { internalId?: string; } -export interface UpdateMemberNewsletterCommand { - id: number; - communicationId: number; -} - export interface DeleteMemberCommand { id: number; } diff --git a/src/command/club/member/memberCommandHandler.ts b/src/command/club/member/memberCommandHandler.ts index 569e835..54862f7 100644 --- a/src/command/club/member/memberCommandHandler.ts +++ b/src/command/club/member/memberCommandHandler.ts @@ -2,12 +2,7 @@ import { dataSource } from "../../../data-source"; import { communication } from "../../../entity/club/member/communication"; import { member } from "../../../entity/club/member/member"; import InternalException from "../../../exceptions/internalException"; -import { - CreateMemberCommand, - DeleteMemberCommand, - UpdateMemberCommand, - UpdateMemberNewsletterCommand, -} from "./memberCommand"; +import { CreateMemberCommand, DeleteMemberCommand, UpdateMemberCommand } from "./memberCommand"; export default abstract class MemberCommandHandler { /** @@ -68,51 +63,6 @@ export default abstract class MemberCommandHandler { }); } - /** - * @description update member newsletter - * @param {UpdateMemberCommand} updateMember - * @returns {Promise} - */ - static async updateNewsletter(updateMember: UpdateMemberNewsletterCommand): Promise { - return await dataSource - .createQueryBuilder() - .update(member) - .set({ - sendNewsletter: await dataSource - .getRepository(communication) - .createQueryBuilder("communication") - .where("id = :id", { id: updateMember.communicationId }) - .andWhere("memberId = :memberId", { memberId: updateMember.id }) - .getOneOrFail(), - }) - .where("id = :id", { id: updateMember.id }) - .execute() - .then(() => {}) - .catch((err) => { - throw new InternalException(`Failed updating member`, err); - }); - } - - /** - * @description update member newsletter to unset - * @param {number} memberId - * @returns {Promise} - */ - static async unsetNewsletter(memberId: number): Promise { - return await dataSource - .createQueryBuilder() - .update(member) - .set({ - sendNewsletter: null, - }) - .where("id = :id", { id: memberId }) - .execute() - .then(() => {}) - .catch((err) => { - throw new InternalException("Failed updating member", err); - }); - } - /** * @description delete member * @param {DeleteMemberCommand} deleteMember diff --git a/src/controller/admin/club/memberController.ts b/src/controller/admin/club/memberController.ts index ec99ef3..2e8698a 100644 --- a/src/controller/admin/club/memberController.ts +++ b/src/controller/admin/club/memberController.ts @@ -15,7 +15,6 @@ import { CreateMemberCommand, DeleteMemberCommand, UpdateMemberCommand, - UpdateMemberNewsletterCommand, } from "../../../command/club/member/memberCommand"; import MemberCommandHandler from "../../../command/club/member/memberCommandHandler"; import { @@ -409,6 +408,7 @@ export async function addCommunicationToMember(req: Request, res: Response): Pro const memberId = parseInt(req.params.memberId); const preferred = req.body.preferred; const isSMSAlarming = req.body.isSMSAlarming; + const isSendNewsletter = req.body.isNewsletterMain; const mobile = req.body.mobile; const email = req.body.email; const postalCode = req.body.postalCode; @@ -417,11 +417,11 @@ export async function addCommunicationToMember(req: Request, res: Response): Pro const streetNumber = req.body.streetNumber; const streetNumberAddition = req.body.streetNumberAddition; const typeId = req.body.typeId; - const isNewsletterMain = req.body.isNewsletterMain; let createCommunication: CreateCommunicationCommand = { preferred, isSMSAlarming, + isSendNewsletter, mobile, email, postalCode, @@ -434,14 +434,6 @@ export async function addCommunicationToMember(req: Request, res: Response): Pro }; let id = await CommunicationCommandHandler.create(createCommunication); - if (isNewsletterMain) { - let updateNewsletter: UpdateMemberNewsletterCommand = { - id: memberId, - communicationId: id, - }; - await MemberCommandHandler.updateNewsletter(updateNewsletter); - } - res.sendStatus(204); } @@ -595,6 +587,7 @@ export async function updateCommunicationOfMember(req: Request, res: Response): const recordId = parseInt(req.params.recordId); const preferred = req.body.preferred; const isSMSAlarming = req.body.isSMSAlarming; + const isSendNewsletter = req.body.isNewsletterMain; const mobile = req.body.mobile; const email = req.body.email; const postalCode = req.body.postalCode; @@ -602,12 +595,12 @@ export async function updateCommunicationOfMember(req: Request, res: Response): const street = req.body.street; const streetNumber = req.body.streetNumber; const streetNumberAddition = req.body.streetNumberAddition; - const isNewsletterMain = req.body.isNewsletterMain; let updateCommunication: UpdateCommunicationCommand = { id: recordId, preferred, isSMSAlarming, + isSendNewsletter, mobile, email, postalCode, @@ -619,18 +612,6 @@ export async function updateCommunicationOfMember(req: Request, res: Response): }; await CommunicationCommandHandler.update(updateCommunication); - let currentUserNewsletterMain = await MemberService.getNewsletterById(memberId); - - if (isNewsletterMain) { - let updateNewsletter: UpdateMemberNewsletterCommand = { - id: memberId, - communicationId: recordId, - }; - await MemberCommandHandler.updateNewsletter(updateNewsletter); - } else if (currentUserNewsletterMain.sendNewsletter?.id == recordId) { - await MemberCommandHandler.unsetNewsletter(memberId); - } - res.sendStatus(204); } diff --git a/src/data-source.ts b/src/data-source.ts index c016ca4..851997f 100644 --- a/src/data-source.ts +++ b/src/data-source.ts @@ -74,6 +74,7 @@ import { AddWebapiTokens1737453096674 } from "./migrations/1737453096674-addweba import { salutation } from "./entity/settings/salutation"; import { SalutationAsTable1737796878058 } from "./migrations/1737796878058-salutationAsTable"; import { UpdateViews1737800468938 } from "./migrations/1737800468938-updateViews"; +import { MoveSendNewsletterFlag1737816852011 } from "./migrations/1737816852011-moveSendNewsletterFlag"; const dataSource = new DataSource({ type: DB_TYPE as any, @@ -158,6 +159,7 @@ const dataSource = new DataSource({ AddWebapiTokens1737453096674, SalutationAsTable1737796878058, UpdateViews1737800468938, + MoveSendNewsletterFlag1737816852011, ], migrationsRun: true, migrationsTransactionMode: "each", diff --git a/src/entity/club/member/communication.ts b/src/entity/club/member/communication.ts index f50d1a9..c3c79fc 100644 --- a/src/entity/club/member/communication.ts +++ b/src/entity/club/member/communication.ts @@ -13,6 +13,9 @@ export class communication { @Column({ type: "boolean", default: false }) isSMSAlarming: boolean; + @Column({ type: "boolean", default: false }) + isSendNewsletter: boolean; + @Column({ type: "varchar", length: 255, nullable: true }) mobile: string; diff --git a/src/entity/club/member/member.ts b/src/entity/club/member/member.ts index e5b27c2..3557d58 100644 --- a/src/entity/club/member/member.ts +++ b/src/entity/club/member/member.ts @@ -32,14 +32,6 @@ export class member { @OneToMany(() => communication, (communications) => communications.member) communications: communication[]; - @OneToOne(() => communication, { - nullable: true, - onDelete: "SET NULL", - onUpdate: "RESTRICT", - }) - @JoinColumn() - sendNewsletter?: communication; - @ManyToOne(() => salutation, (salutation) => salutation.members) salutation: salutation; @@ -59,4 +51,5 @@ export class member { lastMembershipEntry?: membership; preferredCommunication?: Array; smsAlarming?: Array; + sendNewsletter?: communication; } diff --git a/src/factory/admin/club/member/communication.ts b/src/factory/admin/club/member/communication.ts index 702cfc5..127ab82 100644 --- a/src/factory/admin/club/member/communication.ts +++ b/src/factory/admin/club/member/communication.ts @@ -20,7 +20,7 @@ export default abstract class CommunicationFactory { streetNumber: record.streetNumber, streetNumberAddition: record.streetNumberAddition, type: CommunicationTypeFactory.mapToSingle(record.type), - isNewsletterMain: isMain ? isMain : record?.member?.sendNewsletter?.id == record.id, + isNewsletterMain: record?.isSendNewsletter, isSMSAlarming: record.isSMSAlarming, }; } diff --git a/src/migrations/1737816852011-moveSendNewsletterFlag.ts b/src/migrations/1737816852011-moveSendNewsletterFlag.ts new file mode 100644 index 0000000..d294337 --- /dev/null +++ b/src/migrations/1737816852011-moveSendNewsletterFlag.ts @@ -0,0 +1,71 @@ +import { MigrationInterface, QueryRunner, TableColumn, TableForeignKey, TableIndex } from "typeorm"; +import { communication } from "../entity/club/member/communication"; +import { member } from "../entity/club/member/member"; + +export class MoveSendNewsletterFlag1737816852011 implements MigrationInterface { + name = "MoveSendNewsletterFlag1737816852011"; + + public async up(queryRunner: QueryRunner): Promise { + const table = await queryRunner.getTable("member"); + const foreignKey = table.foreignKeys.find((fk) => fk.columnNames.indexOf("sendNewsletterId") !== -1); + await queryRunner.dropForeignKey("member", foreignKey); + + await queryRunner.addColumn( + "communication", + new TableColumn({ name: "isSendNewsletter", type: "tinyint", isNullable: false, default: 0 }) + ); + + // ! has to be sql. Else no data is returned. + const member_newsletter_send = await queryRunner.query("SELECT sendNewsletterId, id FROM `member` `member`"); + + for (let assigned of member_newsletter_send.map((mns: any) => ({ + id: mns.id, + sendNewsletterId: mns.sendNewsletterId, + })) as Array<{ id: number; sendNewsletterId: number }>) { + await queryRunner.manager + .getRepository(communication) + .createQueryBuilder("communication") + .update({ isSendNewsletter: true }) + .where({ memberId: assigned.id, id: assigned.sendNewsletterId }) + .execute(); + } + + await queryRunner.dropColumn("member", "sendNewsletterId"); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.addColumn( + "member", + new TableColumn({ name: "sendNewsletterId", type: "int", isNullable: true, isUnique: true, default: null }) + ); + + const member_newsletter_send = await queryRunner.manager + .getRepository(communication) + .createQueryBuilder("communication") + .where("communication.isSendNewsletter = :isSendNewsletter", { isSendNewsletter: true }) + .getMany(); + + for (let assigned of member_newsletter_send.map((mns: any) => ({ + id: mns.id, + memberId: mns.memberId, + })) as Array<{ id: number; memberId: number }>) { + await queryRunner.query("UPDATE `member` SET sendNewsletterId = ? WHERE id = ?", [ + assigned.id, + assigned.memberId, + ]); + } + + await queryRunner.createForeignKey( + "member", + new TableForeignKey({ + columnNames: ["sendNewsletterId"], + referencedColumnNames: ["id"], + referencedTableName: "communication", + onDelete: "SET NULL", + onUpdate: "RESTRICT", + }) + ); + + await queryRunner.dropColumn("communication", "isSendNewsletter"); + } +} diff --git a/src/service/club/member/communicationService.ts b/src/service/club/member/communicationService.ts index 5ca7bdd..b828bc7 100644 --- a/src/service/club/member/communicationService.ts +++ b/src/service/club/member/communicationService.ts @@ -14,7 +14,6 @@ export default abstract class CommunicationService { .createQueryBuilder("communication") .leftJoinAndSelect("communication.type", "communicationType") .leftJoinAndSelect("communication.member", "member") - .leftJoinAndSelect("member.sendNewsletter", "sendNewsletter") .where("communication.memberId = :memberId", { memberId: memberId }) .orderBy("communicationType.type", "ASC") .getMany() @@ -38,7 +37,6 @@ export default abstract class CommunicationService { .createQueryBuilder("communication") .leftJoinAndSelect("communication.type", "communicationType") .leftJoinAndSelect("communication.member", "member") - .leftJoinAndSelect("member.sendNewsletter", "sendNewsletter") .where("communication.memberId = :memberId", { memberId: memberId }) .andWhere("communication.id = :recordId", { recordId: recordId }) .getOneOrFail() diff --git a/src/service/club/member/memberService.ts b/src/service/club/member/memberService.ts index 35b8c1d..53a2aaa 100644 --- a/src/service/club/member/memberService.ts +++ b/src/service/club/member/memberService.ts @@ -39,8 +39,6 @@ export default abstract class MemberService { ) .leftJoinAndSelect("membership_first.status", "status_first") .leftJoinAndSelect("membership_last.status", "status_last") - .leftJoinAndSelect("member.sendNewsletter", "sendNewsletter") - .leftJoinAndSelect("sendNewsletter.type", "communicationtype") .leftJoinAndMapMany( "member.preferredCommunication", "member.communications", @@ -48,6 +46,13 @@ export default abstract class MemberService { "preferredCommunication.preferred = 1" ) .leftJoinAndSelect("preferredCommunication.type", "communicationtype_preferred") + .leftJoinAndMapOne( + "member.sendNewsletter", + "member.communications", + "sendNewsletter", + "sendNewsletter.isSendNewsletter = 1" + ) + .leftJoinAndSelect("sendNewsletter.type", "communicationtype") .leftJoinAndMapMany("member.smsAlarming", "member.communications", "smsAlarming", "smsAlarming.isSMSAlarming = 1") .leftJoinAndSelect("smsAlarming.type", "communicationtype_smsAlarming") .leftJoinAndSelect("member.salutation", "salutation"); @@ -112,18 +117,22 @@ export default abstract class MemberService { ) .leftJoinAndSelect("membership_first.status", "status_first") .leftJoinAndSelect("membership_last.status", "status_last") - .leftJoinAndSelect("member.sendNewsletter", "sendNewsletter") - .leftJoinAndSelect("sendNewsletter.type", "communicationtype") .leftJoinAndMapMany( "member.preferredCommunication", "member.communications", "preferredCommunication", "preferredCommunication.preferred = 1" ) - + .leftJoinAndSelect("preferredCommunication.type", "communicationtype_preferred") + .leftJoinAndMapOne( + "member.sendNewsletter", + "member.communications", + "sendNewsletter", + "sendNewsletter.isSendNewsletter = 1" + ) + .leftJoinAndSelect("sendNewsletter.type", "communicationtype") .leftJoinAndMapMany("member.smsAlarming", "member.communications", "smsAlarming", "smsAlarming.isSMSAlarming = 1") .leftJoinAndSelect("smsAlarming.type", "communicationtype_smsAlarming") - .leftJoinAndSelect("preferredCommunication.type", "communicationtype_preferred") .leftJoinAndSelect("member.salutation", "salutation") .where("member.id = :id", { id: id }) .getOneOrFail() @@ -185,7 +194,12 @@ export default abstract class MemberService { return await dataSource .getRepository(member) .createQueryBuilder("member") - .leftJoinAndSelect("member.sendNewsletter", "sendNewsletter") + .leftJoinAndMapOne( + "member.sendNewsletter", + "member.communications", + "sendNewsletter", + "sendNewsletter.isSendNewsletter = 1" + ) .where("member.id = :id", { id: id }) .getOneOrFail() .then((res) => { -- 2.45.3 From 684c24e4fd14c296b0476f25653d7c16a85194da Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Sat, 25 Jan 2025 16:54:01 +0100 Subject: [PATCH 02/22] extend ENVs --- .env.example | 8 ++++++-- README.md | 3 +++ src/env.defaults.ts | 7 +++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index c3fd1f3..4dc7854 100644 --- a/.env.example +++ b/.env.example @@ -16,7 +16,11 @@ MAIL_USERNAME = mail_username MAIL_PASSWORD = mail_password MAIL_HOST = mail_hoststring MAIL_PORT = mail_portnumber -MAIL_SECURE (true|false) // true for port 465, fals for other ports +MAIL_SECURE = (true|false) // true for port 465, fals for other ports CLUB_NAME = clubname #default FF Admin -CLUB_WEBSITE = https://my-club-website-url \ No newline at end of file +CLUB_WEBSITE = https://my-club-website-url + +BACKUP_INTERVAL = number of days (min 1) +BACKUP_COPIES = number of parallel copies +BACKUP_AUTO_RESTORE = (true|false) # default false \ No newline at end of file diff --git a/README.md b/README.md index 17ee5d8..a4f7665 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,9 @@ services: - MAIL_SECURE= # default ist auf false gesetzt - CLUB_NAME= # default ist auf FF Admin gesetzt - CLUB_WEBSITE= + - BACKUP_INTERVAL= # alle x Tage, sonst keine + - BACKUP_COPIES= # Anzahl parallel bestehender Backups + - BACKUP_AUTO_RESTORE= # default ist auf false gesetzt volumes: - :/app/files networks: diff --git a/src/env.defaults.ts b/src/env.defaults.ts index d4e3c3b..343a9b5 100644 --- a/src/env.defaults.ts +++ b/src/env.defaults.ts @@ -24,6 +24,10 @@ export const MAIL_SECURE = process.env.MAIL_SECURE ?? "false"; export const CLUB_NAME = process.env.CLUB_NAME ?? "FF Admin"; export const CLUB_WEBSITE = process.env.CLUB_WEBSITE ?? ""; +export const BACKUP_INTERVAL = Number(process.env.CLUB_WEBSITE ?? "0"); +export const BACKUP_COPIES = Number(process.env.CLUB_WEBSITE ?? "0"); +export const BACKUP_AUTO_RESTORE = process.env.CLUB_WEBSITE ?? "false"; + export function configCheck() { if (DB_TYPE != "mysql" && DB_TYPE != "sqlite") throw new Error("set valid value to DB_TYPE (mysql|sqlite)"); if (DB_HOST == "" || typeof DB_HOST != "string") throw new Error("set valid value to DB_HOST"); @@ -49,6 +53,9 @@ export function configCheck() { !/^(http(s):\/\/.)[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)$/.test(CLUB_WEBSITE) ) throw new Error("CLUB_WEBSITE is not valid url"); + + if (BACKUP_AUTO_RESTORE != "true" && BACKUP_AUTO_RESTORE != "false") + throw new Error("set 'true' or 'false' to BACKUP_AUTO_RESTORE"); } function checkMS(input: string, origin: string) { -- 2.45.3 From 4378c02d179c95b36d11df291bfec9b10d344d4b Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Tue, 28 Jan 2025 11:09:42 +0100 Subject: [PATCH 03/22] unique fields for tables --- src/data-source.ts | 2 + src/entity/club/member/member.ts | 6 +- src/entity/club/newsletter/newsletter.ts | 2 +- src/entity/club/protocol/protocol.ts | 2 +- src/entity/refresh.ts | 4 +- src/entity/settings/award.ts | 2 +- src/entity/settings/calendarType.ts | 2 +- src/entity/settings/communicationType.ts | 2 +- src/entity/settings/executivePosition.ts | 2 +- src/entity/settings/membershipStatus.ts | 2 +- src/entity/settings/qualification.ts | 2 +- src/entity/settings/template.ts | 2 +- src/entity/user/role.ts | 2 +- src/entity/user/user.ts | 8 +- src/entity/user/user_permission.ts | 4 +- src/migrations/1738057119384-UniqueFields.ts | 119 +++++++++++++++++++ 16 files changed, 144 insertions(+), 19 deletions(-) create mode 100644 src/migrations/1738057119384-UniqueFields.ts diff --git a/src/data-source.ts b/src/data-source.ts index 851997f..145da3f 100644 --- a/src/data-source.ts +++ b/src/data-source.ts @@ -75,6 +75,7 @@ import { salutation } from "./entity/settings/salutation"; import { SalutationAsTable1737796878058 } from "./migrations/1737796878058-salutationAsTable"; import { UpdateViews1737800468938 } from "./migrations/1737800468938-updateViews"; import { MoveSendNewsletterFlag1737816852011 } from "./migrations/1737816852011-moveSendNewsletterFlag"; +import { UniqueFields1738057119384 } from "./migrations/1738057119384-UniqueFields"; const dataSource = new DataSource({ type: DB_TYPE as any, @@ -160,6 +161,7 @@ const dataSource = new DataSource({ SalutationAsTable1737796878058, UpdateViews1737800468938, MoveSendNewsletterFlag1737816852011, + UniqueFields1738057119384, ], migrationsRun: true, migrationsTransactionMode: "each", diff --git a/src/entity/club/member/member.ts b/src/entity/club/member/member.ts index 3557d58..d20ba70 100644 --- a/src/entity/club/member/member.ts +++ b/src/entity/club/member/member.ts @@ -32,7 +32,11 @@ export class member { @OneToMany(() => communication, (communications) => communications.member) communications: communication[]; - @ManyToOne(() => salutation, (salutation) => salutation.members) + @ManyToOne(() => salutation, (salutation) => salutation.members, { + nullable: false, + onDelete: "RESTRICT", + onUpdate: "RESTRICT", + }) salutation: salutation; @OneToMany(() => membership, (membership) => membership.member) diff --git a/src/entity/club/newsletter/newsletter.ts b/src/entity/club/newsletter/newsletter.ts index 021ce23..1c26a68 100644 --- a/src/entity/club/newsletter/newsletter.ts +++ b/src/entity/club/newsletter/newsletter.ts @@ -9,7 +9,7 @@ export class newsletter { @PrimaryColumn({ generated: "increment", type: "int" }) id: number; - @Column({ type: "varchar", length: 255 }) + @Column({ type: "varchar", length: 255, unique: true }) title: string; @Column({ type: "varchar", length: 255, default: "" }) diff --git a/src/entity/club/protocol/protocol.ts b/src/entity/club/protocol/protocol.ts index b218164..215d9bc 100644 --- a/src/entity/club/protocol/protocol.ts +++ b/src/entity/club/protocol/protocol.ts @@ -5,7 +5,7 @@ export class protocol { @PrimaryColumn({ generated: "increment", type: "int" }) id: number; - @Column({ type: "varchar", length: 255 }) + @Column({ type: "varchar", length: 255, unique: true }) title: string; @Column({ type: "date" }) diff --git a/src/entity/refresh.ts b/src/entity/refresh.ts index 560d7c2..646b3bd 100644 --- a/src/entity/refresh.ts +++ b/src/entity/refresh.ts @@ -6,8 +6,8 @@ export class refresh { @PrimaryColumn({ type: "varchar", length: 255 }) token: string; - @PrimaryColumn({ type: "int" }) - userId: number; + @PrimaryColumn() + userId: string; @Column({ type: "datetime" }) expiry: Date; diff --git a/src/entity/settings/award.ts b/src/entity/settings/award.ts index 74d5260..1bcead0 100644 --- a/src/entity/settings/award.ts +++ b/src/entity/settings/award.ts @@ -6,7 +6,7 @@ export class award { @PrimaryColumn({ generated: "increment", type: "int" }) id: number; - @Column({ type: "varchar", length: 255 }) + @Column({ type: "varchar", length: 255, unique: true }) award: string; @OneToMany(() => memberAwards, (member) => member.award) diff --git a/src/entity/settings/calendarType.ts b/src/entity/settings/calendarType.ts index 1e99e26..acef5e5 100644 --- a/src/entity/settings/calendarType.ts +++ b/src/entity/settings/calendarType.ts @@ -6,7 +6,7 @@ export class calendarType { @PrimaryColumn({ generated: "increment", type: "int" }) id: number; - @Column({ type: "varchar", length: 255 }) + @Column({ type: "varchar", length: 255, unique: true }) type: string; @Column({ type: "boolean" }) // none specified cal dav request diff --git a/src/entity/settings/communicationType.ts b/src/entity/settings/communicationType.ts index 1c81be5..7fd82ee 100644 --- a/src/entity/settings/communicationType.ts +++ b/src/entity/settings/communicationType.ts @@ -7,7 +7,7 @@ export class communicationType { @PrimaryColumn({ generated: "increment", type: "int" }) id: number; - @Column({ type: "varchar", length: 255 }) + @Column({ type: "varchar", length: 255, unique: true }) type: string; @Column({ diff --git a/src/entity/settings/executivePosition.ts b/src/entity/settings/executivePosition.ts index 8d77bf7..19843d0 100644 --- a/src/entity/settings/executivePosition.ts +++ b/src/entity/settings/executivePosition.ts @@ -6,7 +6,7 @@ export class executivePosition { @PrimaryColumn({ generated: "increment", type: "int" }) id: number; - @Column({ type: "varchar", length: 255 }) + @Column({ type: "varchar", length: 255, unique: true }) position: string; @OneToMany(() => memberExecutivePositions, (memberExecutivePositions) => memberExecutivePositions.executivePosition) diff --git a/src/entity/settings/membershipStatus.ts b/src/entity/settings/membershipStatus.ts index 4362171..f8fc745 100644 --- a/src/entity/settings/membershipStatus.ts +++ b/src/entity/settings/membershipStatus.ts @@ -6,7 +6,7 @@ export class membershipStatus { @PrimaryColumn({ generated: "increment", type: "int" }) id: number; - @Column({ type: "varchar", length: 255 }) + @Column({ type: "varchar", length: 255, unique: true }) status: string; @OneToMany(() => membership, (membership) => membership.status) diff --git a/src/entity/settings/qualification.ts b/src/entity/settings/qualification.ts index 8e76ff8..9fa541a 100644 --- a/src/entity/settings/qualification.ts +++ b/src/entity/settings/qualification.ts @@ -6,7 +6,7 @@ export class qualification { @PrimaryColumn({ generated: "increment", type: "int" }) id: number; - @Column({ type: "varchar", length: 255 }) + @Column({ type: "varchar", length: 255, unique: true }) qualification: string; @Column({ type: "varchar", length: 255, nullable: true }) diff --git a/src/entity/settings/template.ts b/src/entity/settings/template.ts index 6a25724..a237973 100644 --- a/src/entity/settings/template.ts +++ b/src/entity/settings/template.ts @@ -5,7 +5,7 @@ export class template { @PrimaryColumn({ generated: "increment", type: "int" }) id: number; - @Column({ type: "varchar", length: 255 }) + @Column({ type: "varchar", length: 255, unique: true }) template: string; @Column({ type: "varchar", length: 255, nullable: true }) diff --git a/src/entity/user/role.ts b/src/entity/user/role.ts index 87f0049..b7692bf 100644 --- a/src/entity/user/role.ts +++ b/src/entity/user/role.ts @@ -7,7 +7,7 @@ export class role { @PrimaryColumn({ generated: "increment", type: "int" }) id: number; - @Column({ type: "varchar", length: 255 }) + @Column({ type: "varchar", length: 255, unique: true }) role: string; @ManyToMany(() => user, (user) => user.roles, { diff --git a/src/entity/user/user.ts b/src/entity/user/user.ts index 09435cd..bb42bb8 100644 --- a/src/entity/user/user.ts +++ b/src/entity/user/user.ts @@ -4,13 +4,13 @@ import { userPermission } from "./user_permission"; @Entity() export class user { - @PrimaryColumn({ generated: "increment", type: "int" }) - id: number; + @PrimaryColumn({ generated: "uuid", type: "varchar" }) + id: string; - @Column({ type: "varchar", length: 255 }) + @Column({ type: "varchar", unique: true, length: 255 }) mail: string; - @Column({ type: "varchar", length: 255 }) + @Column({ type: "varchar", unique: true, length: 255 }) username: string; @Column({ type: "varchar", length: 255 }) diff --git a/src/entity/user/user_permission.ts b/src/entity/user/user_permission.ts index 7d782ae..1c6ccfd 100644 --- a/src/entity/user/user_permission.ts +++ b/src/entity/user/user_permission.ts @@ -4,8 +4,8 @@ import { PermissionObject, PermissionString } from "../../type/permissionTypes"; @Entity() export class userPermission { - @PrimaryColumn({ type: "int" }) - userId: number; + @PrimaryColumn() + userId: string; @PrimaryColumn({ type: "varchar", length: 255 }) permission: PermissionString; diff --git a/src/migrations/1738057119384-UniqueFields.ts b/src/migrations/1738057119384-UniqueFields.ts new file mode 100644 index 0000000..f60def2 --- /dev/null +++ b/src/migrations/1738057119384-UniqueFields.ts @@ -0,0 +1,119 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class UniqueFields1738057119384 implements MigrationInterface { + name = "UniqueFields1738057119384"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP INDEX \`UQ_6c6bdcaeb808549ae66c2493efd\` ON \`salutation\``); + await queryRunner.query(`DROP INDEX \`UQ_9ffb36037fb8d7456689956ed80\` ON \`query\``); + await queryRunner.query(`DROP INDEX \`UQ_424ac388ca888a1dc67f5fe3a80\` ON \`webapi\``); + await queryRunner.query(`DROP INDEX \`UQ_55c99c82f45936d8d34c48f5515\` ON \`webapi\``); + + await queryRunner.query(`ALTER TABLE \`award\` ADD UNIQUE INDEX \`IDX_646bbdb45883cc39c55740d7b6\` (\`award\`)`); + await queryRunner.query(`ALTER TABLE \`protocol\` ADD UNIQUE INDEX \`IDX_e60159678b8dad84b2e7dfa20c\` (\`title\`)`); + await queryRunner.query( + `ALTER TABLE \`calendar_type\` ADD UNIQUE INDEX \`IDX_fbcbfda68b80800afa78e21c88\` (\`type\`)` + ); + await queryRunner.query(`ALTER TABLE \`query\` ADD UNIQUE INDEX \`IDX_9ffb36037fb8d7456689956ed8\` (\`title\`)`); + await queryRunner.query( + `ALTER TABLE \`template\` ADD UNIQUE INDEX \`IDX_9d0ad817708f4c1a7c78e4abf6\` (\`template\`)` + ); + await queryRunner.query( + `ALTER TABLE \`newsletter\` ADD UNIQUE INDEX \`IDX_80b2037e9f015eb2cf597bae6c\` (\`title\`)` + ); + await queryRunner.query( + `ALTER TABLE \`membership_status\` ADD UNIQUE INDEX \`IDX_9d71cc98acfae7a8276718f052\` (\`status\`)` + ); + await queryRunner.query( + `ALTER TABLE \`qualification\` ADD UNIQUE INDEX \`IDX_2d2540f8b970a5a43a905278da\` (\`qualification\`)` + ); + await queryRunner.query( + `ALTER TABLE \`executive_position\` ADD UNIQUE INDEX \`IDX_427169e456c217a317273c31d7\` (\`position\`)` + ); + await queryRunner.query( + `ALTER TABLE \`communication_type\` ADD UNIQUE INDEX \`IDX_63a3b23374fc0dd80e5ab32e09\` (\`type\`)` + ); + await queryRunner.query( + `ALTER TABLE \`salutation\` ADD UNIQUE INDEX \`IDX_6c6bdcaeb808549ae66c2493ef\` (\`salutation\`)` + ); + await queryRunner.query(`ALTER TABLE \`webapi\` ADD UNIQUE INDEX \`IDX_55c99c82f45936d8d34c48f551\` (\`token\`)`); + await queryRunner.query(`ALTER TABLE \`webapi\` ADD UNIQUE INDEX \`IDX_424ac388ca888a1dc67f5fe3a8\` (\`title\`)`); + await queryRunner.query(`ALTER TABLE \`role\` ADD UNIQUE INDEX \`IDX_367aad98203bd8afaed0d70409\` (\`role\`)`); + await queryRunner.query(`ALTER TABLE \`user\` ADD UNIQUE INDEX \`IDX_7395ecde6cda2e7fe90253ec59\` (\`mail\`)`); + await queryRunner.query(`ALTER TABLE \`user\` ADD UNIQUE INDEX \`IDX_78a916df40e02a9deb1c4b75ed\` (\`username\`)`); + + await queryRunner.query(`ALTER TABLE \`user_permission\` DROP FOREIGN KEY \`FK_deb59c09715314aed1866e18a81\``); + await queryRunner.query(`ALTER TABLE \`refresh\` DROP FOREIGN KEY \`FK_b39e4ed3bfa789758e476870ec2\``); + await queryRunner.query(`ALTER TABLE \`user_roles\` DROP FOREIGN KEY \`FK_472b25323af01488f1f66a06b67\``); + + await queryRunner.query(`ALTER TABLE \`user\` MODIFY COLUMN \`id\` varchar(36) NOT NULL`); + + await queryRunner.query(`ALTER TABLE \`user_permission\` MODIFY COLUMN \`userId\` varchar(36) NOT NULL`); + await queryRunner.query( + `ALTER TABLE \`user_permission\` ADD CONSTRAINT \`FK_deb59c09715314aed1866e18a81\` FOREIGN KEY (\`userId\`) REFERENCES \`user\`(\`id\`) ON DELETE CASCADE ON UPDATE RESTRICT` + ); + + await queryRunner.query(`ALTER TABLE \`refresh\` MODIFY COLUMN \`userId\` varchar(36) NOT NULL`); + await queryRunner.query( + `ALTER TABLE \`refresh\` ADD CONSTRAINT \`FK_b39e4ed3bfa789758e476870ec2\` FOREIGN KEY (\`userId\`) REFERENCES \`user\`(\`id\`) ON DELETE CASCADE ON UPDATE RESTRICT` + ); + + await queryRunner.query(`DROP INDEX \`IDX_472b25323af01488f1f66a06b6\` ON \`user_roles\``); + await queryRunner.query(`ALTER TABLE \`user_roles\` MODIFY COLUMN \`userId\` varchar(36) NOT NULL`); + await queryRunner.query(`CREATE INDEX \`IDX_472b25323af01488f1f66a06b6\` ON \`user_roles\` (\`userId\`)`); + await queryRunner.query( + `ALTER TABLE \`user_roles\` ADD CONSTRAINT \`FK_472b25323af01488f1f66a06b67\` FOREIGN KEY (\`userId\`) REFERENCES \`user\`(\`id\`) ON DELETE CASCADE ON UPDATE RESTRICT` + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE \`webapi\` DROP INDEX \`IDX_424ac388ca888a1dc67f5fe3a8\``); + await queryRunner.query(`ALTER TABLE \`webapi\` DROP INDEX \`IDX_55c99c82f45936d8d34c48f551\``); + await queryRunner.query(`ALTER TABLE \`newsletter\` DROP INDEX \`IDX_80b2037e9f015eb2cf597bae6c\``); + await queryRunner.query(`ALTER TABLE \`template\` DROP INDEX \`IDX_9d0ad817708f4c1a7c78e4abf6\``); + await queryRunner.query(`ALTER TABLE \`query\` DROP INDEX \`IDX_9ffb36037fb8d7456689956ed8\``); + await queryRunner.query(`ALTER TABLE \`calendar_type\` DROP INDEX \`IDX_fbcbfda68b80800afa78e21c88\``); + await queryRunner.query(`ALTER TABLE \`protocol\` DROP INDEX \`IDX_e60159678b8dad84b2e7dfa20c\``); + await queryRunner.query(`ALTER TABLE \`award\` DROP INDEX \`IDX_646bbdb45883cc39c55740d7b6\``); + await queryRunner.query(`ALTER TABLE \`salutation\` DROP INDEX \`IDX_6c6bdcaeb808549ae66c2493ef\``); + await queryRunner.query(`ALTER TABLE \`communication_type\` DROP INDEX \`IDX_63a3b23374fc0dd80e5ab32e09\``); + await queryRunner.query(`ALTER TABLE \`executive_position\` DROP INDEX \`IDX_427169e456c217a317273c31d7\``); + await queryRunner.query(`ALTER TABLE \`qualification\` DROP INDEX \`IDX_2d2540f8b970a5a43a905278da\``); + await queryRunner.query(`ALTER TABLE \`membership_status\` DROP INDEX \`IDX_9d71cc98acfae7a8276718f052\``); + + await queryRunner.query(`ALTER TABLE \`user_roles\` DROP FOREIGN KEY \`FK_472b25323af01488f1f66a06b67\``); + await queryRunner.query(`ALTER TABLE \`user_permission\` DROP FOREIGN KEY \`FK_deb59c09715314aed1866e18a81\``); + await queryRunner.query(`ALTER TABLE \`refresh\` DROP FOREIGN KEY \`FK_b39e4ed3bfa789758e476870ec2\``); + + await queryRunner.query(`ALTER TABLE \`user\` DROP INDEX \`IDX_78a916df40e02a9deb1c4b75ed\``); + await queryRunner.query(`ALTER TABLE \`user\` DROP INDEX \`IDX_7395ecde6cda2e7fe90253ec59\``); + await queryRunner.query(`ALTER TABLE \`user\` MODIFY COLUMN \`id\` int NOT NULL AUTO_INCREMENT`); + + await queryRunner.query(`DROP INDEX \`IDX_472b25323af01488f1f66a06b6\` ON \`user_roles\``); + + await queryRunner.query(`ALTER TABLE \`refresh\` MODIFY COLUMN \`userId\` int NOT NULL`); + await queryRunner.query( + `ALTER TABLE \`refresh\` ADD CONSTRAINT \`FK_b39e4ed3bfa789758e476870ec2\` FOREIGN KEY (\`userId\`) REFERENCES \`user\`(\`id\`) ON DELETE CASCADE ON UPDATE RESTRICT` + ); + + await queryRunner.query(`ALTER TABLE \`user_permission\` MODIFY COLUMN \`userId\` int NOT NULL`); + await queryRunner.query( + `ALTER TABLE \`user_permission\` ADD CONSTRAINT \`FK_deb59c09715314aed1866e18a81\` FOREIGN KEY (\`userId\`) REFERENCES \`user\`(\`id\`) ON DELETE CASCADE ON UPDATE RESTRICT` + ); + + await queryRunner.query(`ALTER TABLE \`user_roles\` MODIFY COLUMN \`userId\` int NOT NULL`); + await queryRunner.query(`CREATE INDEX \`IDX_472b25323af01488f1f66a06b6\` ON \`user_roles\` (\`userId\`)`); + await queryRunner.query( + `ALTER TABLE \`user_roles\` ADD CONSTRAINT \`FK_472b25323af01488f1f66a06b67\` FOREIGN KEY (\`userId\`) REFERENCES \`user\`(\`id\`) ON DELETE CASCADE ON UPDATE RESTRICT` + ); + + await queryRunner.query(`ALTER TABLE \`role\` DROP INDEX \`IDX_367aad98203bd8afaed0d70409\``); + + await queryRunner.query(`CREATE UNIQUE INDEX \`UQ_55c99c82f45936d8d34c48f5515\` ON \`webapi\` (\`token\`)`); + await queryRunner.query(`CREATE UNIQUE INDEX \`UQ_424ac388ca888a1dc67f5fe3a80\` ON \`webapi\` (\`title\`)`); + await queryRunner.query(`CREATE UNIQUE INDEX \`UQ_9ffb36037fb8d7456689956ed80\` ON \`query\` (\`title\`)`); + await queryRunner.query( + `CREATE UNIQUE INDEX \`UQ_6c6bdcaeb808549ae66c2493efd\` ON \`salutation\` (\`salutation\`)` + ); + } +} -- 2.45.3 From f89483f878cfeb39f29c6f99508fbbd50727efb2 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Wed, 29 Jan 2025 08:53:49 +0100 Subject: [PATCH 04/22] change user to uuid --- .../club/member/communicationCommand.ts | 6 +- src/command/club/member/memberAwardCommand.ts | 6 +- src/command/club/member/memberCommand.ts | 4 +- .../member/memberExecutivePositionCommand.ts | 6 +- .../club/member/memberQualificationCommand.ts | 6 +- src/command/club/member/membershipCommand.ts | 6 +- .../newsletter/newsletterRecipientsCommand.ts | 2 +- .../newsletterRecipientsCommandHandler.ts | 4 +- .../club/protocol/protocolPresenceCommand.ts | 2 +- src/command/refreshCommand.ts | 4 +- src/command/user/user/userCommand.ts | 12 ++-- src/command/user/user/userCommandHandler.ts | 8 +-- .../user/user/userPermissionCommand.ts | 6 +- .../user/user/userPermissionCommandHandler.ts | 4 +- src/controller/admin/club/memberController.ts | 65 +++++++++---------- .../admin/club/newsletterController.ts | 6 +- src/controller/admin/user/userController.ts | 14 ++-- src/controller/userController.ts | 10 +-- src/entity/club/member/communication.ts | 2 +- src/entity/club/member/member.ts | 4 +- src/entity/club/member/memberAwards.ts | 2 +- .../club/member/memberExecutivePositions.ts | 2 +- .../club/member/memberQualifications.ts | 2 +- src/entity/club/member/membership.ts | 2 +- .../club/newsletter/newsletterRecipients.ts | 6 +- src/entity/club/protocol/protocolPresence.ts | 2 +- src/helpers/jwtHelper.ts | 4 +- src/helpers/newsletterHelper.ts | 6 +- .../club/member/communicationService.ts | 8 +-- src/service/club/member/memberAwardService.ts | 8 +-- .../member/memberExecutivePositionService.ts | 8 +-- .../club/member/memberQualificationService.ts | 8 +-- src/service/club/member/memberService.ts | 14 ++-- src/service/club/member/membershipService.ts | 12 ++-- src/service/user/userPermissionService.ts | 4 +- src/service/user/userService.ts | 8 +-- src/type/jwtTypes.ts | 2 +- .../admin/club/member/member.models.ts | 4 +- .../admin/club/member/membership.models.ts | 2 +- .../newsletter/newsletterRecipients.models.ts | 2 +- .../club/protocol/protocolPresence.models.ts | 4 +- src/viewmodel/admin/user/user.models.ts | 2 +- src/views/memberExecutivePositionView.ts | 2 +- src/views/memberQualificationsView.ts | 2 +- src/views/memberView.ts | 2 +- src/views/membershipsView.ts | 2 +- 46 files changed, 146 insertions(+), 151 deletions(-) diff --git a/src/command/club/member/communicationCommand.ts b/src/command/club/member/communicationCommand.ts index cc80442..1505fae 100644 --- a/src/command/club/member/communicationCommand.ts +++ b/src/command/club/member/communicationCommand.ts @@ -9,7 +9,7 @@ export interface CreateCommunicationCommand { street: string; streetNumber: number; streetNumberAddition: string; - memberId: number; + memberId: string; typeId: number; } @@ -25,10 +25,10 @@ export interface UpdateCommunicationCommand { street: string; streetNumber: number; streetNumberAddition: string; - memberId: number; + memberId: string; } export interface DeleteCommunicationCommand { id: number; - memberId: number; + memberId: string; } diff --git a/src/command/club/member/memberAwardCommand.ts b/src/command/club/member/memberAwardCommand.ts index b9c2458..acb2620 100644 --- a/src/command/club/member/memberAwardCommand.ts +++ b/src/command/club/member/memberAwardCommand.ts @@ -2,7 +2,7 @@ export interface CreateMemberAwardCommand { given: boolean; note?: string; date: Date; - memberId: number; + memberId: string; awardId: number; } @@ -11,11 +11,11 @@ export interface UpdateMemberAwardCommand { given: boolean; note?: string; date: Date; - memberId: number; + memberId: string; awardId: number; } export interface DeleteMemberAwardCommand { id: number; - memberId: number; + memberId: string; } diff --git a/src/command/club/member/memberCommand.ts b/src/command/club/member/memberCommand.ts index 90ecee0..f5c03d1 100644 --- a/src/command/club/member/memberCommand.ts +++ b/src/command/club/member/memberCommand.ts @@ -8,7 +8,7 @@ export interface CreateMemberCommand { } export interface UpdateMemberCommand { - id: number; + id: string; salutationId: number; firstname: string; lastname: string; @@ -18,5 +18,5 @@ export interface UpdateMemberCommand { } export interface DeleteMemberCommand { - id: number; + id: string; } diff --git a/src/command/club/member/memberExecutivePositionCommand.ts b/src/command/club/member/memberExecutivePositionCommand.ts index 2fd2578..da25ad3 100644 --- a/src/command/club/member/memberExecutivePositionCommand.ts +++ b/src/command/club/member/memberExecutivePositionCommand.ts @@ -2,7 +2,7 @@ export interface CreateMemberExecutivePositionCommand { note?: string; start: Date; end?: Date; - memberId: number; + memberId: string; executivePositionId: number; } @@ -11,11 +11,11 @@ export interface UpdateMemberExecutivePositionCommand { note?: string; start: Date; end?: Date; - memberId: number; + memberId: string; executivePositionId: number; } export interface DeleteMemberExecutivePositionCommand { id: number; - memberId: number; + memberId: string; } diff --git a/src/command/club/member/memberQualificationCommand.ts b/src/command/club/member/memberQualificationCommand.ts index 8c6cb04..76b8920 100644 --- a/src/command/club/member/memberQualificationCommand.ts +++ b/src/command/club/member/memberQualificationCommand.ts @@ -1,7 +1,7 @@ export interface CreateMemberQualificationCommand { note?: string; start: Date; - memberId: number; + memberId: string; qualificationId: number; } @@ -11,11 +11,11 @@ export interface UpdateMemberQualificationCommand { start: Date; end?: Date; terminationReason?: string; - memberId: number; + memberId: string; qualificationId: number; } export interface DeleteMemberQualificationCommand { id: number; - memberId: number; + memberId: string; } diff --git a/src/command/club/member/membershipCommand.ts b/src/command/club/member/membershipCommand.ts index 9e9ca25..28302ca 100644 --- a/src/command/club/member/membershipCommand.ts +++ b/src/command/club/member/membershipCommand.ts @@ -1,6 +1,6 @@ export interface CreateMembershipCommand { start: Date; - memberId: number; + memberId: string; statusId: number; } @@ -9,11 +9,11 @@ export interface UpdateMembershipCommand { start: Date; end?: Date; terminationReason?: string; - memberId: number; + memberId: string; statusId: number; } export interface DeleteMembershipCommand { id: number; - memberId: number; + memberId: string; } diff --git a/src/command/club/newsletter/newsletterRecipientsCommand.ts b/src/command/club/newsletter/newsletterRecipientsCommand.ts index ec69c35..90cb2f7 100644 --- a/src/command/club/newsletter/newsletterRecipientsCommand.ts +++ b/src/command/club/newsletter/newsletterRecipientsCommand.ts @@ -1,4 +1,4 @@ export interface SynchronizeNewsletterRecipientsCommand { newsletterId: number; - recipients: Array; + recipients: Array; } diff --git a/src/command/club/newsletter/newsletterRecipientsCommandHandler.ts b/src/command/club/newsletter/newsletterRecipientsCommandHandler.ts index 262febc..2be82e0 100644 --- a/src/command/club/newsletter/newsletterRecipientsCommandHandler.ts +++ b/src/command/club/newsletter/newsletterRecipientsCommandHandler.ts @@ -42,7 +42,7 @@ export default abstract class NewsletterRecipientsCommandHandler { private static async syncPresenceAdd( manager: EntityManager, newsletterId: number, - recipients: Array + recipients: Array ): Promise { return await manager .createQueryBuilder() @@ -60,7 +60,7 @@ export default abstract class NewsletterRecipientsCommandHandler { private static async syncPresenceRemove( manager: EntityManager, newsletterId: number, - recipients: Array + recipients: Array ): Promise { return await manager .createQueryBuilder() diff --git a/src/command/club/protocol/protocolPresenceCommand.ts b/src/command/club/protocol/protocolPresenceCommand.ts index 134aeac..758ba03 100644 --- a/src/command/club/protocol/protocolPresenceCommand.ts +++ b/src/command/club/protocol/protocolPresenceCommand.ts @@ -4,7 +4,7 @@ export interface SynchronizeProtocolPresenceCommand { } export interface ProtocolPresenceCommand { - memberId: number; + memberId: string; absent: boolean; excused: boolean; } diff --git a/src/command/refreshCommand.ts b/src/command/refreshCommand.ts index 80b14cc..0aa0896 100644 --- a/src/command/refreshCommand.ts +++ b/src/command/refreshCommand.ts @@ -1,9 +1,9 @@ export interface CreateRefreshCommand { - userId: number; + userId: string; isFromPwa?: boolean; } export interface DeleteRefreshCommand { token: string; - userId: number; + userId: string; } diff --git a/src/command/user/user/userCommand.ts b/src/command/user/user/userCommand.ts index cf23bb8..90f9872 100644 --- a/src/command/user/user/userCommand.ts +++ b/src/command/user/user/userCommand.ts @@ -8,7 +8,7 @@ export interface CreateUserCommand { } export interface UpdateUserCommand { - id: number; + id: string; mail: string; username: string; firstname: string; @@ -16,20 +16,20 @@ export interface UpdateUserCommand { } export interface UpdateUserSecretCommand { - id: number; + id: string; secret: string; } export interface TransferUserOwnerCommand { - fromId: number; - toId: number; + fromId: string; + toId: string; } export interface UpdateUserRolesCommand { - id: number; + id: string; roleIds: Array; } export interface DeleteUserCommand { - id: number; + id: string; } diff --git a/src/command/user/user/userCommandHandler.ts b/src/command/user/user/userCommandHandler.ts index affab87..50afb2a 100644 --- a/src/command/user/user/userCommandHandler.ts +++ b/src/command/user/user/userCommandHandler.ts @@ -16,9 +16,9 @@ export default abstract class UserCommandHandler { /** * @description create user * @param {CreateUserCommand} createUser - * @returns {Promise} + * @returns {Promise} */ - static async create(createUser: CreateUserCommand): Promise { + static async create(createUser: CreateUserCommand): Promise { return await dataSource .createQueryBuilder() .insert() @@ -109,11 +109,11 @@ export default abstract class UserCommandHandler { }); } - private static async updateRolesAdd(manager: EntityManager, userId: number, roleId: number): Promise { + private static async updateRolesAdd(manager: EntityManager, userId: string, roleId: number): Promise { return await manager.createQueryBuilder().relation(user, "roles").of(userId).add(roleId); } - private static async updateRolesRemove(manager: EntityManager, userId: number, roleId: number): Promise { + private static async updateRolesRemove(manager: EntityManager, userId: string, roleId: number): Promise { return await manager.createQueryBuilder().relation(user, "roles").of(userId).remove(roleId); } diff --git a/src/command/user/user/userPermissionCommand.ts b/src/command/user/user/userPermissionCommand.ts index 47f7594..c7a8ae6 100644 --- a/src/command/user/user/userPermissionCommand.ts +++ b/src/command/user/user/userPermissionCommand.ts @@ -2,15 +2,15 @@ import { PermissionString } from "../../../type/permissionTypes"; export interface CreateUserPermissionCommand { permission: PermissionString; - userId: number; + userId: string; } export interface DeleteUserPermissionCommand { permission: PermissionString; - userId: number; + userId: string; } export interface UpdateUserPermissionsCommand { - userId: number; + userId: string; permissions: Array; } diff --git a/src/command/user/user/userPermissionCommandHandler.ts b/src/command/user/user/userPermissionCommandHandler.ts index 75c1142..bd6ffc3 100644 --- a/src/command/user/user/userPermissionCommandHandler.ts +++ b/src/command/user/user/userPermissionCommandHandler.ts @@ -42,7 +42,7 @@ export default abstract class UserPermissionCommandHandler { private static async updatePermissionsAdd( manager: EntityManager, - userId: number, + userId: string, permissions: Array ): Promise { return await manager @@ -61,7 +61,7 @@ export default abstract class UserPermissionCommandHandler { private static async updatePermissionsRemove( manager: EntityManager, - userId: number, + userId: string, permissions: Array ): Promise { return await manager diff --git a/src/controller/admin/club/memberController.ts b/src/controller/admin/club/memberController.ts index 42588fd..6758ee6 100644 --- a/src/controller/admin/club/memberController.ts +++ b/src/controller/admin/club/memberController.ts @@ -61,10 +61,7 @@ export async function getAllMembers(req: Request, res: Response): Promise { let count = parseInt((req.query.count as string) ?? "25"); let search = (req.query.search as string) ?? ""; let noLimit = req.query.noLimit === "true"; - let ids = ((req.query.ids ?? "") as string) - .split(",") - .filter((i) => i) - .map((i) => parseInt(i)); + let ids = ((req.query.ids ?? "") as string).split(",").filter((i) => i); let [members, total] = await MemberService.getAll({ offset, count, search, noLimit, ids }); @@ -83,7 +80,7 @@ export async function getAllMembers(req: Request, res: Response): Promise { * @returns {Promise<*>} */ export async function getMemberById(req: Request, res: Response): Promise { - const memberId = parseInt(req.params.id); + const memberId = req.params.id; let member = await MemberService.getById(memberId); res.json(MemberFactory.mapToSingle(member)); @@ -96,7 +93,7 @@ export async function getMemberById(req: Request, res: Response): Promise { * @returns {Promise<*>} */ export async function getMemberStatisticsById(req: Request, res: Response): Promise { - const memberId = parseInt(req.params.id); + const memberId = req.params.id; let member = await MemberService.getStatisticsById(memberId); res.json(MemberFactory.mapToMemberStatistic(member)); @@ -109,7 +106,7 @@ export async function getMemberStatisticsById(req: Request, res: Response): Prom * @returns {Promise<*>} */ export async function getMembershipsByMember(req: Request, res: Response): Promise { - const memberId = parseInt(req.params.memberId); + const memberId = req.params.memberId; let memberships = await MembershipService.getAll(memberId); res.json(MembershipFactory.mapToBase(memberships)); @@ -122,7 +119,7 @@ export async function getMembershipsByMember(req: Request, res: Response): Promi * @returns {Promise<*>} */ export async function getMembershipStatisticsById(req: Request, res: Response): Promise { - const memberId = parseInt(req.params.memberId); + const memberId = req.params.memberId; let member = await MembershipService.getStatisticsById(memberId); res.json(MembershipFactory.mapToBaseStatistics(member)); @@ -135,7 +132,7 @@ export async function getMembershipStatisticsById(req: Request, res: Response): * @returns {Promise<*>} */ export async function getMembershipByMemberAndRecord(req: Request, res: Response): Promise { - const memberId = parseInt(req.params.memberId); + const memberId = req.params.memberId; const recordId = parseInt(req.params.id); let membership = await MembershipService.getById(memberId, recordId); @@ -149,7 +146,7 @@ export async function getMembershipByMemberAndRecord(req: Request, res: Response * @returns {Promise<*>} */ export async function getAwardsByMember(req: Request, res: Response): Promise { - const memberId = parseInt(req.params.memberId); + const memberId = req.params.memberId; let awards = await MemberAwardService.getAll(memberId); res.json(MemberAwardFactory.mapToBase(awards)); @@ -162,7 +159,7 @@ export async function getAwardsByMember(req: Request, res: Response): Promise} */ export async function getAwardByMemberAndRecord(req: Request, res: Response): Promise { - const memberId = parseInt(req.params.memberId); + const memberId = req.params.memberId; const recordId = parseInt(req.params.id); let award = await MemberAwardService.getById(memberId, recordId); @@ -176,7 +173,7 @@ export async function getAwardByMemberAndRecord(req: Request, res: Response): Pr * @returns {Promise<*>} */ export async function getQualificationsByMember(req: Request, res: Response): Promise { - const memberId = parseInt(req.params.memberId); + const memberId = req.params.memberId; let qualifications = await MemberQualificationService.getAll(memberId); res.json(MemberQualificationFactory.mapToBase(qualifications)); @@ -189,7 +186,7 @@ export async function getQualificationsByMember(req: Request, res: Response): Pr * @returns {Promise<*>} */ export async function getQualificationByMemberAndRecord(req: Request, res: Response): Promise { - const memberId = parseInt(req.params.memberId); + const memberId = req.params.memberId; const recordId = parseInt(req.params.id); let qualification = await MemberQualificationService.getById(memberId, recordId); @@ -203,7 +200,7 @@ export async function getQualificationByMemberAndRecord(req: Request, res: Respo * @returns {Promise<*>} */ export async function getExecutivePositionsByMember(req: Request, res: Response): Promise { - const memberId = parseInt(req.params.memberId); + const memberId = req.params.memberId; let positions = await MemberExecutivePositionService.getAll(memberId); res.json(MemberExecutivePositionFactory.mapToBase(positions)); @@ -216,7 +213,7 @@ export async function getExecutivePositionsByMember(req: Request, res: Response) * @returns {Promise<*>} */ export async function getExecutivePositionByMemberAndRecord(req: Request, res: Response): Promise { - const memberId = parseInt(req.params.memberId); + const memberId = req.params.memberId; const recordId = parseInt(req.params.id); let position = await MemberExecutivePositionService.getById(memberId, recordId); @@ -230,7 +227,7 @@ export async function getExecutivePositionByMemberAndRecord(req: Request, res: R * @returns {Promise<*>} */ export async function getCommunicationsByMember(req: Request, res: Response): Promise { - const memberId = parseInt(req.params.memberId); + const memberId = req.params.memberId; let communications = await CommunicationService.getAll(memberId); res.json(CommunicationFactory.mapToBase(communications)); @@ -243,7 +240,7 @@ export async function getCommunicationsByMember(req: Request, res: Response): Pr * @returns {Promise<*>} */ export async function getCommunicationByMemberAndRecord(req: Request, res: Response): Promise { - const memberId = parseInt(req.params.memberId); + const memberId = req.params.memberId; const recordId = parseInt(req.params.id); let communication = await CommunicationService.getById(memberId, recordId); @@ -311,7 +308,7 @@ export async function createMember(req: Request, res: Response): Promise { * @returns {Promise<*>} */ export async function addMembershipToMember(req: Request, res: Response): Promise { - const memberId = parseInt(req.params.memberId); + const memberId = req.params.memberId; const start = req.body.start; const statusId = req.body.statusId; @@ -332,7 +329,7 @@ export async function addMembershipToMember(req: Request, res: Response): Promis * @returns {Promise<*>} */ export async function addAwardToMember(req: Request, res: Response): Promise { - const memberId = parseInt(req.params.memberId); + const memberId = req.params.memberId; const given = req.body.given; const note = req.body.note; const date = req.body.date; @@ -357,7 +354,7 @@ export async function addAwardToMember(req: Request, res: Response): Promise} */ export async function addQualificationToMember(req: Request, res: Response): Promise { - const memberId = parseInt(req.params.memberId); + const memberId = req.params.memberId; const note = req.body.note; const start = req.body.start; const qualificationId = req.body.qualificationId; @@ -380,7 +377,7 @@ export async function addQualificationToMember(req: Request, res: Response): Pro * @returns {Promise<*>} */ export async function addExecutivePositionToMember(req: Request, res: Response): Promise { - const memberId = parseInt(req.params.memberId); + const memberId = req.params.memberId; const note = req.body.note; const start = req.body.start; const end = req.body.end || null; @@ -405,7 +402,7 @@ export async function addExecutivePositionToMember(req: Request, res: Response): * @returns {Promise<*>} */ export async function addCommunicationToMember(req: Request, res: Response): Promise { - const memberId = parseInt(req.params.memberId); + const memberId = req.params.memberId; const preferred = req.body.preferred; const isSMSAlarming = req.body.isSMSAlarming; const isSendNewsletter = req.body.isNewsletterMain; @@ -444,7 +441,7 @@ export async function addCommunicationToMember(req: Request, res: Response): Pro * @returns {Promise<*>} */ export async function updateMemberById(req: Request, res: Response): Promise { - const memberId = parseInt(req.params.id); + const memberId = req.params.id; const salutationId = parseInt(req.body.salutationId); const firstname = req.body.firstname; const lastname = req.body.lastname; @@ -473,7 +470,7 @@ export async function updateMemberById(req: Request, res: Response): Promise} */ export async function updateMembershipOfMember(req: Request, res: Response): Promise { - const memberId = parseInt(req.params.memberId); + const memberId = req.params.memberId; const recordId = parseInt(req.params.recordId); const start = req.body.start; const end = req.body.end || null; @@ -500,7 +497,7 @@ export async function updateMembershipOfMember(req: Request, res: Response): Pro * @returns {Promise<*>} */ export async function updateAwardOfMember(req: Request, res: Response): Promise { - const memberId = parseInt(req.params.memberId); + const memberId = req.params.memberId; const recordId = parseInt(req.params.recordId); const given = req.body.given; const note = req.body.note; @@ -527,7 +524,7 @@ export async function updateAwardOfMember(req: Request, res: Response): Promise< * @returns {Promise<*>} */ export async function updateQualificationOfMember(req: Request, res: Response): Promise { - const memberId = parseInt(req.params.memberId); + const memberId = req.params.memberId; const recordId = parseInt(req.params.recordId); const note = req.body.note; const start = req.body.start; @@ -556,7 +553,7 @@ export async function updateQualificationOfMember(req: Request, res: Response): * @returns {Promise<*>} */ export async function updateExecutivePositionOfMember(req: Request, res: Response): Promise { - const memberId = parseInt(req.params.memberId); + const memberId = req.params.memberId; const recordId = parseInt(req.params.recordId); const note = req.body.note; const start = req.body.start; @@ -583,7 +580,7 @@ export async function updateExecutivePositionOfMember(req: Request, res: Respons * @returns {Promise<*>} */ export async function updateCommunicationOfMember(req: Request, res: Response): Promise { - const memberId = parseInt(req.params.memberId); + const memberId = req.params.memberId; const recordId = parseInt(req.params.recordId); const preferred = req.body.preferred; const isSMSAlarming = req.body.isSMSAlarming; @@ -622,7 +619,7 @@ export async function updateCommunicationOfMember(req: Request, res: Response): * @returns {Promise<*>} */ export async function deleteMemberById(req: Request, res: Response): Promise { - const memberId = parseInt(req.params.id); + const memberId = req.params.id; let deleteMember: DeleteMemberCommand = { id: memberId, @@ -639,7 +636,7 @@ export async function deleteMemberById(req: Request, res: Response): Promise} */ export async function deleteMembershipOfMember(req: Request, res: Response): Promise { - const memberId = parseInt(req.params.memberId); + const memberId = req.params.memberId; const recordId = parseInt(req.params.recordId); let deleteMembership: DeleteMembershipCommand = { @@ -658,7 +655,7 @@ export async function deleteMembershipOfMember(req: Request, res: Response): Pro * @returns {Promise<*>} */ export async function deleteAwardOfMember(req: Request, res: Response): Promise { - const memberId = parseInt(req.params.memberId); + const memberId = req.params.memberId; const recordId = parseInt(req.params.recordId); let deleteMemberAward: DeleteMemberAwardCommand = { @@ -677,7 +674,7 @@ export async function deleteAwardOfMember(req: Request, res: Response): Promise< * @returns {Promise<*>} */ export async function deleteQualificationOfMember(req: Request, res: Response): Promise { - const memberId = parseInt(req.params.memberId); + const memberId = req.params.memberId; const recordId = parseInt(req.params.recordId); let deleteMemberQualification: DeleteMemberQualificationCommand = { @@ -696,7 +693,7 @@ export async function deleteQualificationOfMember(req: Request, res: Response): * @returns {Promise<*>} */ export async function deleteExecutivePositionOfMember(req: Request, res: Response): Promise { - const memberId = parseInt(req.params.memberId); + const memberId = req.params.memberId; const recordId = parseInt(req.params.recordId); let deleteMemberExecutivePosition: DeleteMemberExecutivePositionCommand = { @@ -715,7 +712,7 @@ export async function deleteExecutivePositionOfMember(req: Request, res: Respons * @returns {Promise<*>} */ export async function deleteCommunicationOfMember(req: Request, res: Response): Promise { - const memberId = parseInt(req.params.memberId); + const memberId = req.params.memberId; const recordId = parseInt(req.params.recordId); let deleteCommunication: DeleteCommunicationCommand = { diff --git a/src/controller/admin/club/newsletterController.ts b/src/controller/admin/club/newsletterController.ts index f63fd5e..eeb25e6 100644 --- a/src/controller/admin/club/newsletterController.ts +++ b/src/controller/admin/club/newsletterController.ts @@ -135,7 +135,7 @@ export async function createNewsletterPrintoutPreviewById(req: Request, res: Res let newsletterId = parseInt(req.params.newsletterId); let newsletter = await NewsletterService.getById(newsletterId); let dates = await NewsletterDatesService.getAll(newsletterId); - let recipient = await UserService.getById(parseInt(req.userId)); + let recipient = await UserService.getById(req.userId); let data = NewsletterHelper.buildData(newsletter, dates); data.recipient = { @@ -241,7 +241,7 @@ export async function createNewsletterMailPreviewById(req: Request, res: Respons let newsletterId = parseInt(req.params.newsletterId); let newsletter = await NewsletterService.getById(newsletterId); let dates = await NewsletterDatesService.getAll(newsletterId); - let recipient = await UserService.getById(parseInt(req.userId)); + let recipient = await UserService.getById(req.userId); let data = NewsletterHelper.buildData(newsletter, dates); data.recipient = { @@ -375,7 +375,7 @@ export async function synchronizeNewsletterDatesById(req: Request, res: Response */ export async function synchronizeNewsletterRecipientsById(req: Request, res: Response): Promise { let newsletterId = parseInt(req.params.newsletterId); - let recipients = req.body.recipients as Array; + let recipients = req.body.recipients as Array; let syncRecipients: SynchronizeNewsletterRecipientsCommand = { newsletterId, diff --git a/src/controller/admin/user/userController.ts b/src/controller/admin/user/userController.ts index f6ad511..58d815e 100644 --- a/src/controller/admin/user/userController.ts +++ b/src/controller/admin/user/userController.ts @@ -31,7 +31,7 @@ export async function getAllUsers(req: Request, res: Response): Promise { * @returns {Promise<*>} */ export async function getUserById(req: Request, res: Response): Promise { - const id = parseInt(req.params.id); + const id = req.params.id; let user = await UserService.getById(id); res.json(UserFactory.mapToSingle(user)); @@ -44,7 +44,7 @@ export async function getUserById(req: Request, res: Response): Promise { * @returns {Promise<*>} */ export async function getUserPermissions(req: Request, res: Response): Promise { - const id = parseInt(req.params.id); + const id = req.params.id; let permissions = await UserPermissionService.getByUser(id); res.json(PermissionHelper.convertToObject(permissions.map((p) => p.permission))); @@ -57,7 +57,7 @@ export async function getUserPermissions(req: Request, res: Response): Promise} */ export async function getUserRoles(req: Request, res: Response): Promise { - const id = parseInt(req.params.id); + const id = req.params.id; let roles = await UserService.getAssignedRolesByUserId(id); @@ -71,7 +71,7 @@ export async function getUserRoles(req: Request, res: Response): Promise { * @returns {Promise<*>} */ export async function updateUser(req: Request, res: Response): Promise { - const id = parseInt(req.params.id); + const id = req.params.id; let mail = req.body.mail; let firstname = req.body.firstname; let lastname = req.body.lastname; @@ -96,7 +96,7 @@ export async function updateUser(req: Request, res: Response): Promise { * @returns {Promise<*>} */ export async function updateUserPermissions(req: Request, res: Response): Promise { - const id = parseInt(req.params.id); + const id = req.params.id; let permissions = req.body.permissions; let permissionStrings = PermissionHelper.convertToStringArray(permissions); @@ -117,7 +117,7 @@ export async function updateUserPermissions(req: Request, res: Response): Promis * @returns {Promise<*>} */ export async function updateUserRoles(req: Request, res: Response): Promise { - const id = parseInt(req.params.id); + const id = req.params.id; let roleIds = req.body.roleIds as Array; let updateRoles: UpdateUserRolesCommand = { @@ -136,7 +136,7 @@ export async function updateUserRoles(req: Request, res: Response): Promise * @returns {Promise<*>} */ export async function deleteUser(req: Request, res: Response): Promise { - const id = parseInt(req.params.id); + const id = req.params.id; let { mail, isOwner } = await UserService.getById(id); diff --git a/src/controller/userController.ts b/src/controller/userController.ts index a29487b..fb5b874 100644 --- a/src/controller/userController.ts +++ b/src/controller/userController.ts @@ -16,7 +16,7 @@ import ForbiddenRequestException from "../exceptions/forbiddenRequestException"; * @returns {Promise<*>} */ export async function getMeById(req: Request, res: Response): Promise { - const id = parseInt(req.userId); + const id = req.userId; let user = await UserService.getById(id); res.json(UserFactory.mapToSingle(user)); @@ -29,7 +29,7 @@ export async function getMeById(req: Request, res: Response): Promise { * @returns {Promise<*>} */ export async function getMyTotp(req: Request, res: Response): Promise { - const userId = parseInt(req.userId); + const userId = req.userId; let { secret } = await UserService.getById(userId); @@ -54,7 +54,7 @@ export async function getMyTotp(req: Request, res: Response): Promise { * @returns {Promise<*>} */ export async function verifyMyTotp(req: Request, res: Response): Promise { - const userId = parseInt(req.userId); + const userId = req.userId; let totp = req.body.totp; let { secret } = await UserService.getById(userId); @@ -78,7 +78,7 @@ export async function verifyMyTotp(req: Request, res: Response): Promise { * @returns {Promise<*>} */ export async function transferOwnership(req: Request, res: Response): Promise { - const userId = parseInt(req.userId); + const userId = req.userId; let toId = req.body.toId; let { isOwner } = await UserService.getById(userId); @@ -102,7 +102,7 @@ export async function transferOwnership(req: Request, res: Response): Promise} */ export async function updateMe(req: Request, res: Response): Promise { - const id = parseInt(req.userId); + const id = req.userId; let mail = req.body.mail; let firstname = req.body.firstname; let lastname = req.body.lastname; diff --git a/src/entity/club/member/communication.ts b/src/entity/club/member/communication.ts index c3c79fc..574f3c5 100644 --- a/src/entity/club/member/communication.ts +++ b/src/entity/club/member/communication.ts @@ -38,7 +38,7 @@ export class communication { streetNumberAddition: string; @Column() - memberId: number; + memberId: string; @Column() typeId: number; diff --git a/src/entity/club/member/member.ts b/src/entity/club/member/member.ts index d20ba70..a584222 100644 --- a/src/entity/club/member/member.ts +++ b/src/entity/club/member/member.ts @@ -8,8 +8,8 @@ import { salutation } from "../../settings/salutation"; @Entity() export class member { - @PrimaryColumn({ generated: "increment", type: "int" }) - id: number; + @PrimaryColumn({ generated: "uuid", type: "varchar" }) + id: string; @Column({ type: "varchar", length: 255 }) firstname: string; diff --git a/src/entity/club/member/memberAwards.ts b/src/entity/club/member/memberAwards.ts index ef5b473..bf11ee6 100644 --- a/src/entity/club/member/memberAwards.ts +++ b/src/entity/club/member/memberAwards.ts @@ -17,7 +17,7 @@ export class memberAwards { date: Date; @Column() - memberId: number; + memberId: string; @Column() awardId: number; diff --git a/src/entity/club/member/memberExecutivePositions.ts b/src/entity/club/member/memberExecutivePositions.ts index b4475ef..0051184 100644 --- a/src/entity/club/member/memberExecutivePositions.ts +++ b/src/entity/club/member/memberExecutivePositions.ts @@ -17,7 +17,7 @@ export class memberExecutivePositions { end?: Date; @Column() - memberId: number; + memberId: string; @Column() executivePositionId: number; diff --git a/src/entity/club/member/memberQualifications.ts b/src/entity/club/member/memberQualifications.ts index c4d7d09..78cd610 100644 --- a/src/entity/club/member/memberQualifications.ts +++ b/src/entity/club/member/memberQualifications.ts @@ -20,7 +20,7 @@ export class memberQualifications { terminationReason?: string; @Column() - memberId: number; + memberId: string; @Column() qualificationId: number; diff --git a/src/entity/club/member/membership.ts b/src/entity/club/member/membership.ts index aa14f27..14e9b6f 100644 --- a/src/entity/club/member/membership.ts +++ b/src/entity/club/member/membership.ts @@ -17,7 +17,7 @@ export class membership { terminationReason?: string; @Column() - memberId: number; + memberId: string; @Column() statusId: number; diff --git a/src/entity/club/newsletter/newsletterRecipients.ts b/src/entity/club/newsletter/newsletterRecipients.ts index 21f696f..840f178 100644 --- a/src/entity/club/newsletter/newsletterRecipients.ts +++ b/src/entity/club/newsletter/newsletterRecipients.ts @@ -4,11 +4,11 @@ import { member } from "../member/member"; @Entity() export class newsletterRecipients { - @PrimaryColumn({ type: "int" }) + @PrimaryColumn() newsletterId: number; - @PrimaryColumn({ type: "int" }) - memberId: number; + @PrimaryColumn() + memberId: string; @ManyToOne(() => newsletter, (newsletter) => newsletter.recipients, { nullable: false, diff --git a/src/entity/club/protocol/protocolPresence.ts b/src/entity/club/protocol/protocolPresence.ts index 15fe061..707d6e5 100644 --- a/src/entity/club/protocol/protocolPresence.ts +++ b/src/entity/club/protocol/protocolPresence.ts @@ -5,7 +5,7 @@ import { member } from "../member/member"; @Entity() export class protocolPresence { @PrimaryColumn() - memberId: number; + memberId: string; @PrimaryColumn() protocolId: number; diff --git a/src/helpers/jwtHelper.ts b/src/helpers/jwtHelper.ts index 791af2a..11a5232 100644 --- a/src/helpers/jwtHelper.ts +++ b/src/helpers/jwtHelper.ts @@ -50,7 +50,7 @@ export abstract class JWTHelper { }); } - static async buildToken(id: number): Promise { + static async buildToken(id: string): Promise { let { firstname, lastname, mail, username, isOwner } = await UserService.getById(id); let userPermissions = await UserPermissionService.getByUser(id); let userPermissionStrings = userPermissions.map((e) => e.permission); @@ -86,7 +86,7 @@ export abstract class JWTHelper { let permissionObject = PermissionHelper.convertToObject(webapiPermissionStrings); let jwtData: JWTToken = { - userId: id, + userId: id.toString(), mail: "", username: title, firstname: "", diff --git a/src/helpers/newsletterHelper.ts b/src/helpers/newsletterHelper.ts index 1e458c1..56f5865 100644 --- a/src/helpers/newsletterHelper.ts +++ b/src/helpers/newsletterHelper.ts @@ -121,7 +121,7 @@ export abstract class NewsletterHelper { ): Promise> { let useQuery = newsletter.recipientsByQuery?.query; - let queryMemberIds: Array = []; + let queryMemberIds: Array = []; if (useQuery) { let result = await DynamicQueryBuilder.executeQuery( useQuery.startsWith("{") ? JSON.parse(useQuery) : useQuery, @@ -131,7 +131,7 @@ export abstract class NewsletterHelper { if (result.stats == "success") { let keys = Object.keys(result.rows?.[0] ?? {}); let memberKey = keys.find((k) => k.includes("member_id")); - queryMemberIds = result.rows.map((t) => parseInt((t[memberKey] ?? t.id) as string)); + queryMemberIds = result.rows.map((t) => (t[memberKey] ?? t.id) as string); } } @@ -253,7 +253,7 @@ export abstract class NewsletterHelper { for (const [index, rec] of [ ...pdfRecipients, - { id: 0, firstname: "Alle Mitglieder", lastname: CLUB_NAME } as member, + { id: "0", firstname: "Alle Mitglieder", lastname: CLUB_NAME } as member, ].entries()) { let data = this.buildData(newsletter, dates, rec, printWithAdress.includes(rec.sendNewsletter?.type?.id)); diff --git a/src/service/club/member/communicationService.ts b/src/service/club/member/communicationService.ts index b828bc7..2a016d7 100644 --- a/src/service/club/member/communicationService.ts +++ b/src/service/club/member/communicationService.ts @@ -5,10 +5,10 @@ import InternalException from "../../../exceptions/internalException"; export default abstract class CommunicationService { /** * @description get all by member id - * @param {number} memberId + * @param {string} memberId * @returns {Promise>} */ - static async getAll(memberId: number): Promise> { + static async getAll(memberId: string): Promise> { return await dataSource .getRepository(communication) .createQueryBuilder("communication") @@ -27,11 +27,11 @@ export default abstract class CommunicationService { /** * @description get all by memberId and recordId - * @param {number} memberId + * @param {string} memberId * @param {number} recordId * @returns {Promise} */ - static async getById(memberId: number, recordId: number): Promise { + static async getById(memberId: string, recordId: number): Promise { return await dataSource .getRepository(communication) .createQueryBuilder("communication") diff --git a/src/service/club/member/memberAwardService.ts b/src/service/club/member/memberAwardService.ts index db69f06..8e3aa8e 100644 --- a/src/service/club/member/memberAwardService.ts +++ b/src/service/club/member/memberAwardService.ts @@ -5,10 +5,10 @@ import InternalException from "../../../exceptions/internalException"; export default abstract class MemberAwardService { /** * @description get all by member id - * @param {number} memberId + * @param {string} memberId * @returns {Promise>} */ - static async getAll(memberId: number): Promise> { + static async getAll(memberId: string): Promise> { return await dataSource .getRepository(memberAwards) .createQueryBuilder("memberAwards") @@ -26,11 +26,11 @@ export default abstract class MemberAwardService { /** * @description get by memberId and recordId - * @param {number} memberId + * @param {string} memberId * @param {number} recordId * @returns {Promise>} */ - static async getById(memberId: number, recordId: number): Promise { + static async getById(memberId: string, recordId: number): Promise { return await dataSource .getRepository(memberAwards) .createQueryBuilder("memberAwards") diff --git a/src/service/club/member/memberExecutivePositionService.ts b/src/service/club/member/memberExecutivePositionService.ts index ff15ba0..93d76eb 100644 --- a/src/service/club/member/memberExecutivePositionService.ts +++ b/src/service/club/member/memberExecutivePositionService.ts @@ -5,10 +5,10 @@ import InternalException from "../../../exceptions/internalException"; export default abstract class MemberExecutivePositionService { /** * @description get all by member id - * @param {number} memberId + * @param {string} memberId * @returns {Promise>} */ - static async getAll(memberId: number): Promise> { + static async getAll(memberId: string): Promise> { return await dataSource .getRepository(memberExecutivePositions) .createQueryBuilder("memberExecutivePositions") @@ -27,11 +27,11 @@ export default abstract class MemberExecutivePositionService { /** * @description get by memberId and recordId - * @param {number} memberId + * @param {string} memberId * @param {number} recordId * @returns {Promise>} */ - static async getById(memberId: number, recordId: number): Promise { + static async getById(memberId: string, recordId: number): Promise { return await dataSource .getRepository(memberExecutivePositions) .createQueryBuilder("memberExecutivePositions") diff --git a/src/service/club/member/memberQualificationService.ts b/src/service/club/member/memberQualificationService.ts index 84c6a5f..afd3ee5 100644 --- a/src/service/club/member/memberQualificationService.ts +++ b/src/service/club/member/memberQualificationService.ts @@ -5,10 +5,10 @@ import InternalException from "../../../exceptions/internalException"; export default abstract class MemberQualificationService { /** * @description get all by member id - * @param {number} memberId + * @param {string} memberId * @returns {Promise>} */ - static async getAll(memberId: number): Promise> { + static async getAll(memberId: string): Promise> { return await dataSource .getRepository(memberQualifications) .createQueryBuilder("memberQualifications") @@ -26,11 +26,11 @@ export default abstract class MemberQualificationService { /** * @description get by memberId and recordId - * @param {number} memberId + * @param {string} memberId * @param {number} recordId * @returns {Promise>} */ - static async getById(memberId: number, recordId: number): Promise { + static async getById(memberId: string, recordId: number): Promise { return await dataSource .getRepository(memberQualifications) .createQueryBuilder("memberQualifications") diff --git a/src/service/club/member/memberService.ts b/src/service/club/member/memberService.ts index 4c7ddb3..5ec6d09 100644 --- a/src/service/club/member/memberService.ts +++ b/src/service/club/member/memberService.ts @@ -20,7 +20,7 @@ export default abstract class MemberService { count?: number; search?: string; noLimit?: boolean; - ids?: Array; + ids?: Array; }): Promise<[Array, number]> { let query = dataSource .getRepository(member) @@ -96,10 +96,10 @@ export default abstract class MemberService { /** * @description get member by id - * @param {number} id + * @param {string} id * @returns {Promise} */ - static async getById(id: number): Promise { + static async getById(id: string): Promise { return await dataSource .getRepository(member) .createQueryBuilder("member") @@ -146,10 +146,10 @@ export default abstract class MemberService { /** * @description get member statistics by id - * @param {number} id + * @param {string} id * @returns {Promise} */ - static async getStatisticsById(id: number): Promise { + static async getStatisticsById(id: string): Promise { return await dataSource .getRepository(memberView) .createQueryBuilder("memberView") @@ -187,10 +187,10 @@ export default abstract class MemberService { /** * @description get newsletter by member by id - * @param {number} id + * @param {string} id * @returns {Promise} */ - static async getNewsletterById(id: number): Promise { + static async getNewsletterById(id: string): Promise { return await dataSource .getRepository(member) .createQueryBuilder("member") diff --git a/src/service/club/member/membershipService.ts b/src/service/club/member/membershipService.ts index 8ef99e1..5067e81 100644 --- a/src/service/club/member/membershipService.ts +++ b/src/service/club/member/membershipService.ts @@ -6,10 +6,10 @@ import { membershipView } from "../../../views/membershipsView"; export default abstract class MembershipService { /** * @description get all by member id - * @param {number} memberId + * @param {string} memberId * @returns {Promise>} */ - static async getAll(memberId: number): Promise> { + static async getAll(memberId: string): Promise> { return await dataSource .getRepository(membership) .createQueryBuilder("membership") @@ -27,11 +27,11 @@ export default abstract class MembershipService { /** * @description get by memberId and recordId - * @param {number} memberId + * @param {string} memberId * @param {number} recordId * @returns {Promise>} */ - static async getById(memberId: number, recordId: number): Promise { + static async getById(memberId: string, recordId: number): Promise { return await dataSource .getRepository(membership) .createQueryBuilder("membership") @@ -49,10 +49,10 @@ export default abstract class MembershipService { /** * @description get membership statistics by memberId - * @param {number} memberId + * @param {string} memberId * @returns {Promise>} */ - static async getStatisticsById(memberId: number): Promise> { + static async getStatisticsById(memberId: string): Promise> { return await dataSource .getRepository(membershipView) .createQueryBuilder("membershipView") diff --git a/src/service/user/userPermissionService.ts b/src/service/user/userPermissionService.ts index 2f5a059..0078ee3 100644 --- a/src/service/user/userPermissionService.ts +++ b/src/service/user/userPermissionService.ts @@ -5,10 +5,10 @@ import InternalException from "../../exceptions/internalException"; export default abstract class UserPermissionService { /** * @description get permission by user - * @param userId number + * @param userId string * @returns {Promise>} */ - static async getByUser(userId: number): Promise> { + static async getByUser(userId: string): Promise> { return await dataSource .getRepository(userPermission) .createQueryBuilder("permission") diff --git a/src/service/user/userService.ts b/src/service/user/userService.ts index 45102a5..b75d499 100644 --- a/src/service/user/userService.ts +++ b/src/service/user/userService.ts @@ -28,10 +28,10 @@ export default abstract class UserService { /** * @description get user by id - * @param id number + * @param id string * @returns {Promise} */ - static async getById(id: number): Promise { + static async getById(id: string): Promise { return await dataSource .getRepository(user) .createQueryBuilder("user") @@ -110,10 +110,10 @@ export default abstract class UserService { /** * @description get roles assigned to user - * @param userId number + * @param userId string * @returns {Promise>} */ - static async getAssignedRolesByUserId(userId: number): Promise> { + static async getAssignedRolesByUserId(userId: string): Promise> { return await dataSource .getRepository(user) .createQueryBuilder("user") diff --git a/src/type/jwtTypes.ts b/src/type/jwtTypes.ts index 1d65d38..6a0486d 100644 --- a/src/type/jwtTypes.ts +++ b/src/type/jwtTypes.ts @@ -5,7 +5,7 @@ export type JWTData = { }; export type JWTToken = { - userId: number; + userId: string; mail: string; username: string; firstname: string; diff --git a/src/viewmodel/admin/club/member/member.models.ts b/src/viewmodel/admin/club/member/member.models.ts index 2254d68..466a6f7 100644 --- a/src/viewmodel/admin/club/member/member.models.ts +++ b/src/viewmodel/admin/club/member/member.models.ts @@ -3,7 +3,7 @@ import { CommunicationViewModel } from "./communication.models"; import { MembershipViewModel } from "./membership.models"; export interface MemberViewModel { - id: number; + id: string; salutation: SalutationViewModel; firstname: string; lastname: string; @@ -18,7 +18,7 @@ export interface MemberViewModel { } export interface MemberStatisticsViewModel { - id: number; + id: string; salutation: string; firstname: string; lastname: string; diff --git a/src/viewmodel/admin/club/member/membership.models.ts b/src/viewmodel/admin/club/member/membership.models.ts index 1db540a..6b53086 100644 --- a/src/viewmodel/admin/club/member/membership.models.ts +++ b/src/viewmodel/admin/club/member/membership.models.ts @@ -12,7 +12,7 @@ export interface MembershipStatisticsViewModel { durationInYears: string; status: string; statusId: number; - memberId: number; + memberId: string; memberSalutation: string; memberFirstname: string; memberLastname: string; diff --git a/src/viewmodel/admin/club/newsletter/newsletterRecipients.models.ts b/src/viewmodel/admin/club/newsletter/newsletterRecipients.models.ts index 7db108a..25f0170 100644 --- a/src/viewmodel/admin/club/newsletter/newsletterRecipients.models.ts +++ b/src/viewmodel/admin/club/newsletter/newsletterRecipients.models.ts @@ -2,6 +2,6 @@ import { MemberViewModel } from "../member/member.models"; export interface NewsletterRecipientsViewModel { newsletterId: number; - memberId: number; + memberId: string; member: MemberViewModel; } diff --git a/src/viewmodel/admin/club/protocol/protocolPresence.models.ts b/src/viewmodel/admin/club/protocol/protocolPresence.models.ts index 9bc363c..c954966 100644 --- a/src/viewmodel/admin/club/protocol/protocolPresence.models.ts +++ b/src/viewmodel/admin/club/protocol/protocolPresence.models.ts @@ -1,7 +1,5 @@ -import { MemberViewModel } from "../member/member.models"; - export interface ProtocolPresenceViewModel { - memberId: number; + memberId: string; absent: boolean; excused: boolean; protocolId: number; diff --git a/src/viewmodel/admin/user/user.models.ts b/src/viewmodel/admin/user/user.models.ts index 8bc495b..b9c5984 100644 --- a/src/viewmodel/admin/user/user.models.ts +++ b/src/viewmodel/admin/user/user.models.ts @@ -2,7 +2,7 @@ import { PermissionObject } from "../../../type/permissionTypes"; import { RoleViewModel } from "./role.models"; export interface UserViewModel { - id: number; + id: string; username: string; mail: string; firstname: string; diff --git a/src/views/memberExecutivePositionView.ts b/src/views/memberExecutivePositionView.ts index c1df465..6bac173 100644 --- a/src/views/memberExecutivePositionView.ts +++ b/src/views/memberExecutivePositionView.ts @@ -35,7 +35,7 @@ export class memberExecutivePositionsView { positionId: number; @ViewColumn() - memberId: number; + memberId: string; @ViewColumn() memberSalutation: string; diff --git a/src/views/memberQualificationsView.ts b/src/views/memberQualificationsView.ts index b22e05e..9db700c 100644 --- a/src/views/memberQualificationsView.ts +++ b/src/views/memberQualificationsView.ts @@ -35,7 +35,7 @@ export class memberQualificationsView { qualificationId: number; @ViewColumn() - memberId: number; + memberId: string; @ViewColumn() memberSalutation: string; diff --git a/src/views/memberView.ts b/src/views/memberView.ts index 90debc3..74be622 100644 --- a/src/views/memberView.ts +++ b/src/views/memberView.ts @@ -19,7 +19,7 @@ import { member } from "../entity/club/member/member"; }) export class memberView { @ViewColumn() - id: number; + id: string; @ViewColumn() salutation: string; diff --git a/src/views/membershipsView.ts b/src/views/membershipsView.ts index 1c764bf..52ebcc7 100644 --- a/src/views/membershipsView.ts +++ b/src/views/membershipsView.ts @@ -39,7 +39,7 @@ export class membershipView { statusId: number; @ViewColumn() - memberId: number; + memberId: string; @ViewColumn() memberSalutation: string; -- 2.45.3 From f245ff74a88f8afb77c873f1352c2231ccfc4327 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Wed, 29 Jan 2025 09:42:22 +0100 Subject: [PATCH 05/22] update database error messages --- .../club/calendar/calendarCommandHandler.ts | 7 +- .../member/communicationCommandHandler.ts | 7 +- .../club/member/memberAwardCommandHandler.ts | 7 +- .../club/member/memberCommandHandler.ts | 13 +- .../memberExecutivePositionCommandHandler.ts | 7 +- .../memberQualificationCommandHandler.ts | 7 +- .../club/member/membershipCommandHandler.ts | 7 +- .../newsletter/newsletterCommandHandler.ts | 7 +- .../newsletterDatesCommandHandler.ts | 3 +- .../newsletterRecipientsCommandHandler.ts | 3 +- .../protocol/protocolAgendaCommandHandler.ts | 5 +- .../club/protocol/protocolCommandHandler.ts | 5 +- .../protocolDecisionCommandHandler.ts | 5 +- .../protocolPresenceCommandHandler.ts | 3 +- .../protocolPrintoutCommandHandler.ts | 3 +- .../protocol/protocolVotingCommandHandler.ts | 5 +- src/command/refreshCommandHandler.ts | 7 +- src/command/resetCommandHandler.ts | 5 +- .../settings/award/awardCommandHandler.ts | 10 +- .../calendarTypeCommandHandler.ts | 10 +- .../communicationTypeCommandHandler.ts | 12 +- .../executivePositionCommandHandler.ts | 12 +- .../membershipStatusCommandHandler.ts | 12 +- .../newsletterConfigCommandHandler.ts | 3 +- .../qualificationCommandHandler.ts | 10 +- .../queryStore/queryStoreCommandHandler.ts | 10 +- .../salutation/salutationCommandHandler.ts | 10 +- .../template/templateCommandHandler.ts | 10 +- .../templateUsageCommandHandler.ts | 3 +- src/command/user/role/roleCommandHandler.ts | 7 +- .../user/role/rolePermissionCommandHandler.ts | 45 +--- src/command/user/user/inviteCommandHandler.ts | 7 +- src/command/user/user/userCommandHandler.ts | 13 +- .../user/user/userPermissionCommandHandler.ts | 45 +--- .../user/webapi/webapiCommandHandler.ts | 15 +- .../webapi/webapiPermissionCommandHandler.ts | 45 +--- src/exceptions/databaseActionException.ts | 8 + src/service/club/calendarService.ts | 9 +- .../club/member/communicationService.ts | 5 +- src/service/club/member/memberAwardService.ts | 5 +- .../member/memberExecutivePositionService.ts | 5 +- .../club/member/memberQualificationService.ts | 5 +- src/service/club/member/memberService.ts | 197 ++++++++---------- src/service/club/member/membershipService.ts | 7 +- .../club/newsletter/newsletterDatesService.ts | 3 +- .../newsletter/newsletterRecipientsService.ts | 3 +- .../club/newsletter/newsletterService.ts | 5 +- .../club/protocol/protocolAgendaService.ts | 5 +- .../club/protocol/protocolDecisionService.ts | 5 +- .../club/protocol/protocolPrecenseService.ts | 3 +- .../club/protocol/protocolPrintoutService.ts | 7 +- src/service/club/protocol/protocolService.ts | 3 +- .../club/protocol/protocolVotingService.ts | 5 +- src/service/resetService.ts | 3 +- src/service/settings/awardService.ts | 5 +- src/service/settings/calendarTypeService.ts | 7 +- .../settings/communicationTypeService.ts | 5 +- .../settings/executivePositionService.ts | 5 +- .../settings/membershipStatusService.ts | 5 +- .../settings/newsletterConfigService.ts | 5 +- src/service/settings/qualification.ts | 5 +- src/service/settings/queryStoreService.ts | 5 +- src/service/settings/salutationService.ts | 5 +- src/service/settings/templateService.ts | 5 +- src/service/settings/templateUsageService.ts | 3 +- src/service/user/inviteService.ts | 5 +- src/service/user/rolePermissionService.ts | 5 +- src/service/user/roleService.ts | 5 +- src/service/user/userPermissionService.ts | 3 +- src/service/user/userService.ts | 13 +- src/service/user/webapiPermissionService.ts | 3 +- src/service/user/webapiService.ts | 9 +- 72 files changed, 325 insertions(+), 441 deletions(-) create mode 100644 src/exceptions/databaseActionException.ts diff --git a/src/command/club/calendar/calendarCommandHandler.ts b/src/command/club/calendar/calendarCommandHandler.ts index 5cd89b6..97fbff4 100644 --- a/src/command/club/calendar/calendarCommandHandler.ts +++ b/src/command/club/calendar/calendarCommandHandler.ts @@ -1,6 +1,7 @@ import { dataSource } from "../../../data-source"; import { calendar } from "../../../entity/club/calendar"; import { calendarType } from "../../../entity/settings/calendarType"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; import InternalException from "../../../exceptions/internalException"; import { CreateCalendarCommand, DeleteCalendarCommand, UpdateCalendarCommand } from "./calendarCommand"; @@ -33,7 +34,7 @@ export default abstract class CalendarCommandHandler { return result.identifiers[0].id; }) .catch((err) => { - throw new InternalException("Failed creating calendar", err); + throw new DatabaseActionException("CREATE", "calendar", err); }); } @@ -72,7 +73,7 @@ export default abstract class CalendarCommandHandler { .execute() .then(() => {}) .catch((err) => { - throw new InternalException("Failed updating award", err); + throw new DatabaseActionException("UPDATE", "calendar", err); }); } @@ -90,7 +91,7 @@ export default abstract class CalendarCommandHandler { .execute() .then(() => {}) .catch((err) => { - throw new InternalException("Failed deleting calendar", err); + throw new DatabaseActionException("DELETE", "calendar", err); }); } } diff --git a/src/command/club/member/communicationCommandHandler.ts b/src/command/club/member/communicationCommandHandler.ts index 24402fc..3899394 100644 --- a/src/command/club/member/communicationCommandHandler.ts +++ b/src/command/club/member/communicationCommandHandler.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../../data-source"; import { communication } from "../../../entity/club/member/communication"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; import InternalException from "../../../exceptions/internalException"; import { CreateCommunicationCommand, @@ -54,7 +55,7 @@ export default abstract class CommunicationCommandHandler { return insertId; }) .catch((err) => { - throw new InternalException("Failed creating communication", err); + throw new DatabaseActionException("CREATE", "communication", err); }); } @@ -97,7 +98,7 @@ export default abstract class CommunicationCommandHandler { }) .then(() => {}) .catch((err) => { - throw new InternalException("Failed updating communication", err); + throw new DatabaseActionException("UPDATE", "communication", err); }); } @@ -116,7 +117,7 @@ export default abstract class CommunicationCommandHandler { .execute() .then(() => {}) .catch((err) => { - throw new InternalException("Failed deleting communication", err); + throw new DatabaseActionException("DELETE", "communication", err); }); } } diff --git a/src/command/club/member/memberAwardCommandHandler.ts b/src/command/club/member/memberAwardCommandHandler.ts index 0a02e01..53deccf 100644 --- a/src/command/club/member/memberAwardCommandHandler.ts +++ b/src/command/club/member/memberAwardCommandHandler.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../../data-source"; import { memberAwards } from "../../../entity/club/member/memberAwards"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; import InternalException from "../../../exceptions/internalException"; import { CreateMemberAwardCommand, DeleteMemberAwardCommand, UpdateMemberAwardCommand } from "./memberAwardCommand"; @@ -26,7 +27,7 @@ export default abstract class MemberAwardCommandHandler { return result.identifiers[0].id; }) .catch((err) => { - throw new InternalException("Failed creating memberAward", err); + throw new DatabaseActionException("CREATE", "memberAward", err); }); } @@ -50,7 +51,7 @@ export default abstract class MemberAwardCommandHandler { .execute() .then(() => {}) .catch((err) => { - throw new InternalException("Failed updating memberAward", err); + throw new DatabaseActionException("UPDATE", "memberAward", err); }); } @@ -69,7 +70,7 @@ export default abstract class MemberAwardCommandHandler { .execute() .then(() => {}) .catch((err) => { - throw new InternalException("Failed deleting memberAward", err); + throw new DatabaseActionException("DELETE", "memberAward", err); }); } } diff --git a/src/command/club/member/memberCommandHandler.ts b/src/command/club/member/memberCommandHandler.ts index 54862f7..78f1da0 100644 --- a/src/command/club/member/memberCommandHandler.ts +++ b/src/command/club/member/memberCommandHandler.ts @@ -1,6 +1,7 @@ import { dataSource } from "../../../data-source"; import { communication } from "../../../entity/club/member/communication"; import { member } from "../../../entity/club/member/member"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; import InternalException from "../../../exceptions/internalException"; import { CreateMemberCommand, DeleteMemberCommand, UpdateMemberCommand } from "./memberCommand"; @@ -28,10 +29,7 @@ export default abstract class MemberCommandHandler { return result.identifiers[0].id; }) .catch((err) => { - throw new InternalException( - `Failed creating member${err.code.includes("ER_DUP_ENTRY") ? " due to duplicate entry for column" : ""}`, - err - ); + throw new DatabaseActionException("CREATE", "member", err); }); } @@ -56,10 +54,7 @@ export default abstract class MemberCommandHandler { .execute() .then(() => {}) .catch((err) => { - throw new InternalException( - `Failed updating member${err.code.includes("ER_DUP_ENTRY") ? " due to duplicate entry for column" : ""}`, - err - ); + throw new DatabaseActionException("UPDATE", "member", err); }); } @@ -77,7 +72,7 @@ export default abstract class MemberCommandHandler { .execute() .then(() => {}) .catch((err) => { - throw new InternalException("Failed deleting member", err); + throw new DatabaseActionException("DELETE", "member", err); }); } } diff --git a/src/command/club/member/memberExecutivePositionCommandHandler.ts b/src/command/club/member/memberExecutivePositionCommandHandler.ts index f683541..95d5ab2 100644 --- a/src/command/club/member/memberExecutivePositionCommandHandler.ts +++ b/src/command/club/member/memberExecutivePositionCommandHandler.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../../data-source"; import { memberExecutivePositions } from "../../../entity/club/member/memberExecutivePositions"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; import InternalException from "../../../exceptions/internalException"; import { CreateMemberExecutivePositionCommand, @@ -30,7 +31,7 @@ export default abstract class MemberExecutivePositionCommandHandler { return result.identifiers[0].id; }) .catch((err) => { - throw new InternalException("Failed creating memberExecutivePosition", err); + throw new DatabaseActionException("CREATE", "memberExecutivePosition", err); }); } @@ -54,7 +55,7 @@ export default abstract class MemberExecutivePositionCommandHandler { .execute() .then(() => {}) .catch((err) => { - throw new InternalException("Failed updating memberExecutivePosition", err); + throw new DatabaseActionException("UPDATE", "memberExecutivePosition", err); }); } @@ -73,7 +74,7 @@ export default abstract class MemberExecutivePositionCommandHandler { .execute() .then(() => {}) .catch((err) => { - throw new InternalException("Failed deleting memberExecutivePosition", err); + throw new DatabaseActionException("DELETE", "memberExecutivePosition", err); }); } } diff --git a/src/command/club/member/memberQualificationCommandHandler.ts b/src/command/club/member/memberQualificationCommandHandler.ts index 351cfb6..7ec87e3 100644 --- a/src/command/club/member/memberQualificationCommandHandler.ts +++ b/src/command/club/member/memberQualificationCommandHandler.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../../data-source"; import { memberQualifications } from "../../../entity/club/member/memberQualifications"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; import InternalException from "../../../exceptions/internalException"; import { CreateMemberQualificationCommand, @@ -29,7 +30,7 @@ export default abstract class MemberQualificationCommandHandler { return result.identifiers[0].id; }) .catch((err) => { - throw new InternalException("Failed creating memberQualification", err); + throw new DatabaseActionException("CREATE", "memberQualification", err); }); } @@ -54,7 +55,7 @@ export default abstract class MemberQualificationCommandHandler { .execute() .then(() => {}) .catch((err) => { - throw new InternalException("Failed updating memberQualification", err); + throw new DatabaseActionException("UPDATE", "memberQualification", err); }); } @@ -73,7 +74,7 @@ export default abstract class MemberQualificationCommandHandler { .execute() .then(() => {}) .catch((err) => { - throw new InternalException("Failed deleting memberQualification", err); + throw new DatabaseActionException("DELETE", "memberQualification", err); }); } } diff --git a/src/command/club/member/membershipCommandHandler.ts b/src/command/club/member/membershipCommandHandler.ts index 0cd0e5a..52f3029 100644 --- a/src/command/club/member/membershipCommandHandler.ts +++ b/src/command/club/member/membershipCommandHandler.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../../data-source"; import { membership } from "../../../entity/club/member/membership"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; import InternalException from "../../../exceptions/internalException"; import { CreateMembershipCommand, DeleteMembershipCommand, UpdateMembershipCommand } from "./membershipCommand"; @@ -43,7 +44,7 @@ export default abstract class MembershipCommandHandler { return insertId; }) .catch((err) => { - throw new InternalException("Failed creating membership", err); + throw new DatabaseActionException("CREATE", "membership", err); }); } @@ -67,7 +68,7 @@ export default abstract class MembershipCommandHandler { .execute() .then(() => {}) .catch((err) => { - throw new InternalException("Failed updating membership", err); + throw new DatabaseActionException("UPDATE", "membership", err); }); } @@ -86,7 +87,7 @@ export default abstract class MembershipCommandHandler { .execute() .then(() => {}) .catch((err) => { - throw new InternalException("Failed deleting membership", err); + throw new DatabaseActionException("DELETE", "membership", err); }); } } diff --git a/src/command/club/newsletter/newsletterCommandHandler.ts b/src/command/club/newsletter/newsletterCommandHandler.ts index 38c156b..45eb281 100644 --- a/src/command/club/newsletter/newsletterCommandHandler.ts +++ b/src/command/club/newsletter/newsletterCommandHandler.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../../data-source"; import { newsletter } from "../../../entity/club/newsletter/newsletter"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; import InternalException from "../../../exceptions/internalException"; import { CreateNewsletterCommand, SendNewsletterCommand, SynchronizeNewsletterCommand } from "./newsletterCommand"; @@ -22,7 +23,7 @@ export default abstract class NewsletterCommandHandler { return result.identifiers[0].id; }) .catch((err) => { - throw new InternalException("Failed creating newsletter", err); + throw new DatabaseActionException("CREATE", "newsletter", err); }); } @@ -47,7 +48,7 @@ export default abstract class NewsletterCommandHandler { .execute() .then(() => {}) .catch((err) => { - throw new InternalException("Failed synching newsletter", err); + throw new DatabaseActionException("SYNC", "newsletter", err); }); } @@ -67,7 +68,7 @@ export default abstract class NewsletterCommandHandler { .execute() .then(() => {}) .catch((err) => { - throw new InternalException("Failed setting newsletter send state", err); + throw new DatabaseActionException("SET SEND STATE", "newsletter", err); }); } } diff --git a/src/command/club/newsletter/newsletterDatesCommandHandler.ts b/src/command/club/newsletter/newsletterDatesCommandHandler.ts index efaea75..2a11d43 100644 --- a/src/command/club/newsletter/newsletterDatesCommandHandler.ts +++ b/src/command/club/newsletter/newsletterDatesCommandHandler.ts @@ -4,6 +4,7 @@ import InternalException from "../../../exceptions/internalException"; import NewsletterDatesService from "../../../service/club/newsletter/newsletterDatesService"; import { NewsletterDateCommand, SynchronizeNewsletterDatesCommand } from "./newsletterDatesCommand"; import { newsletterDates } from "../../../entity/club/newsletter/newsletterDates"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; export default abstract class NewsletterDatesCommandHandler { /** @@ -42,7 +43,7 @@ export default abstract class NewsletterDatesCommandHandler { }) .then(() => {}) .catch((err) => { - throw new InternalException("Failed syncing newsletter dates", err); + throw new DatabaseActionException("SYNC", "newsletterDates", err); }); } diff --git a/src/command/club/newsletter/newsletterRecipientsCommandHandler.ts b/src/command/club/newsletter/newsletterRecipientsCommandHandler.ts index 2be82e0..76fa67d 100644 --- a/src/command/club/newsletter/newsletterRecipientsCommandHandler.ts +++ b/src/command/club/newsletter/newsletterRecipientsCommandHandler.ts @@ -4,6 +4,7 @@ import InternalException from "../../../exceptions/internalException"; import NewsletterRecipientsService from "../../../service/club/newsletter/newsletterRecipientsService"; import { SynchronizeNewsletterRecipientsCommand } from "./newsletterRecipientsCommand"; import { newsletterRecipients } from "../../../entity/club/newsletter/newsletterRecipients"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; export default abstract class NewsletterRecipientsCommandHandler { /** @@ -35,7 +36,7 @@ export default abstract class NewsletterRecipientsCommandHandler { }) .then(() => {}) .catch((err) => { - throw new InternalException("Failed syncing newsletter recipients", err); + throw new DatabaseActionException("SYNC", "newsletterRecipients", err); }); } diff --git a/src/command/club/protocol/protocolAgendaCommandHandler.ts b/src/command/club/protocol/protocolAgendaCommandHandler.ts index 0c74179..02fe4f2 100644 --- a/src/command/club/protocol/protocolAgendaCommandHandler.ts +++ b/src/command/club/protocol/protocolAgendaCommandHandler.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../../data-source"; import { protocolAgenda } from "../../../entity/club/protocol/protocolAgenda"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; import InternalException from "../../../exceptions/internalException"; import { SynchronizeProtocolAgendaCommand } from "./protocolAgendaCommand"; @@ -24,7 +25,7 @@ export default abstract class ProtocolAgendaCommandHandler { return result.identifiers[0].id; }) .catch((err) => { - throw new InternalException("Failed creating protocol", err); + throw new DatabaseActionException("CREATE", "protocolAgenda", err); }); } @@ -43,7 +44,7 @@ export default abstract class ProtocolAgendaCommandHandler { .execute() .then(() => {}) .catch((err) => { - throw new InternalException("Failed creating protocol", err); + throw new DatabaseActionException("SYNC", "protocolAgenda", err); }); } } diff --git a/src/command/club/protocol/protocolCommandHandler.ts b/src/command/club/protocol/protocolCommandHandler.ts index 95e5017..7174594 100644 --- a/src/command/club/protocol/protocolCommandHandler.ts +++ b/src/command/club/protocol/protocolCommandHandler.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../../data-source"; import { protocol } from "../../../entity/club/protocol/protocol"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; import InternalException from "../../../exceptions/internalException"; import { CreateProtocolCommand, SynchronizeProtocolCommand } from "./protocolCommand"; @@ -23,7 +24,7 @@ export default abstract class ProtocolCommandHandler { return result.identifiers[0].id; }) .catch((err) => { - throw new InternalException("Failed creating protocol", err); + throw new DatabaseActionException("CREATE", "protocol", err); }); } @@ -47,7 +48,7 @@ export default abstract class ProtocolCommandHandler { .execute() .then(() => {}) .catch((err) => { - throw new InternalException("Failed creating protocol", err); + throw new DatabaseActionException("SYNC", "protocol", err); }); } } diff --git a/src/command/club/protocol/protocolDecisionCommandHandler.ts b/src/command/club/protocol/protocolDecisionCommandHandler.ts index 19aba38..585abf1 100644 --- a/src/command/club/protocol/protocolDecisionCommandHandler.ts +++ b/src/command/club/protocol/protocolDecisionCommandHandler.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../../data-source"; import { protocolDecision } from "../../../entity/club/protocol/protocolDecision"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; import InternalException from "../../../exceptions/internalException"; import { SynchronizeProtocolDecisionCommand } from "./protocolDecisionCommand"; @@ -24,7 +25,7 @@ export default abstract class ProtocolDecisionCommandHandler { return result.identifiers[0].id; }) .catch((err) => { - throw new InternalException("Failed creating protocol", err); + throw new DatabaseActionException("CREATE", "protocolDecision", err); }); } /** @@ -42,7 +43,7 @@ export default abstract class ProtocolDecisionCommandHandler { .execute() .then(() => {}) .catch((err) => { - throw new InternalException("Failed creating protocol", err); + throw new DatabaseActionException("SYNC", "protocolDecision", err); }); } } diff --git a/src/command/club/protocol/protocolPresenceCommandHandler.ts b/src/command/club/protocol/protocolPresenceCommandHandler.ts index 8d48fe6..0bdc302 100644 --- a/src/command/club/protocol/protocolPresenceCommandHandler.ts +++ b/src/command/club/protocol/protocolPresenceCommandHandler.ts @@ -4,6 +4,7 @@ import { protocolPresence } from "../../../entity/club/protocol/protocolPresence import InternalException from "../../../exceptions/internalException"; import ProtocolPresenceService from "../../../service/club/protocol/protocolPrecenseService"; import { ProtocolPresenceCommand, SynchronizeProtocolPresenceCommand } from "./protocolPresenceCommand"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; export default abstract class ProtocolPresenceCommandHandler { /** @@ -42,7 +43,7 @@ export default abstract class ProtocolPresenceCommandHandler { }) .then(() => {}) .catch((err) => { - throw new InternalException("Failed saving protocol presence", err); + throw new DatabaseActionException("SYNC", "protocolSresence", err); }); } diff --git a/src/command/club/protocol/protocolPrintoutCommandHandler.ts b/src/command/club/protocol/protocolPrintoutCommandHandler.ts index 9ceabea..5ef3770 100644 --- a/src/command/club/protocol/protocolPrintoutCommandHandler.ts +++ b/src/command/club/protocol/protocolPrintoutCommandHandler.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../../data-source"; import { protocolPrintout } from "../../../entity/club/protocol/protocolPrintout"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; import InternalException from "../../../exceptions/internalException"; import { CreateProtocolPrintoutCommand } from "./protocolPrintoutCommand"; @@ -25,7 +26,7 @@ export default abstract class ProtocolPrintoutCommandHandler { return result.identifiers[0].id; }) .catch((err) => { - throw new InternalException("Failed creating protocol", err); + throw new DatabaseActionException("CREATE", "protocolPrintout", err); }); } } diff --git a/src/command/club/protocol/protocolVotingCommandHandler.ts b/src/command/club/protocol/protocolVotingCommandHandler.ts index 6b8834d..45a4642 100644 --- a/src/command/club/protocol/protocolVotingCommandHandler.ts +++ b/src/command/club/protocol/protocolVotingCommandHandler.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../../data-source"; import { protocolVoting } from "../../../entity/club/protocol/protocolVoting"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; import InternalException from "../../../exceptions/internalException"; import { SynchronizeProtocolVotingCommand } from "./protocolVotingCommand"; @@ -24,7 +25,7 @@ export default abstract class ProtocolVotingCommandHandler { return result.identifiers[0].id; }) .catch((err) => { - throw new InternalException("Failed creating protocol", err); + throw new DatabaseActionException("CREATE", "protocolVoting", err); }); } /** @@ -42,7 +43,7 @@ export default abstract class ProtocolVotingCommandHandler { .execute() .then(() => {}) .catch((err) => { - throw new InternalException("Failed creating protocol", err); + throw new DatabaseActionException("SYNC", "protocolVoting", err); }); } } diff --git a/src/command/refreshCommandHandler.ts b/src/command/refreshCommandHandler.ts index 709f51b..5796851 100644 --- a/src/command/refreshCommandHandler.ts +++ b/src/command/refreshCommandHandler.ts @@ -1,6 +1,7 @@ import { dataSource } from "../data-source"; import { refresh } from "../entity/refresh"; import { PWA_REFRESH_EXPIRATION, REFRESH_EXPIRATION } from "../env.defaults"; +import DatabaseActionException from "../exceptions/databaseActionException"; import InternalException from "../exceptions/internalException"; import { StringHelper } from "../helpers/stringHelper"; import UserService from "../service/user/userService"; @@ -32,7 +33,7 @@ export default abstract class RefreshCommandHandler { return refreshToken; }) .catch((err) => { - throw new InternalException("Failed saving refresh token", err); + throw new DatabaseActionException("CREATE", "refresh", err); }); } @@ -51,7 +52,7 @@ export default abstract class RefreshCommandHandler { .execute() .then((res) => {}) .catch((err) => { - throw new InternalException("failed refresh removal", err); + throw new DatabaseActionException("DELETE", "refresh", err); }); } @@ -68,7 +69,7 @@ export default abstract class RefreshCommandHandler { .execute() .then((res) => {}) .catch((err) => { - throw new InternalException("failed expired refresh removal", err); + throw new DatabaseActionException("DELETE", "refresh", err); }); } } diff --git a/src/command/resetCommandHandler.ts b/src/command/resetCommandHandler.ts index 9c75aba..8c7d06e 100644 --- a/src/command/resetCommandHandler.ts +++ b/src/command/resetCommandHandler.ts @@ -1,5 +1,6 @@ import { dataSource } from "../data-source"; import { reset } from "../entity/reset"; +import DatabaseActionException from "../exceptions/databaseActionException"; import InternalException from "../exceptions/internalException"; import { StringHelper } from "../helpers/stringHelper"; import { CreateResetCommand, DeleteResetCommand } from "./resetCommand"; @@ -29,7 +30,7 @@ export default abstract class ResetCommandHandler { return token; }) .catch((err) => { - throw new InternalException("Failed saving reset", err); + throw new DatabaseActionException("CREATE", "reset", err); }); } @@ -48,7 +49,7 @@ export default abstract class ResetCommandHandler { .execute() .then((res) => {}) .catch((err) => { - throw new InternalException("failed reset removal", err); + throw new DatabaseActionException("DELETE", "reset", err); }); } } diff --git a/src/command/settings/award/awardCommandHandler.ts b/src/command/settings/award/awardCommandHandler.ts index 2a525a9..bad5746 100644 --- a/src/command/settings/award/awardCommandHandler.ts +++ b/src/command/settings/award/awardCommandHandler.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../../data-source"; import { award } from "../../../entity/settings/award"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; import InternalException from "../../../exceptions/internalException"; import { CreateAwardCommand, DeleteAwardCommand, UpdateAwardCommand } from "./awardCommand"; @@ -22,7 +23,7 @@ export default abstract class AwardCommandHandler { return result.identifiers[0].id; }) .catch((err) => { - throw new InternalException("Failed creating award", err); + throw new DatabaseActionException("CREATE", "award", err); }); } @@ -42,7 +43,7 @@ export default abstract class AwardCommandHandler { .execute() .then(() => {}) .catch((err) => { - throw new InternalException("Failed updating award", err); + throw new DatabaseActionException("UPDATE", "award", err); }); } @@ -60,10 +61,7 @@ export default abstract class AwardCommandHandler { .execute() .then(() => {}) .catch((err) => { - throw new InternalException( - `Failed deleting award ${err.code.includes("ER_ROW_IS_REFERENCED") ? " due to referenced data" : ""}`, - err - ); + throw new DatabaseActionException("DELETE", "award", err); }); } } diff --git a/src/command/settings/calendarType/calendarTypeCommandHandler.ts b/src/command/settings/calendarType/calendarTypeCommandHandler.ts index 702728d..7de554c 100644 --- a/src/command/settings/calendarType/calendarTypeCommandHandler.ts +++ b/src/command/settings/calendarType/calendarTypeCommandHandler.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../../data-source"; import { calendarType } from "../../../entity/settings/calendarType"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; import InternalException from "../../../exceptions/internalException"; import { CreateCalendarTypeCommand, DeleteCalendarTypeCommand, UpdateCalendarTypeCommand } from "./calendarTypeCommand"; @@ -25,7 +26,7 @@ export default abstract class CalendarTypeCommandHandler { return result.identifiers[0].id; }) .catch((err) => { - throw new InternalException("Failed creating calendarType", err); + throw new DatabaseActionException("CREATE", "calendarType", err); }); } @@ -48,7 +49,7 @@ export default abstract class CalendarTypeCommandHandler { .execute() .then(() => {}) .catch((err) => { - throw new InternalException("Failed updating award", err); + throw new DatabaseActionException("UPDATE", "calenarType", err); }); } @@ -66,10 +67,7 @@ export default abstract class CalendarTypeCommandHandler { .execute() .then(() => {}) .catch((err) => { - throw new InternalException( - `Failed deleting calendarType${err.code.includes("ER_ROW_IS_REFERENCED") ? " due to referenced data" : ""}`, - err - ); + throw new DatabaseActionException("DELETE", "calendarType", err); }); } } diff --git a/src/command/settings/communicationType/communicationTypeCommandHandler.ts b/src/command/settings/communicationType/communicationTypeCommandHandler.ts index df11e20..f05b0ff 100644 --- a/src/command/settings/communicationType/communicationTypeCommandHandler.ts +++ b/src/command/settings/communicationType/communicationTypeCommandHandler.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../../data-source"; import { communicationType } from "../../../entity/settings/communicationType"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; import InternalException from "../../../exceptions/internalException"; import { CreateCommunicationTypeCommand, @@ -27,7 +28,7 @@ export default abstract class CommunicationTypeCommandHandler { return result.identifiers[0].id; }) .catch((err) => { - throw new InternalException("Failed creating communicationType", err); + throw new DatabaseActionException("CREATE", "communicationType", err); }); } @@ -48,7 +49,7 @@ export default abstract class CommunicationTypeCommandHandler { .execute() .then(() => {}) .catch((err) => { - throw new InternalException("Failed updating communicationType", err); + throw new DatabaseActionException("UPDATE", "communicationType", err); }); } @@ -66,12 +67,7 @@ export default abstract class CommunicationTypeCommandHandler { .execute() .then(() => {}) .catch((err) => { - throw new InternalException( - `Failed deleting communicationType${ - err.code.includes("ER_ROW_IS_REFERENCED") ? " due to referenced data" : "" - }`, - err - ); + throw new DatabaseActionException("DELETE", "communicationType", err); }); } } diff --git a/src/command/settings/executivePosition/executivePositionCommandHandler.ts b/src/command/settings/executivePosition/executivePositionCommandHandler.ts index 14a31a5..d2fb77e 100644 --- a/src/command/settings/executivePosition/executivePositionCommandHandler.ts +++ b/src/command/settings/executivePosition/executivePositionCommandHandler.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../../data-source"; import { executivePosition } from "../../../entity/settings/executivePosition"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; import InternalException from "../../../exceptions/internalException"; import { CreateExecutivePositionCommand, @@ -26,7 +27,7 @@ export default abstract class ExecutivePositionCommandHandler { return result.identifiers[0].id; }) .catch((err) => { - throw new InternalException("Failed creating executivePosition", err); + throw new DatabaseActionException("DELETE", "executivePosition", err); }); } @@ -46,7 +47,7 @@ export default abstract class ExecutivePositionCommandHandler { .execute() .then(() => {}) .catch((err) => { - throw new InternalException("Failed updating executivePosition", err); + throw new DatabaseActionException("UPDATE", "executivePosition", err); }); } @@ -64,12 +65,7 @@ export default abstract class ExecutivePositionCommandHandler { .execute() .then(() => {}) .catch((err) => { - throw new InternalException( - `Failed deleting executivePosition${ - err.code.includes("ER_ROW_IS_REFERENCED") ? " due to referenced data" : "" - }`, - err - ); + throw new DatabaseActionException("DELETE", "executivePosition", err); }); } } diff --git a/src/command/settings/membershipStatus/membershipStatusCommandHandler.ts b/src/command/settings/membershipStatus/membershipStatusCommandHandler.ts index 7e5e953..e1cff7f 100644 --- a/src/command/settings/membershipStatus/membershipStatusCommandHandler.ts +++ b/src/command/settings/membershipStatus/membershipStatusCommandHandler.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../../data-source"; import { membershipStatus } from "../../../entity/settings/membershipStatus"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; import InternalException from "../../../exceptions/internalException"; import { CreateMembershipStatusCommand, @@ -26,7 +27,7 @@ export default abstract class MembershipStatusCommandHandler { return result.identifiers[0].id; }) .catch((err) => { - throw new InternalException("Failed creating membershipStatus", err); + throw new DatabaseActionException("CREATING", "membershipStatus", err); }); } @@ -46,7 +47,7 @@ export default abstract class MembershipStatusCommandHandler { .execute() .then(() => {}) .catch((err) => { - throw new InternalException("Failed updating membershipStatus", err); + throw new DatabaseActionException("UPDATE", "membershipStatus", err); }); } @@ -64,12 +65,7 @@ export default abstract class MembershipStatusCommandHandler { .execute() .then(() => {}) .catch((err) => { - throw new InternalException( - `Failed deleting membershipStatus${ - err.code.includes("ER_ROW_IS_REFERENCED") ? " due to referenced data" : "" - }`, - err - ); + throw new DatabaseActionException("DELETE", "membershipStatus", err); }); } } diff --git a/src/command/settings/newsletterConfig/newsletterConfigCommandHandler.ts b/src/command/settings/newsletterConfig/newsletterConfigCommandHandler.ts index 4f26df4..98d8e02 100644 --- a/src/command/settings/newsletterConfig/newsletterConfigCommandHandler.ts +++ b/src/command/settings/newsletterConfig/newsletterConfigCommandHandler.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../../data-source"; import { newsletterConfig } from "../../../entity/settings/newsletterConfig"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; import InternalException from "../../../exceptions/internalException"; import { SetNewsletterConfigCommand } from "./newsletterConfigCommand"; @@ -24,7 +25,7 @@ export default abstract class NewsletterConfigCommandHandler { return result.identifiers[0].id; }) .catch((err) => { - throw new InternalException("Failed setting newsletterConfig", err); + throw new DatabaseActionException("SET", "newsletterConfig", err); }); } } diff --git a/src/command/settings/qualification/qualificationCommandHandler.ts b/src/command/settings/qualification/qualificationCommandHandler.ts index b0b9f22..31480f2 100644 --- a/src/command/settings/qualification/qualificationCommandHandler.ts +++ b/src/command/settings/qualification/qualificationCommandHandler.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../../data-source"; import { qualification } from "../../../entity/settings/qualification"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; import InternalException from "../../../exceptions/internalException"; import { CreateQualificationCommand, @@ -27,7 +28,7 @@ export default abstract class QualificationCommandHandler { return result.identifiers[0].id; }) .catch((err) => { - throw new InternalException("Failed creating qualification", err); + throw new DatabaseActionException("CREATE", "qualification", err); }); } @@ -48,7 +49,7 @@ export default abstract class QualificationCommandHandler { .execute() .then(() => {}) .catch((err) => { - throw new InternalException("Failed updating qualification", err); + throw new DatabaseActionException("UPDATE", "qualification", err); }); } @@ -66,10 +67,7 @@ export default abstract class QualificationCommandHandler { .execute() .then(() => {}) .catch((err) => { - throw new InternalException( - `Failed deleting qualification${err.code.includes("ER_ROW_IS_REFERENCED") ? " due to referenced data" : ""}`, - err - ); + throw new DatabaseActionException("DELETE", "qualification", err); }); } } diff --git a/src/command/settings/queryStore/queryStoreCommandHandler.ts b/src/command/settings/queryStore/queryStoreCommandHandler.ts index e8c6917..c33cadc 100644 --- a/src/command/settings/queryStore/queryStoreCommandHandler.ts +++ b/src/command/settings/queryStore/queryStoreCommandHandler.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../../data-source"; import { query } from "../../../entity/settings/query"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; import InternalException from "../../../exceptions/internalException"; import { CreateQueryStoreCommand, DeleteQueryStoreCommand, UpdateQueryStoreCommand } from "./queryStoreCommand"; @@ -24,7 +25,7 @@ export default abstract class QueryStoreCommandHandler { return result.identifiers[0].id; }) .catch((err) => { - throw new InternalException("Failed creating queryStore", err); + throw new DatabaseActionException("CREATE", "queryStore", err); }); } @@ -45,7 +46,7 @@ export default abstract class QueryStoreCommandHandler { .execute() .then(() => {}) .catch((err) => { - throw new InternalException("Failed updating queryStore", err); + throw new DatabaseActionException("UPDATE", "queryStore", err); }); } @@ -63,10 +64,7 @@ export default abstract class QueryStoreCommandHandler { .execute() .then(() => {}) .catch((err) => { - throw new InternalException( - `Failed deleting queryStore${err.code.includes("ER_ROW_IS_REFERENCED") ? " due to referenced data" : ""}`, - err - ); + throw new DatabaseActionException("DELETE", "queryStore", err); }); } } diff --git a/src/command/settings/salutation/salutationCommandHandler.ts b/src/command/settings/salutation/salutationCommandHandler.ts index 78f7c69..70b24c2 100644 --- a/src/command/settings/salutation/salutationCommandHandler.ts +++ b/src/command/settings/salutation/salutationCommandHandler.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../../data-source"; import { salutation } from "../../../entity/settings/salutation"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; import InternalException from "../../../exceptions/internalException"; import { CreateSalutationCommand, DeleteSalutationCommand, UpdateSalutationCommand } from "./salutationCommand"; @@ -22,7 +23,7 @@ export default abstract class SalutationCommandHandler { return result.identifiers[0].id; }) .catch((err) => { - throw new InternalException("Failed creating salutation", err); + throw new DatabaseActionException("CREATE", "salutation", err); }); } @@ -42,7 +43,7 @@ export default abstract class SalutationCommandHandler { .execute() .then(() => {}) .catch((err) => { - throw new InternalException("Failed updating salutation", err); + throw new DatabaseActionException("UPDATE", "salutation", err); }); } @@ -60,10 +61,7 @@ export default abstract class SalutationCommandHandler { .execute() .then(() => {}) .catch((err) => { - throw new InternalException( - `Failed deleting salutation ${err.code.includes("ER_ROW_IS_REFERENCED") ? " due to referenced data" : ""}`, - err - ); + throw new DatabaseActionException("DELETE", "salutation", err); }); } } diff --git a/src/command/settings/template/templateCommandHandler.ts b/src/command/settings/template/templateCommandHandler.ts index 6a88353..ebb2df7 100644 --- a/src/command/settings/template/templateCommandHandler.ts +++ b/src/command/settings/template/templateCommandHandler.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../../data-source"; import { template } from "../../../entity/settings/template"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; import InternalException from "../../../exceptions/internalException"; import { CreateTemplateCommand, DeleteTemplateCommand, UpdateTemplateCommand } from "./templateCommand"; @@ -23,7 +24,7 @@ export default abstract class TemplateCommandHandler { return result.identifiers[0].id; }) .catch((err) => { - throw new InternalException("Failed creating template", err); + throw new DatabaseActionException("CREATE", "template", err); }); } @@ -46,7 +47,7 @@ export default abstract class TemplateCommandHandler { .execute() .then(() => {}) .catch((err) => { - throw new InternalException("Failed updating template", err); + throw new DatabaseActionException("UPDATE", "template", err); }); } @@ -64,10 +65,7 @@ export default abstract class TemplateCommandHandler { .execute() .then(() => {}) .catch((err) => { - throw new InternalException( - `Failed deleting template${err.code.includes("ER_ROW_IS_REFERENCED") ? " due to referenced data" : ""}`, - err - ); + throw new DatabaseActionException("DELETE", "template", err); }); } } diff --git a/src/command/settings/templateUsage/templateUsageCommandHandler.ts b/src/command/settings/templateUsage/templateUsageCommandHandler.ts index 06c46c9..dc75944 100644 --- a/src/command/settings/templateUsage/templateUsageCommandHandler.ts +++ b/src/command/settings/templateUsage/templateUsageCommandHandler.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../../data-source"; import { templateUsage } from "../../../entity/settings/templateUsage"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; import InternalException from "../../../exceptions/internalException"; import { UpdateTemplateUsageCommand } from "./templateUsageCommand"; @@ -24,7 +25,7 @@ export default abstract class TemplateUsageCommandHandler { .execute() .then(() => {}) .catch((err) => { - throw new InternalException("Failed updating templateUsage", err); + throw new DatabaseActionException("SET", "templateUsage", err); }); } } diff --git a/src/command/user/role/roleCommandHandler.ts b/src/command/user/role/roleCommandHandler.ts index 739d8b9..40f9a8f 100644 --- a/src/command/user/role/roleCommandHandler.ts +++ b/src/command/user/role/roleCommandHandler.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../../data-source"; import { role } from "../../../entity/user/role"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; import InternalException from "../../../exceptions/internalException"; import { CreateRoleCommand, DeleteRoleCommand, UpdateRoleCommand } from "./roleCommand"; @@ -22,7 +23,7 @@ export default abstract class RoleCommandHandler { return result.identifiers[0].id; }) .catch((err) => { - throw new InternalException("Failed creating role", err); + throw new DatabaseActionException("CREATE", "role", err); }); } @@ -42,7 +43,7 @@ export default abstract class RoleCommandHandler { .execute() .then(() => {}) .catch((err) => { - throw new InternalException("Failed updating role", err); + throw new DatabaseActionException("UPDATE", "role", err); }); } @@ -60,7 +61,7 @@ export default abstract class RoleCommandHandler { .execute() .then(() => {}) .catch((err) => { - throw new InternalException("Failed deleting role", err); + throw new DatabaseActionException("DELETE", "role", err); }); } } diff --git a/src/command/user/role/rolePermissionCommandHandler.ts b/src/command/user/role/rolePermissionCommandHandler.ts index a9a0b1f..21a98d4 100644 --- a/src/command/user/role/rolePermissionCommandHandler.ts +++ b/src/command/user/role/rolePermissionCommandHandler.ts @@ -11,6 +11,7 @@ import { import PermissionHelper from "../../../helpers/permissionHelper"; import RolePermissionService from "../../../service/user/rolePermissionService"; import { PermissionString } from "../../../type/permissionTypes"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; export default abstract class RolePermissionCommandHandler { /** @@ -35,7 +36,7 @@ export default abstract class RolePermissionCommandHandler { }) .then(() => {}) .catch((err) => { - throw new InternalException("Failed saving role permissions", err); + throw new DatabaseActionException("UPDATE", "rolePermissions", err); }); } @@ -71,46 +72,4 @@ export default abstract class RolePermissionCommandHandler { .andWhere("permission IN (:...permission)", { permission: permissions }) .execute(); } - - /** - * @description grant permission to user - * @param {CreateRolePermissionCommand} createPermission - * @returns {Promise} - */ - static async create(createPermission: CreateRolePermissionCommand): Promise { - return await dataSource - .createQueryBuilder() - .insert() - .into(rolePermission) - .values({ - permission: createPermission.permission, - role: await RoleService.getById(createPermission.roleId), - }) - .execute() - .then((result) => { - return result.identifiers[0].id; - }) - .catch((err) => { - throw new InternalException("Failed saving role permission", err); - }); - } - - /** - * @description remove permission from role - * @param {DeleteRolePermissionCommand} deletePermission - * @returns {Promise} - */ - static async delete(deletePermission: DeleteRolePermissionCommand): Promise { - return await dataSource - .createQueryBuilder() - .delete() - .from(rolePermission) - .where("roleId = :id", { id: deletePermission.roleId }) - .andWhere("permission = :permission", { permission: deletePermission.permission }) - .execute() - .then(() => {}) - .catch((err) => { - throw new InternalException("failed role permission removal", err); - }); - } } diff --git a/src/command/user/user/inviteCommandHandler.ts b/src/command/user/user/inviteCommandHandler.ts index 61c6149..fd760d4 100644 --- a/src/command/user/user/inviteCommandHandler.ts +++ b/src/command/user/user/inviteCommandHandler.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../../data-source"; import { invite } from "../../../entity/user/invite"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; import InternalException from "../../../exceptions/internalException"; import { StringHelper } from "../../../helpers/stringHelper"; import { CreateInviteCommand, DeleteInviteCommand } from "./inviteCommand"; @@ -31,7 +32,7 @@ export default abstract class InviteCommandHandler { return token; }) .catch((err) => { - throw new InternalException("Failed saving invite", err); + throw new DatabaseActionException("CREATE", "invite", err); }); } @@ -50,7 +51,7 @@ export default abstract class InviteCommandHandler { .execute() .then((res) => {}) .catch((err) => { - throw new InternalException("failed invite removal", err); + throw new DatabaseActionException("DELETE", "invite", err); }); } @@ -68,7 +69,7 @@ export default abstract class InviteCommandHandler { .execute() .then((res) => {}) .catch((err) => { - throw new InternalException("failed invite removal", err); + throw new DatabaseActionException("DELETE", "invite", err); }); } } diff --git a/src/command/user/user/userCommandHandler.ts b/src/command/user/user/userCommandHandler.ts index 50afb2a..0f1102a 100644 --- a/src/command/user/user/userCommandHandler.ts +++ b/src/command/user/user/userCommandHandler.ts @@ -11,6 +11,7 @@ import { UpdateUserSecretCommand, } from "./userCommand"; import UserService from "../../../service/user/userService"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; export default abstract class UserCommandHandler { /** @@ -36,7 +37,7 @@ export default abstract class UserCommandHandler { return result.identifiers[0].id; }) .catch((err) => { - throw new InternalException("Failed saving user", err); + throw new DatabaseActionException("CREATE", "user", err); }); } @@ -59,7 +60,7 @@ export default abstract class UserCommandHandler { .execute() .then(() => {}) .catch((err) => { - throw new InternalException("Failed updating user", err); + throw new DatabaseActionException("UPDATE", "user", err); }); } @@ -79,7 +80,7 @@ export default abstract class UserCommandHandler { .execute() .then(() => {}) .catch((err) => { - throw new InternalException("Failed updating user secret", err); + throw new DatabaseActionException("UPDATE", "user", err); }); } @@ -105,7 +106,7 @@ export default abstract class UserCommandHandler { }) .then(() => {}) .catch((err) => { - throw new InternalException("Failed saving user roles", err); + throw new DatabaseActionException("UPDATE", "userRoles", err); }); } @@ -145,7 +146,7 @@ export default abstract class UserCommandHandler { }) .then(() => {}) .catch((err) => { - throw new InternalException("Failed transfering ownership", err); + throw new DatabaseActionException("ABORT", "transfer owner", err); }); } @@ -163,7 +164,7 @@ export default abstract class UserCommandHandler { .execute() .then(() => {}) .catch((err) => { - throw new InternalException("Failed deleting user", err); + throw new DatabaseActionException("DELETE", "user", err); }); } } diff --git a/src/command/user/user/userPermissionCommandHandler.ts b/src/command/user/user/userPermissionCommandHandler.ts index bd6ffc3..77694f3 100644 --- a/src/command/user/user/userPermissionCommandHandler.ts +++ b/src/command/user/user/userPermissionCommandHandler.ts @@ -11,6 +11,7 @@ import { import UserPermissionService from "../../../service/user/userPermissionService"; import PermissionHelper from "../../../helpers/permissionHelper"; import { PermissionString } from "../../../type/permissionTypes"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; export default abstract class UserPermissionCommandHandler { /** @@ -36,7 +37,7 @@ export default abstract class UserPermissionCommandHandler { }) .then(() => {}) .catch((err) => { - throw new InternalException("Failed saving user permissions", err); + throw new DatabaseActionException("UPDATE", "userPermissions", err); }); } @@ -72,46 +73,4 @@ export default abstract class UserPermissionCommandHandler { .andWhere("permission IN (:...permission)", { permission: permissions }) .execute(); } - - /** - * @description grant permission to user - * @param {CreateUserPermissionCommand} createPermission - * @returns {Promise} - */ - static async create(createPermission: CreateUserPermissionCommand): Promise { - return await dataSource - .createQueryBuilder() - .insert() - .into(userPermission) - .values({ - permission: createPermission.permission, - userId: createPermission.userId, - }) - .execute() - .then((result) => { - return result.identifiers[0].id; - }) - .catch((err) => { - throw new InternalException("Failed saving user permission", err); - }); - } - - /** - * @description remove permission to user - * @param {DeleteUserPermissionCommand} deletePermission - * @returns {Promise} - */ - static async delete(deletePermission: DeleteUserPermissionCommand): Promise { - return await dataSource - .createQueryBuilder() - .delete() - .from(userPermission) - .where("userId = :id", { id: deletePermission.userId }) - .andWhere("permission = :permission", { permission: deletePermission.permission }) - .execute() - .then((res) => {}) - .catch((err) => { - throw new InternalException("failed user permission removal", err); - }); - } } diff --git a/src/command/user/webapi/webapiCommandHandler.ts b/src/command/user/webapi/webapiCommandHandler.ts index b49f7a6..ea12d98 100644 --- a/src/command/user/webapi/webapiCommandHandler.ts +++ b/src/command/user/webapi/webapiCommandHandler.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../../data-source"; import { webapi } from "../../../entity/user/webapi"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; import InternalException from "../../../exceptions/internalException"; import { CreateWebapiCommand, @@ -29,10 +30,7 @@ export default abstract class WebapiCommandHandler { return result.identifiers[0].token; }) .catch((err) => { - throw new InternalException( - `Failed creating api${err.code.includes("ER_DUP_ENTRY") ? " due to duplicate entry for column" : ""}`, - err - ); + throw new DatabaseActionException("CREATE", "webapi", err); }); } @@ -53,10 +51,7 @@ export default abstract class WebapiCommandHandler { .execute() .then(() => {}) .catch((err) => { - throw new InternalException( - `Failed updating api${err.code.includes("ER_DUP_ENTRY") ? " due to duplicate entry for column" : ""}`, - err - ); + throw new DatabaseActionException("UPDATE", "webapi", err); }); } @@ -76,7 +71,7 @@ export default abstract class WebapiCommandHandler { .execute() .then(() => {}) .catch((err) => { - throw new InternalException(`Failed updating api last usage`, err); + throw new DatabaseActionException("UPDATE", "webapi", err); }); } @@ -94,7 +89,7 @@ export default abstract class WebapiCommandHandler { .execute() .then(() => {}) .catch((err) => { - throw new InternalException("Failed deleting api", err); + throw new DatabaseActionException("DELETE", "webapi", err); }); } } diff --git a/src/command/user/webapi/webapiPermissionCommandHandler.ts b/src/command/user/webapi/webapiPermissionCommandHandler.ts index 74ea514..ec661f4 100644 --- a/src/command/user/webapi/webapiPermissionCommandHandler.ts +++ b/src/command/user/webapi/webapiPermissionCommandHandler.ts @@ -11,6 +11,7 @@ import { import PermissionHelper from "../../../helpers/permissionHelper"; import WebapiPermissionService from "../../../service/user/webapiPermissionService"; import { PermissionString } from "../../../type/permissionTypes"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; export default abstract class WebapiPermissionCommandHandler { /** @@ -38,7 +39,7 @@ export default abstract class WebapiPermissionCommandHandler { }) .then(() => {}) .catch((err) => { - throw new InternalException("Failed saving api permissions", err); + throw new DatabaseActionException("UPDATE", "webapiPermission", err); }); } @@ -74,46 +75,4 @@ export default abstract class WebapiPermissionCommandHandler { .andWhere("permission IN (:...permission)", { permission: permissions }) .execute(); } - - /** - * @description grant permission to user - * @param {CreateWebapiPermissionCommand} createPermission - * @returns {Promise} - */ - static async create(createPermission: CreateWebapiPermissionCommand): Promise { - return await dataSource - .createQueryBuilder() - .insert() - .into(webapiPermission) - .values({ - permission: createPermission.permission, - webapiId: createPermission.webapiId, - }) - .execute() - .then((result) => { - return result.identifiers[0].id; - }) - .catch((err) => { - throw new InternalException("Failed saving api permission", err); - }); - } - - /** - * @description remove permission from api - * @param {DeleteWebapiPermissionCommand} deletePermission - * @returns {Promise} - */ - static async delete(deletePermission: DeleteWebapiPermissionCommand): Promise { - return await dataSource - .createQueryBuilder() - .delete() - .from(webapiPermission) - .where("webapiId = :id", { id: deletePermission.webapiId }) - .andWhere("permission = :permission", { permission: deletePermission.permission }) - .execute() - .then(() => {}) - .catch((err) => { - throw new InternalException("failed api permission removal", err); - }); - } } diff --git a/src/exceptions/databaseActionException.ts b/src/exceptions/databaseActionException.ts new file mode 100644 index 0000000..b0b145c --- /dev/null +++ b/src/exceptions/databaseActionException.ts @@ -0,0 +1,8 @@ +import CustomRequestException from "./customRequestException"; + +export default class DatabaseActionException extends CustomRequestException { + constructor(action: string, table: string, err: any) { + let errstring = `${action} on ${table} with ${err?.code ?? "XX"} at ${err?.sqlMessage ?? "XX"}`; + super(500, errstring, err); + } +} diff --git a/src/service/club/calendarService.ts b/src/service/club/calendarService.ts index e1186a1..7e8f86b 100644 --- a/src/service/club/calendarService.ts +++ b/src/service/club/calendarService.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../data-source"; import { calendar } from "../../entity/club/calendar"; +import DatabaseActionException from "../../exceptions/databaseActionException"; import InternalException from "../../exceptions/internalException"; export default abstract class CalendarService { @@ -18,7 +19,7 @@ export default abstract class CalendarService { return res; }) .catch((err) => { - throw new InternalException("calendars not found", err); + throw new DatabaseActionException("SELECT", "calendar", err); }); } @@ -37,7 +38,7 @@ export default abstract class CalendarService { return res; }) .catch((err) => { - throw new InternalException("calendar not found by id", err); + throw new DatabaseActionException("SELECT", "calendar", err); }); } @@ -62,7 +63,7 @@ export default abstract class CalendarService { return res; }) .catch((err) => { - throw new InternalException("calendars not found by types", err); + throw new DatabaseActionException("SELECT", "calendar", err); }); } @@ -81,7 +82,7 @@ export default abstract class CalendarService { return res; }) .catch((err) => { - throw new InternalException("calendars not found by type nscdr", err); + throw new DatabaseActionException("SELECT", "calendar", err); }); } } diff --git a/src/service/club/member/communicationService.ts b/src/service/club/member/communicationService.ts index 2a016d7..8a33b41 100644 --- a/src/service/club/member/communicationService.ts +++ b/src/service/club/member/communicationService.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../../data-source"; import { communication } from "../../../entity/club/member/communication"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; import InternalException from "../../../exceptions/internalException"; export default abstract class CommunicationService { @@ -21,7 +22,7 @@ export default abstract class CommunicationService { return res; }) .catch((err) => { - throw new InternalException("member communications not found", err); + throw new DatabaseActionException("SELECT", "memberCommunications", err); }); } @@ -44,7 +45,7 @@ export default abstract class CommunicationService { return res; }) .catch((err) => { - throw new InternalException("member communication not found by id", err); + throw new DatabaseActionException("SELECT", "memberCommunication", err); }); } diff --git a/src/service/club/member/memberAwardService.ts b/src/service/club/member/memberAwardService.ts index 8e3aa8e..45e71c0 100644 --- a/src/service/club/member/memberAwardService.ts +++ b/src/service/club/member/memberAwardService.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../../data-source"; import { memberAwards } from "../../../entity/club/member/memberAwards"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; import InternalException from "../../../exceptions/internalException"; export default abstract class MemberAwardService { @@ -20,7 +21,7 @@ export default abstract class MemberAwardService { return res; }) .catch((err) => { - throw new InternalException("member awards not found", err); + throw new DatabaseActionException("SELECT", "memberAwards", err); }); } @@ -42,7 +43,7 @@ export default abstract class MemberAwardService { return res; }) .catch((err) => { - throw new InternalException("member award not found by id", err); + throw new DatabaseActionException("SELECT", "memberAwards", err); }); } } diff --git a/src/service/club/member/memberExecutivePositionService.ts b/src/service/club/member/memberExecutivePositionService.ts index 93d76eb..f853b50 100644 --- a/src/service/club/member/memberExecutivePositionService.ts +++ b/src/service/club/member/memberExecutivePositionService.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../../data-source"; import { memberExecutivePositions } from "../../../entity/club/member/memberExecutivePositions"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; import InternalException from "../../../exceptions/internalException"; export default abstract class MemberExecutivePositionService { @@ -21,7 +22,7 @@ export default abstract class MemberExecutivePositionService { return res; }) .catch((err) => { - throw new InternalException("member executivePositions not found", err); + throw new DatabaseActionException("SELECT", "memberExecutivePositions", err); }); } @@ -45,7 +46,7 @@ export default abstract class MemberExecutivePositionService { return res; }) .catch((err) => { - throw new InternalException("member executivePosition not found by id", err); + throw new DatabaseActionException("SELECT", "memberExecutivePositions", err); }); } } diff --git a/src/service/club/member/memberQualificationService.ts b/src/service/club/member/memberQualificationService.ts index afd3ee5..5a2e11c 100644 --- a/src/service/club/member/memberQualificationService.ts +++ b/src/service/club/member/memberQualificationService.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../../data-source"; import { memberQualifications } from "../../../entity/club/member/memberQualifications"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; import InternalException from "../../../exceptions/internalException"; export default abstract class MemberQualificationService { @@ -20,7 +21,7 @@ export default abstract class MemberQualificationService { return res; }) .catch((err) => { - throw new InternalException("member qualifications not found", err); + throw new DatabaseActionException("SELECT", "memberQualifications", err); }); } @@ -42,7 +43,7 @@ export default abstract class MemberQualificationService { return res; }) .catch((err) => { - throw new InternalException("member qualification not found by id", err); + throw new DatabaseActionException("SELECT", "memberQualifications", err); }); } } diff --git a/src/service/club/member/memberService.ts b/src/service/club/member/memberService.ts index 5ec6d09..f970a9f 100644 --- a/src/service/club/member/memberService.ts +++ b/src/service/club/member/memberService.ts @@ -1,6 +1,8 @@ +import { SelectQueryBuilder } from "typeorm"; import { dataSource } from "../../../data-source"; import { member } from "../../../entity/club/member/member"; import { membership } from "../../../entity/club/member/membership"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; import InternalException from "../../../exceptions/internalException"; import { memberView } from "../../../views/memberView"; @@ -22,40 +24,7 @@ export default abstract class MemberService { noLimit?: boolean; ids?: Array; }): Promise<[Array, number]> { - let query = dataSource - .getRepository(member) - .createQueryBuilder("member") - .leftJoinAndMapOne( - "member.firstMembershipEntry", - "member.memberships", - "membership_first", - "membership_first.memberId = member.id AND membership_first.start = (SELECT MIN(m.start) FROM membership m WHERE m.memberId = member.id)" - ) - .leftJoinAndMapOne( - "member.lastMembershipEntry", - "member.memberships", - "membership_last", - "membership_last.memberId = member.id AND membership_last.start = (SELECT MAX(m.start) FROM membership m WHERE m.memberId = member.id)" - ) - .leftJoinAndSelect("membership_first.status", "status_first") - .leftJoinAndSelect("membership_last.status", "status_last") - .leftJoinAndMapMany( - "member.preferredCommunication", - "member.communications", - "preferredCommunication", - "preferredCommunication.preferred = 1" - ) - .leftJoinAndSelect("preferredCommunication.type", "communicationtype_preferred") - .leftJoinAndMapOne( - "member.sendNewsletter", - "member.communications", - "sendNewsletter", - "sendNewsletter.isSendNewsletter = 1" - ) - .leftJoinAndSelect("sendNewsletter.type", "communicationtype") - .leftJoinAndMapMany("member.smsAlarming", "member.communications", "smsAlarming", "smsAlarming.isSMSAlarming = 1") - .leftJoinAndSelect("smsAlarming.type", "communicationtype_smsAlarming") - .leftJoinAndSelect("member.salutation", "salutation"); + let query = this.applyMemberBaseJoins(); if (search != "") { search.split(" ").forEach((term, index) => { @@ -90,7 +59,7 @@ export default abstract class MemberService { return res; }) .catch((err) => { - throw new InternalException("members not found", err); + throw new DatabaseActionException("SELECT", "member", err); }); } @@ -100,7 +69,89 @@ export default abstract class MemberService { * @returns {Promise} */ static async getById(id: string): Promise { + return this.applyMemberBaseJoins() + .where("member.id = :id", { id: id }) + .getOneOrFail() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "member", err); + }); + } + + /** + * @description get member statistics by id + * @param {string} id + * @returns {Promise} + */ + static async getStatisticsById(id: string): Promise { return await dataSource + .getRepository(memberView) + .createQueryBuilder("memberView") + .where("memberView.id = :id", { id: id }) + .getOneOrFail() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "memberView", err); + }); + } + + /** + * @description get members where membership is setz + * @returns {Promise} + */ + static async getByRunningMembership(): Promise> { + return await dataSource + .getRepository(member) + .createQueryBuilder("member") + .leftJoinAndSelect("member.memberships", "membership") + .where("membership.end IS NULL") + .orderBy("member.lastname") + .addOrderBy("member.firstname") + .addOrderBy("member.nameaffix") + .getMany() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "member", err); + }); + } + + /** + * @description get newsletter by member by id + * @param {string} id + * @returns {Promise} + */ + static async getNewsletterById(id: string): Promise { + return await dataSource + .getRepository(member) + .createQueryBuilder("member") + .leftJoinAndMapOne( + "member.sendNewsletter", + "member.communications", + "sendNewsletter", + "sendNewsletter.isSendNewsletter = 1" + ) + .where("member.id = :id", { id: id }) + .getOneOrFail() + .then((res) => { + return res; + }) + .catch((err) => { + throw new DatabaseActionException("SELECT", "member", err); + }); + } + + /** + * @description apply member joins to query + * @returns {SelectQueryBuilder} + */ + static applyMemberBaseJoins(): SelectQueryBuilder { + return dataSource .getRepository(member) .createQueryBuilder("member") .leftJoinAndMapOne( @@ -133,80 +184,6 @@ export default abstract class MemberService { .leftJoinAndSelect("sendNewsletter.type", "communicationtype") .leftJoinAndMapMany("member.smsAlarming", "member.communications", "smsAlarming", "smsAlarming.isSMSAlarming = 1") .leftJoinAndSelect("smsAlarming.type", "communicationtype_smsAlarming") - .leftJoinAndSelect("member.salutation", "salutation") - .where("member.id = :id", { id: id }) - .getOneOrFail() - .then((res) => { - return res; - }) - .catch((err) => { - throw new InternalException("member not found by id", err); - }); - } - - /** - * @description get member statistics by id - * @param {string} id - * @returns {Promise} - */ - static async getStatisticsById(id: string): Promise { - return await dataSource - .getRepository(memberView) - .createQueryBuilder("memberView") - .where("memberView.id = :id", { id: id }) - .getOneOrFail() - .then((res) => { - return res; - }) - .catch((err) => { - throw new InternalException("memberView not found by id", err); - }); - } - - /** - * @description get members where membership is setz - * @returns {Promise} - */ - static async getByRunningMembership(): Promise> { - return await dataSource - .getRepository(member) - .createQueryBuilder("member") - .leftJoinAndSelect("member.memberships", "membership") - .where("membership.end IS NULL") - .orderBy("member.lastname") - .addOrderBy("member.firstname") - .addOrderBy("member.nameaffix") - .getMany() - .then((res) => { - return res; - }) - .catch((err) => { - throw new InternalException("member not found by id", err); - }); - } - - /** - * @description get newsletter by member by id - * @param {string} id - * @returns {Promise} - */ - static async getNewsletterById(id: string): Promise { - return await dataSource - .getRepository(member) - .createQueryBuilder("member") - .leftJoinAndMapOne( - "member.sendNewsletter", - "member.communications", - "sendNewsletter", - "sendNewsletter.isSendNewsletter = 1" - ) - .where("member.id = :id", { id: id }) - .getOneOrFail() - .then((res) => { - return res; - }) - .catch((err) => { - throw new InternalException("member not found by id", err); - }); + .leftJoinAndSelect("member.salutation", "salutation"); } } diff --git a/src/service/club/member/membershipService.ts b/src/service/club/member/membershipService.ts index 5067e81..f5e7a07 100644 --- a/src/service/club/member/membershipService.ts +++ b/src/service/club/member/membershipService.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../../data-source"; import { membership } from "../../../entity/club/member/membership"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; import InternalException from "../../../exceptions/internalException"; import { membershipView } from "../../../views/membershipsView"; @@ -21,7 +22,7 @@ export default abstract class MembershipService { return res; }) .catch((err) => { - throw new InternalException("member memberships not found", err); + throw new DatabaseActionException("SELECT", "membershis", err); }); } @@ -43,7 +44,7 @@ export default abstract class MembershipService { return res; }) .catch((err) => { - throw new InternalException("member membership not found by id", err); + throw new DatabaseActionException("SELECT", "membership", err); }); } @@ -62,7 +63,7 @@ export default abstract class MembershipService { return res; }) .catch((err) => { - throw new InternalException("membershipView not found by id", err); + throw new DatabaseActionException("SELECT", "membershipView", err); }); } } diff --git a/src/service/club/newsletter/newsletterDatesService.ts b/src/service/club/newsletter/newsletterDatesService.ts index 10c4e7a..55264c1 100644 --- a/src/service/club/newsletter/newsletterDatesService.ts +++ b/src/service/club/newsletter/newsletterDatesService.ts @@ -2,6 +2,7 @@ import { dataSource } from "../../../data-source"; import { newsletterDates } from "../../../entity/club/newsletter/newsletterDates"; import { member } from "../../../entity/club/member/member"; import InternalException from "../../../exceptions/internalException"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; export default abstract class NewsletterDatesService { /** @@ -21,7 +22,7 @@ export default abstract class NewsletterDatesService { return res; }) .catch((err) => { - throw new InternalException("newsletterDatess not found", err); + throw new DatabaseActionException("SELECT", "newsletterDates", err); }); } } diff --git a/src/service/club/newsletter/newsletterRecipientsService.ts b/src/service/club/newsletter/newsletterRecipientsService.ts index fe570de..28bed59 100644 --- a/src/service/club/newsletter/newsletterRecipientsService.ts +++ b/src/service/club/newsletter/newsletterRecipientsService.ts @@ -2,6 +2,7 @@ import { dataSource } from "../../../data-source"; import { newsletterRecipients } from "../../../entity/club/newsletter/newsletterRecipients"; import { member } from "../../../entity/club/member/member"; import InternalException from "../../../exceptions/internalException"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; export default abstract class NewsletterRecipientsService { /** @@ -22,7 +23,7 @@ export default abstract class NewsletterRecipientsService { return res; }) .catch((err) => { - throw new InternalException("newsletterRecipients not found", err); + throw new DatabaseActionException("SELECT", "newsletterRecipient", err); }); } } diff --git a/src/service/club/newsletter/newsletterService.ts b/src/service/club/newsletter/newsletterService.ts index 24c2a3f..27489d0 100644 --- a/src/service/club/newsletter/newsletterService.ts +++ b/src/service/club/newsletter/newsletterService.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../../data-source"; import { newsletter } from "../../../entity/club/newsletter/newsletter"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; import InternalException from "../../../exceptions/internalException"; export default abstract class NewsletterService { @@ -18,7 +19,7 @@ export default abstract class NewsletterService { return res; }) .catch((err) => { - throw new InternalException("newsletters not found", err); + throw new DatabaseActionException("SELECT", "newsletter", err); }); } @@ -37,7 +38,7 @@ export default abstract class NewsletterService { return res; }) .catch((err) => { - throw new InternalException("newsletter not found by id", err); + throw new DatabaseActionException("SELECT", "newsletter", err); }); } } diff --git a/src/service/club/protocol/protocolAgendaService.ts b/src/service/club/protocol/protocolAgendaService.ts index c0899bd..fb8ead8 100644 --- a/src/service/club/protocol/protocolAgendaService.ts +++ b/src/service/club/protocol/protocolAgendaService.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../../data-source"; import { protocolAgenda } from "../../../entity/club/protocol/protocolAgenda"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; import InternalException from "../../../exceptions/internalException"; export default abstract class ProtocolAgendaService { @@ -17,7 +18,7 @@ export default abstract class ProtocolAgendaService { return res; }) .catch((err) => { - throw new InternalException("protocolAgendas not found", err); + throw new DatabaseActionException("SELECT", "protocolAgenda", err); }); } @@ -35,7 +36,7 @@ export default abstract class ProtocolAgendaService { return res; }) .catch((err) => { - throw new InternalException("protocolAgenda not found by id", err); + throw new DatabaseActionException("SELECT", "protocolAgenda", err); }); } } diff --git a/src/service/club/protocol/protocolDecisionService.ts b/src/service/club/protocol/protocolDecisionService.ts index 62dd672..56ac4c6 100644 --- a/src/service/club/protocol/protocolDecisionService.ts +++ b/src/service/club/protocol/protocolDecisionService.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../../data-source"; import { protocolDecision } from "../../../entity/club/protocol/protocolDecision"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; import InternalException from "../../../exceptions/internalException"; export default abstract class ProtocolDecisionService { @@ -17,7 +18,7 @@ export default abstract class ProtocolDecisionService { return res; }) .catch((err) => { - throw new InternalException("protocolDecisions not found", err); + throw new DatabaseActionException("SELECT", "protocolDecision", err); }); } @@ -35,7 +36,7 @@ export default abstract class ProtocolDecisionService { return res; }) .catch((err) => { - throw new InternalException("protocolDecision not found by id", err); + throw new DatabaseActionException("SELECT", "protocolDecision", err); }); } } diff --git a/src/service/club/protocol/protocolPrecenseService.ts b/src/service/club/protocol/protocolPrecenseService.ts index ac3a82f..306031c 100644 --- a/src/service/club/protocol/protocolPrecenseService.ts +++ b/src/service/club/protocol/protocolPrecenseService.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../../data-source"; import { protocolPresence } from "../../../entity/club/protocol/protocolPresence"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; import InternalException from "../../../exceptions/internalException"; export default abstract class ProtocolPresenceService { @@ -18,7 +19,7 @@ export default abstract class ProtocolPresenceService { return res; }) .catch((err) => { - throw new InternalException("protocolPresence not found", err); + throw new DatabaseActionException("SELECT", "protocolPresence", err); }); } } diff --git a/src/service/club/protocol/protocolPrintoutService.ts b/src/service/club/protocol/protocolPrintoutService.ts index a1815fb..ec4600c 100644 --- a/src/service/club/protocol/protocolPrintoutService.ts +++ b/src/service/club/protocol/protocolPrintoutService.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../../data-source"; import { protocolPrintout } from "../../../entity/club/protocol/protocolPrintout"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; import InternalException from "../../../exceptions/internalException"; export default abstract class ProtocolPrintoutService { @@ -17,7 +18,7 @@ export default abstract class ProtocolPrintoutService { return res; }) .catch((err) => { - throw new InternalException("protocolPrintouts not found", err); + throw new DatabaseActionException("SELECT", "protocolPrintout", err); }); } @@ -36,7 +37,7 @@ export default abstract class ProtocolPrintoutService { return res; }) .catch((err) => { - throw new InternalException("protocolPrintout not found by id", err); + throw new DatabaseActionException("SELECT", "protocolPrintout", err); }); } @@ -54,7 +55,7 @@ export default abstract class ProtocolPrintoutService { return res; }) .catch((err) => { - throw new InternalException("protocolPrintout not found by id", err); + throw new DatabaseActionException("SELECT", "protocolPrintout", err); }); } } diff --git a/src/service/club/protocol/protocolService.ts b/src/service/club/protocol/protocolService.ts index 12526fe..185d76a 100644 --- a/src/service/club/protocol/protocolService.ts +++ b/src/service/club/protocol/protocolService.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../../data-source"; import { protocol } from "../../../entity/club/protocol/protocol"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; import InternalException from "../../../exceptions/internalException"; export default abstract class ProtocolService { @@ -19,7 +20,7 @@ export default abstract class ProtocolService { return res; }) .catch((err) => { - throw new InternalException("protocols not found", err); + throw new DatabaseActionException("SELECT", "protocol", err); }); } diff --git a/src/service/club/protocol/protocolVotingService.ts b/src/service/club/protocol/protocolVotingService.ts index cc7c5f6..2000cce 100644 --- a/src/service/club/protocol/protocolVotingService.ts +++ b/src/service/club/protocol/protocolVotingService.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../../data-source"; import { protocolVoting } from "../../../entity/club/protocol/protocolVoting"; +import DatabaseActionException from "../../../exceptions/databaseActionException"; import InternalException from "../../../exceptions/internalException"; export default abstract class ProtocolVotingService { @@ -17,7 +18,7 @@ export default abstract class ProtocolVotingService { return res; }) .catch((err) => { - throw new InternalException("protocolVotings not found", err); + throw new DatabaseActionException("SELECT", "protocolVoting", err); }); } @@ -35,7 +36,7 @@ export default abstract class ProtocolVotingService { return res; }) .catch((err) => { - throw new InternalException("protocolVoting not found by id", err); + throw new DatabaseActionException("SELECT", "protocolVoting", err); }); } } diff --git a/src/service/resetService.ts b/src/service/resetService.ts index 802548e..71a3c99 100644 --- a/src/service/resetService.ts +++ b/src/service/resetService.ts @@ -1,5 +1,6 @@ import { dataSource } from "../data-source"; import { reset } from "../entity/reset"; +import DatabaseActionException from "../exceptions/databaseActionException"; import InternalException from "../exceptions/internalException"; export default abstract class ResetService { @@ -20,7 +21,7 @@ export default abstract class ResetService { return res; }) .catch((err) => { - throw new InternalException("reset not found by mail and token", err); + throw new DatabaseActionException("SELECT", "reset", err); }); } } diff --git a/src/service/settings/awardService.ts b/src/service/settings/awardService.ts index 57872da..cdf95d8 100644 --- a/src/service/settings/awardService.ts +++ b/src/service/settings/awardService.ts @@ -2,6 +2,7 @@ import { dataSource } from "../../data-source"; import { award } from "../../entity/settings/award"; import { member } from "../../entity/club/member/member"; import InternalException from "../../exceptions/internalException"; +import DatabaseActionException from "../../exceptions/databaseActionException"; export default abstract class AwardService { /** @@ -18,7 +19,7 @@ export default abstract class AwardService { return res; }) .catch((err) => { - throw new InternalException("awards not found", err); + throw new DatabaseActionException("SELECT", "award", err); }); } @@ -36,7 +37,7 @@ export default abstract class AwardService { return res; }) .catch((err) => { - throw new InternalException("award not found by id", err); + throw new DatabaseActionException("SELECT", "award", err); }); } } diff --git a/src/service/settings/calendarTypeService.ts b/src/service/settings/calendarTypeService.ts index 1fb3f34..74be65f 100644 --- a/src/service/settings/calendarTypeService.ts +++ b/src/service/settings/calendarTypeService.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../data-source"; import { calendarType } from "../../entity/settings/calendarType"; +import DatabaseActionException from "../../exceptions/databaseActionException"; import InternalException from "../../exceptions/internalException"; export default abstract class CalendarTypeService { @@ -17,7 +18,7 @@ export default abstract class CalendarTypeService { return res; }) .catch((err) => { - throw new InternalException("calendarTypes not found", err); + throw new DatabaseActionException("SELECT", "calendarType", err); }); } @@ -35,7 +36,7 @@ export default abstract class CalendarTypeService { return res; }) .catch((err) => { - throw new InternalException("calendarType not found by id", err); + throw new DatabaseActionException("SELECT", "calendarType", err); }); } @@ -53,7 +54,7 @@ export default abstract class CalendarTypeService { return res; }) .catch((err) => { - throw new InternalException("calendarTypes not found by names", err); + throw new DatabaseActionException("SELECT", "calendarType", err); }); } } diff --git a/src/service/settings/communicationTypeService.ts b/src/service/settings/communicationTypeService.ts index 74494b1..fa71786 100644 --- a/src/service/settings/communicationTypeService.ts +++ b/src/service/settings/communicationTypeService.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../data-source"; import { communicationType } from "../../entity/settings/communicationType"; +import DatabaseActionException from "../../exceptions/databaseActionException"; import InternalException from "../../exceptions/internalException"; export default abstract class CommunicationTypeService { @@ -17,7 +18,7 @@ export default abstract class CommunicationTypeService { return res; }) .catch((err) => { - throw new InternalException("communicationTypes not found", err); + throw new DatabaseActionException("SELECT", "communicationType", err); }); } @@ -35,7 +36,7 @@ export default abstract class CommunicationTypeService { return res; }) .catch((err) => { - throw new InternalException("communicationType not found by id", err); + throw new DatabaseActionException("SELECT", "communicationType", err); }); } } diff --git a/src/service/settings/executivePositionService.ts b/src/service/settings/executivePositionService.ts index cf2b105..ef161d3 100644 --- a/src/service/settings/executivePositionService.ts +++ b/src/service/settings/executivePositionService.ts @@ -2,6 +2,7 @@ import { dataSource } from "../../data-source"; import { executivePosition } from "../../entity/settings/executivePosition"; import { memberExecutivePositions } from "../../entity/club/member/memberExecutivePositions"; import InternalException from "../../exceptions/internalException"; +import DatabaseActionException from "../../exceptions/databaseActionException"; export default abstract class ExecutivePositionService { /** @@ -18,7 +19,7 @@ export default abstract class ExecutivePositionService { return res; }) .catch((err) => { - throw new InternalException("executivePositions not found", err); + throw new DatabaseActionException("SELECT", "executivePosition", err); }); } @@ -36,7 +37,7 @@ export default abstract class ExecutivePositionService { return res; }) .catch((err) => { - throw new InternalException("executivePosition not found by id", err); + throw new DatabaseActionException("SELECT", "executivePosition", err); }); } } diff --git a/src/service/settings/membershipStatusService.ts b/src/service/settings/membershipStatusService.ts index b1e94f1..317f788 100644 --- a/src/service/settings/membershipStatusService.ts +++ b/src/service/settings/membershipStatusService.ts @@ -2,6 +2,7 @@ import { dataSource } from "../../data-source"; import { membershipStatus } from "../../entity/settings/membershipStatus"; import InternalException from "../../exceptions/internalException"; import { membership } from "../../entity/club/member/membership"; +import DatabaseActionException from "../../exceptions/databaseActionException"; export default abstract class MembershipStatusService { /** @@ -18,7 +19,7 @@ export default abstract class MembershipStatusService { return res; }) .catch((err) => { - throw new InternalException("membershipStatuss not found", err); + throw new DatabaseActionException("SELECT", "membershipStatus", err); }); } @@ -36,7 +37,7 @@ export default abstract class MembershipStatusService { return res; }) .catch((err) => { - throw new InternalException("membershipStatus not found by id", err); + throw new DatabaseActionException("SELECT", "membershipStatus", err); }); } } diff --git a/src/service/settings/newsletterConfigService.ts b/src/service/settings/newsletterConfigService.ts index 984fbb2..104968b 100644 --- a/src/service/settings/newsletterConfigService.ts +++ b/src/service/settings/newsletterConfigService.ts @@ -2,6 +2,7 @@ import { dataSource } from "../../data-source"; import { newsletterConfig } from "../../entity/settings/newsletterConfig"; import { member } from "../../entity/club/member/member"; import InternalException from "../../exceptions/internalException"; +import DatabaseActionException from "../../exceptions/databaseActionException"; export default abstract class NewsletterConfigService { /** @@ -19,7 +20,7 @@ export default abstract class NewsletterConfigService { return res; }) .catch((err) => { - throw new InternalException("newsletterConfigs not found", err); + throw new DatabaseActionException("SELECT", "newsletterConfig", err); }); } @@ -38,7 +39,7 @@ export default abstract class NewsletterConfigService { return res; }) .catch((err) => { - throw new InternalException("newsletterConfig not found by cmId", err); + throw new DatabaseActionException("SELECT", "newsletterConfig", err); }); } } diff --git a/src/service/settings/qualification.ts b/src/service/settings/qualification.ts index f400576..2d232d7 100644 --- a/src/service/settings/qualification.ts +++ b/src/service/settings/qualification.ts @@ -2,6 +2,7 @@ import { dataSource } from "../../data-source"; import { memberQualifications } from "../../entity/club/member/memberQualifications"; import { qualification } from "../../entity/settings/qualification"; import { user } from "../../entity/user/user"; +import DatabaseActionException from "../../exceptions/databaseActionException"; import InternalException from "../../exceptions/internalException"; export default abstract class QualificationService { @@ -19,7 +20,7 @@ export default abstract class QualificationService { return res; }) .catch((err) => { - throw new InternalException("qualifications not found", err); + throw new DatabaseActionException("SELECT", "qualification", err); }); } @@ -37,7 +38,7 @@ export default abstract class QualificationService { return res; }) .catch((err) => { - throw new InternalException("qualification not found by id", err); + throw new DatabaseActionException("SELECT", "qualification", err); }); } } diff --git a/src/service/settings/queryStoreService.ts b/src/service/settings/queryStoreService.ts index 8ad5a52..5a4edcb 100644 --- a/src/service/settings/queryStoreService.ts +++ b/src/service/settings/queryStoreService.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../data-source"; import { query } from "../../entity/settings/query"; +import DatabaseActionException from "../../exceptions/databaseActionException"; import InternalException from "../../exceptions/internalException"; export default abstract class QueryStoreService { @@ -17,7 +18,7 @@ export default abstract class QueryStoreService { return res; }) .catch((err) => { - throw new InternalException("queryStores not found", err); + throw new DatabaseActionException("SELECT", "queryStore", err); }); } @@ -35,7 +36,7 @@ export default abstract class QueryStoreService { return res; }) .catch((err) => { - throw new InternalException("queryStore not found by id", err); + throw new DatabaseActionException("SELECT", "queryStore", err); }); } } diff --git a/src/service/settings/salutationService.ts b/src/service/settings/salutationService.ts index 70a6b4f..2337748 100644 --- a/src/service/settings/salutationService.ts +++ b/src/service/settings/salutationService.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../data-source"; import { salutation } from "../../entity/settings/salutation"; +import DatabaseActionException from "../../exceptions/databaseActionException"; import InternalException from "../../exceptions/internalException"; export default abstract class SalutationService { @@ -17,7 +18,7 @@ export default abstract class SalutationService { return res; }) .catch((err) => { - throw new InternalException("salutations not found", err); + throw new DatabaseActionException("SELECT", "salutation", err); }); } @@ -35,7 +36,7 @@ export default abstract class SalutationService { return res; }) .catch((err) => { - throw new InternalException("salutation not found by id", err); + throw new DatabaseActionException("SELECT", "salutation", err); }); } } diff --git a/src/service/settings/templateService.ts b/src/service/settings/templateService.ts index fb073f8..e605ab9 100644 --- a/src/service/settings/templateService.ts +++ b/src/service/settings/templateService.ts @@ -2,6 +2,7 @@ import { dataSource } from "../../data-source"; import { template } from "../../entity/settings/template"; import { member } from "../../entity/club/member/member"; import InternalException from "../../exceptions/internalException"; +import DatabaseActionException from "../../exceptions/databaseActionException"; export default abstract class TemplateService { /** @@ -18,7 +19,7 @@ export default abstract class TemplateService { return res; }) .catch((err) => { - throw new InternalException("templates not found", err); + throw new DatabaseActionException("SELECT", "template", err); }); } @@ -36,7 +37,7 @@ export default abstract class TemplateService { return res; }) .catch((err) => { - throw new InternalException("template not found by id", err); + throw new DatabaseActionException("SELECT", "template", err); }); } } diff --git a/src/service/settings/templateUsageService.ts b/src/service/settings/templateUsageService.ts index a782d7b..cfa1d25 100644 --- a/src/service/settings/templateUsageService.ts +++ b/src/service/settings/templateUsageService.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../data-source"; import { templateUsage } from "../../entity/settings/templateUsage"; +import DatabaseActionException from "../../exceptions/databaseActionException"; import InternalException from "../../exceptions/internalException"; export default abstract class TemplateUsageService { @@ -20,7 +21,7 @@ export default abstract class TemplateUsageService { return res; }) .catch((err) => { - throw new InternalException("templates not found", err); + throw new DatabaseActionException("SELECT", "templateUsage", err); }); } diff --git a/src/service/user/inviteService.ts b/src/service/user/inviteService.ts index 6b2a701..b2cd07a 100644 --- a/src/service/user/inviteService.ts +++ b/src/service/user/inviteService.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../data-source"; import { invite } from "../../entity/user/invite"; +import DatabaseActionException from "../../exceptions/databaseActionException"; import InternalException from "../../exceptions/internalException"; export default abstract class InviteService { @@ -16,7 +17,7 @@ export default abstract class InviteService { return res; }) .catch((err) => { - throw new InternalException("invites not found", err); + throw new DatabaseActionException("SELECT", "invite", err); }); } @@ -37,7 +38,7 @@ export default abstract class InviteService { return res; }) .catch((err) => { - throw new InternalException("invite not found by mail and token", err); + throw new DatabaseActionException("SELECT", "invite", err); }); } } diff --git a/src/service/user/rolePermissionService.ts b/src/service/user/rolePermissionService.ts index e40930f..1cfe6f2 100644 --- a/src/service/user/rolePermissionService.ts +++ b/src/service/user/rolePermissionService.ts @@ -1,6 +1,7 @@ import { dataSource } from "../../data-source"; import { rolePermission } from "../../entity/user/role_permission"; import { userPermission } from "../../entity/user/user_permission"; +import DatabaseActionException from "../../exceptions/databaseActionException"; import InternalException from "../../exceptions/internalException"; export default abstract class RolePermissionService { @@ -19,7 +20,7 @@ export default abstract class RolePermissionService { return res; }) .catch((err) => { - throw new InternalException("permissions not found by role", err); + throw new DatabaseActionException("SELECT", "rolePermission", err); }); } @@ -38,7 +39,7 @@ export default abstract class RolePermissionService { return res; }) .catch((err) => { - throw new InternalException("permissions not found by roles", err); + throw new DatabaseActionException("SELECT", "rolePermission", err); }); } } diff --git a/src/service/user/roleService.ts b/src/service/user/roleService.ts index 042c001..7601063 100644 --- a/src/service/user/roleService.ts +++ b/src/service/user/roleService.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../data-source"; import { role } from "../../entity/user/role"; +import DatabaseActionException from "../../exceptions/databaseActionException"; import InternalException from "../../exceptions/internalException"; export default abstract class RoleService { @@ -18,7 +19,7 @@ export default abstract class RoleService { return res; }) .catch((err) => { - throw new InternalException("roles not found", err); + throw new DatabaseActionException("SELECT", "roles", err); }); } @@ -38,7 +39,7 @@ export default abstract class RoleService { return res; }) .catch((err) => { - throw new InternalException("role not found by id", err); + throw new DatabaseActionException("SELECT", "role", err); }); } } diff --git a/src/service/user/userPermissionService.ts b/src/service/user/userPermissionService.ts index 0078ee3..7cb3ee6 100644 --- a/src/service/user/userPermissionService.ts +++ b/src/service/user/userPermissionService.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../data-source"; import { userPermission } from "../../entity/user/user_permission"; +import DatabaseActionException from "../../exceptions/databaseActionException"; import InternalException from "../../exceptions/internalException"; export default abstract class UserPermissionService { @@ -18,7 +19,7 @@ export default abstract class UserPermissionService { return res; }) .catch((err) => { - throw new InternalException("permission not found by user", err); + throw new DatabaseActionException("SELECT", "userPermission", err); }); } } diff --git a/src/service/user/userService.ts b/src/service/user/userService.ts index b75d499..2abb7f7 100644 --- a/src/service/user/userService.ts +++ b/src/service/user/userService.ts @@ -1,6 +1,7 @@ import { dataSource } from "../../data-source"; import { role } from "../../entity/user/role"; import { user } from "../../entity/user/user"; +import DatabaseActionException from "../../exceptions/databaseActionException"; import InternalException from "../../exceptions/internalException"; export default abstract class UserService { @@ -22,7 +23,7 @@ export default abstract class UserService { return res; }) .catch((err) => { - throw new InternalException("users not found", err); + throw new DatabaseActionException("SELECT", "user", err); }); } @@ -44,7 +45,7 @@ export default abstract class UserService { return res; }) .catch((err) => { - throw new InternalException("user not found by id", err); + throw new DatabaseActionException("SELECT", "user", err); }); } @@ -64,7 +65,7 @@ export default abstract class UserService { return res; }) .catch((err) => { - throw new InternalException("user not found by username", err); + throw new DatabaseActionException("SELECT", "user", err); }); } @@ -86,7 +87,7 @@ export default abstract class UserService { return res; }) .catch((err) => { - throw new InternalException("user not found by mail or username", err); + throw new DatabaseActionException("SELECT", "user", err); }); } @@ -104,7 +105,7 @@ export default abstract class UserService { return res; }) .catch((err) => { - throw new InternalException("could not count users", err); + throw new DatabaseActionException("COUNT", "users", err); }); } @@ -125,7 +126,7 @@ export default abstract class UserService { return res.roles; }) .catch((err) => { - throw new InternalException("could not get roles for user", err); + throw new DatabaseActionException("SELECT", "userRoles", err); }); } } diff --git a/src/service/user/webapiPermissionService.ts b/src/service/user/webapiPermissionService.ts index a210353..ab57582 100644 --- a/src/service/user/webapiPermissionService.ts +++ b/src/service/user/webapiPermissionService.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../data-source"; import { webapiPermission } from "../../entity/user/webapi_permission"; +import DatabaseActionException from "../../exceptions/databaseActionException"; import InternalException from "../../exceptions/internalException"; export default abstract class WebapiPermissionService { @@ -18,7 +19,7 @@ export default abstract class WebapiPermissionService { return res; }) .catch((err) => { - throw new InternalException("webapi permissions not found by api", err); + throw new DatabaseActionException("SELECT", "webapiPermission", err); }); } } diff --git a/src/service/user/webapiService.ts b/src/service/user/webapiService.ts index 0faa51a..571a013 100644 --- a/src/service/user/webapiService.ts +++ b/src/service/user/webapiService.ts @@ -1,5 +1,6 @@ import { dataSource } from "../../data-source"; import { webapi } from "../../entity/user/webapi"; +import DatabaseActionException from "../../exceptions/databaseActionException"; import InternalException from "../../exceptions/internalException"; export default abstract class WebapiService { @@ -17,7 +18,7 @@ export default abstract class WebapiService { return res; }) .catch((err) => { - throw new InternalException("webapis not found", err); + throw new DatabaseActionException("SELECT", "webapi", err); }); } @@ -37,7 +38,7 @@ export default abstract class WebapiService { return res; }) .catch((err) => { - throw new InternalException("webapi not found by id", err); + throw new DatabaseActionException("SELECT", "webapi", err); }); } @@ -57,7 +58,7 @@ export default abstract class WebapiService { return res; }) .catch((err) => { - throw new InternalException("webapi not found by token", err); + throw new DatabaseActionException("SELECT", "webapi", err); }); } @@ -77,7 +78,7 @@ export default abstract class WebapiService { return res; }) .catch((err) => { - throw new InternalException("webapi token not found by id", err); + throw new DatabaseActionException("SELECT", "webapi", err); }); } } -- 2.45.3 From 6ae463a7846eb732b38b2f4d2029fc7a0d190a3b Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Wed, 29 Jan 2025 10:05:55 +0100 Subject: [PATCH 06/22] fix: member update internal id default --- src/controller/admin/club/memberController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controller/admin/club/memberController.ts b/src/controller/admin/club/memberController.ts index 6758ee6..fb782cc 100644 --- a/src/controller/admin/club/memberController.ts +++ b/src/controller/admin/club/memberController.ts @@ -447,7 +447,7 @@ export async function updateMemberById(req: Request, res: Response): Promise Date: Wed, 29 Jan 2025 16:49:34 +0100 Subject: [PATCH 07/22] create and restore backups --- src/entity/club/calendar.ts | 1 + src/entity/club/member/communication.ts | 1 + src/entity/club/member/member.ts | 15 +- src/entity/club/member/memberAwards.ts | 1 + .../club/member/memberExecutivePositions.ts | 1 + .../club/member/memberQualifications.ts | 1 + src/entity/club/member/membership.ts | 1 + src/entity/club/newsletter/newsletter.ts | 5 +- src/entity/club/protocol/protocol.ts | 22 +- src/entity/settings/newsletterConfig.ts | 3 +- src/entity/user/role.ts | 2 +- src/entity/user/user.ts | 3 +- src/entity/user/webapi.ts | 2 +- src/env.defaults.ts | 6 +- src/helpers/backupHelper.ts | 638 ++++++++++++++++++ src/index.ts | 15 +- 16 files changed, 696 insertions(+), 21 deletions(-) create mode 100644 src/helpers/backupHelper.ts diff --git a/src/entity/club/calendar.ts b/src/entity/club/calendar.ts index c8c2cb6..63ebb4d 100644 --- a/src/entity/club/calendar.ts +++ b/src/entity/club/calendar.ts @@ -47,6 +47,7 @@ export class calendar { nullable: false, onDelete: "RESTRICT", onUpdate: "RESTRICT", + cascade: ["insert"], }) type: calendarType; } diff --git a/src/entity/club/member/communication.ts b/src/entity/club/member/communication.ts index 574f3c5..3ec5062 100644 --- a/src/entity/club/member/communication.ts +++ b/src/entity/club/member/communication.ts @@ -54,6 +54,7 @@ export class communication { nullable: false, onDelete: "RESTRICT", onUpdate: "RESTRICT", + cascade: ["insert"], }) type: communicationType; } diff --git a/src/entity/club/member/member.ts b/src/entity/club/member/member.ts index a584222..228eebb 100644 --- a/src/entity/club/member/member.ts +++ b/src/entity/club/member/member.ts @@ -29,26 +29,27 @@ export class member { @Column() salutationId: number; - @OneToMany(() => communication, (communications) => communications.member) - communications: communication[]; - @ManyToOne(() => salutation, (salutation) => salutation.members, { nullable: false, onDelete: "RESTRICT", onUpdate: "RESTRICT", + cascade: ["insert"], }) salutation: salutation; - @OneToMany(() => membership, (membership) => membership.member) + @OneToMany(() => communication, (communications) => communications.member, { cascade: ["insert"] }) + communications: communication[]; + + @OneToMany(() => membership, (membership) => membership.member, { cascade: ["insert"] }) memberships: membership[]; - @OneToMany(() => memberAwards, (awards) => awards.member) + @OneToMany(() => memberAwards, (awards) => awards.member, { cascade: ["insert"] }) awards: memberAwards[]; - @OneToMany(() => memberExecutivePositions, (executivePositions) => executivePositions.member) + @OneToMany(() => memberExecutivePositions, (executivePositions) => executivePositions.member, { cascade: ["insert"] }) positions: memberExecutivePositions[]; - @OneToMany(() => memberQualifications, (qualifications) => qualifications.member) + @OneToMany(() => memberQualifications, (qualifications) => qualifications.member, { cascade: ["insert"] }) qualifications: memberQualifications[]; firstMembershipEntry?: membership; diff --git a/src/entity/club/member/memberAwards.ts b/src/entity/club/member/memberAwards.ts index bf11ee6..1a453a2 100644 --- a/src/entity/club/member/memberAwards.ts +++ b/src/entity/club/member/memberAwards.ts @@ -33,6 +33,7 @@ export class memberAwards { nullable: false, onDelete: "RESTRICT", onUpdate: "RESTRICT", + cascade: ["insert"], }) award: award; } diff --git a/src/entity/club/member/memberExecutivePositions.ts b/src/entity/club/member/memberExecutivePositions.ts index 0051184..f09e9a8 100644 --- a/src/entity/club/member/memberExecutivePositions.ts +++ b/src/entity/club/member/memberExecutivePositions.ts @@ -33,6 +33,7 @@ export class memberExecutivePositions { nullable: false, onDelete: "RESTRICT", onUpdate: "RESTRICT", + cascade: ["insert"], }) executivePosition: executivePosition; } diff --git a/src/entity/club/member/memberQualifications.ts b/src/entity/club/member/memberQualifications.ts index 78cd610..fe97ba5 100644 --- a/src/entity/club/member/memberQualifications.ts +++ b/src/entity/club/member/memberQualifications.ts @@ -36,6 +36,7 @@ export class memberQualifications { nullable: false, onDelete: "RESTRICT", onUpdate: "RESTRICT", + cascade: ["insert"], }) qualification: qualification; } diff --git a/src/entity/club/member/membership.ts b/src/entity/club/member/membership.ts index 14e9b6f..63d2d52 100644 --- a/src/entity/club/member/membership.ts +++ b/src/entity/club/member/membership.ts @@ -34,6 +34,7 @@ export class membership { nullable: false, onDelete: "RESTRICT", onUpdate: "RESTRICT", + cascade: ["insert"], }) @JoinColumn() status: membershipStatus; diff --git a/src/entity/club/newsletter/newsletter.ts b/src/entity/club/newsletter/newsletter.ts index 1c26a68..90650e4 100644 --- a/src/entity/club/newsletter/newsletter.ts +++ b/src/entity/club/newsletter/newsletter.ts @@ -30,16 +30,17 @@ export class newsletter { @Column({ type: "int", nullable: true }) recipientsByQueryId?: number; - @OneToMany(() => newsletterDates, (dates) => dates.newsletter) + @OneToMany(() => newsletterDates, (dates) => dates.newsletter, { cascade: ["insert"] }) dates: newsletterDates[]; - @OneToMany(() => newsletterRecipients, (recipient) => recipient.newsletter) + @OneToMany(() => newsletterRecipients, (recipient) => recipient.newsletter, { cascade: ["insert"] }) recipients: newsletterRecipients[]; @ManyToOne(() => query, { nullable: true, onDelete: "CASCADE", onUpdate: "RESTRICT", + cascade: ["insert"], }) recipientsByQuery?: query; } diff --git a/src/entity/club/protocol/protocol.ts b/src/entity/club/protocol/protocol.ts index 215d9bc..feaa4d9 100644 --- a/src/entity/club/protocol/protocol.ts +++ b/src/entity/club/protocol/protocol.ts @@ -1,4 +1,9 @@ -import { Column, Entity, PrimaryColumn } from "typeorm"; +import { Column, Entity, OneToMany, PrimaryColumn } from "typeorm"; +import { protocolAgenda } from "./protocolAgenda"; +import { protocolDecision } from "./protocolDecision"; +import { protocolPresence } from "./protocolPresence"; +import { protocolPrintout } from "./protocolPrintout"; +import { protocolVoting } from "./protocolVoting"; @Entity() export class protocol { @@ -19,4 +24,19 @@ export class protocol { @Column({ type: "text", nullable: true }) summary: string; + + @OneToMany(() => protocolAgenda, (agenda) => agenda.protocol, { cascade: ["insert"] }) + agendas: protocolAgenda[]; + + @OneToMany(() => protocolDecision, (decision) => decision.protocol, { cascade: ["insert"] }) + decisions: protocolDecision[]; + + @OneToMany(() => protocolPresence, (presence) => presence.protocol, { cascade: ["insert"] }) + presences: protocolPresence[]; + + @OneToMany(() => protocolPrintout, (printout) => printout.protocol, { cascade: ["insert"] }) + printouts: protocolPrintout[]; + + @OneToMany(() => protocolVoting, (voting) => voting.protocol, { cascade: ["insert"] }) + votings: protocolVoting[]; } diff --git a/src/entity/settings/newsletterConfig.ts b/src/entity/settings/newsletterConfig.ts index ae335ce..17dde81 100644 --- a/src/entity/settings/newsletterConfig.ts +++ b/src/entity/settings/newsletterConfig.ts @@ -4,7 +4,7 @@ import { communicationType } from "./communicationType"; @Entity() export class newsletterConfig { - @PrimaryColumn({ type: "int" }) + @PrimaryColumn() comTypeId: number; @Column({ @@ -25,6 +25,7 @@ export class newsletterConfig { nullable: false, onDelete: "CASCADE", onUpdate: "RESTRICT", + cascade: ["insert"], }) comType: communicationType; } diff --git a/src/entity/user/role.ts b/src/entity/user/role.ts index b7692bf..1cc76a9 100644 --- a/src/entity/user/role.ts +++ b/src/entity/user/role.ts @@ -17,6 +17,6 @@ export class role { }) users: user[]; - @OneToMany(() => rolePermission, (rolePermission) => rolePermission.role) + @OneToMany(() => rolePermission, (rolePermission) => rolePermission.role, { cascade: ["insert"] }) permissions: rolePermission[]; } diff --git a/src/entity/user/user.ts b/src/entity/user/user.ts index bb42bb8..0a0a7a7 100644 --- a/src/entity/user/user.ts +++ b/src/entity/user/user.ts @@ -29,12 +29,13 @@ export class user { nullable: false, onDelete: "CASCADE", onUpdate: "RESTRICT", + cascade: ["insert"], }) @JoinTable({ name: "user_roles", }) roles: role[]; - @OneToMany(() => userPermission, (userPermission) => userPermission.user) + @OneToMany(() => userPermission, (userPermission) => userPermission.user, { cascade: ["insert"] }) permissions: userPermission[]; } diff --git a/src/entity/user/webapi.ts b/src/entity/user/webapi.ts index 220db8c..2e8292c 100644 --- a/src/entity/user/webapi.ts +++ b/src/entity/user/webapi.ts @@ -21,6 +21,6 @@ export class webapi { @Column({ type: "date", nullable: true }) expiry?: Date; - @OneToMany(() => webapiPermission, (apiPermission) => apiPermission.webapi) + @OneToMany(() => webapiPermission, (apiPermission) => apiPermission.webapi, { cascade: ["insert"] }) permissions: webapiPermission[]; } diff --git a/src/env.defaults.ts b/src/env.defaults.ts index 343a9b5..96968cb 100644 --- a/src/env.defaults.ts +++ b/src/env.defaults.ts @@ -24,9 +24,9 @@ export const MAIL_SECURE = process.env.MAIL_SECURE ?? "false"; export const CLUB_NAME = process.env.CLUB_NAME ?? "FF Admin"; export const CLUB_WEBSITE = process.env.CLUB_WEBSITE ?? ""; -export const BACKUP_INTERVAL = Number(process.env.CLUB_WEBSITE ?? "0"); -export const BACKUP_COPIES = Number(process.env.CLUB_WEBSITE ?? "0"); -export const BACKUP_AUTO_RESTORE = process.env.CLUB_WEBSITE ?? "false"; +export const BACKUP_INTERVAL = Number(process.env.BACKUP_INTERVAL ?? "0"); +export const BACKUP_COPIES = Number(process.env.BACKUP_COPIES ?? "0"); +export const BACKUP_AUTO_RESTORE = process.env.BACKUP_AUTO_RESTORE ?? "false"; export function configCheck() { if (DB_TYPE != "mysql" && DB_TYPE != "sqlite") throw new Error("set valid value to DB_TYPE (mysql|sqlite)"); diff --git a/src/helpers/backupHelper.ts b/src/helpers/backupHelper.ts new file mode 100644 index 0000000..8deff02 --- /dev/null +++ b/src/helpers/backupHelper.ts @@ -0,0 +1,638 @@ +import { dataSource } from "../data-source"; +import { FileSystemHelper } from "./fileSystemHelper"; +import { EntityManager } from "typeorm"; +import InternalException from "../exceptions/internalException"; +import UserService from "../service/user/userService"; + +export type BackupSection = + | "member" + | "memberBase" + | "protocol" + | "newsletter" + | "newsletter_config" + | "calendar" + | "query" + | "template" + | "user" + | "webapi"; + +export type BackupSectionRefered = { + [key in BackupSection]?: Array; +}; + +export type BackupFileContent = { [key in BackupSection]?: BackupFileContentSection } & { collectIds: boolean }; +export type BackupFileContentSection = Array | { [key: string]: Array }; + +export default abstract class BackupHelper { + // ! Order matters because of foreign keys + private static readonly backupSection: Array<{ type: BackupSection; orderOnInsert: number; orderOnClear: number }> = [ + { type: "member", orderOnInsert: 2, orderOnClear: 2 }, // CLEAR depends on protcol INSERT depends on Base + { type: "memberBase", orderOnInsert: 1, orderOnClear: 3 }, // CLEAR depends on member + { type: "protocol", orderOnInsert: 3, orderOnClear: 1 }, // INSERT depends on member + { type: "newsletter", orderOnInsert: 3, orderOnClear: 1 }, // INSERT depends on member & query & calendar + { type: "newsletter_config", orderOnInsert: 2, orderOnClear: 4 }, // INSERT depends on member com + { type: "calendar", orderOnInsert: 1, orderOnClear: 1 }, + { type: "query", orderOnInsert: 1, orderOnClear: 2 }, // CLEAR depends on newsletter + { type: "template", orderOnInsert: 2, orderOnClear: 1 }, // INSERT depends on member com + { type: "user", orderOnInsert: 1, orderOnClear: 1 }, + { type: "webapi", orderOnInsert: 1, orderOnClear: 1 }, + ]; + + private static readonly backupSectionRefered: BackupSectionRefered = { + member: [ + "member", + "member_awards", + "member_qualifications", + "member_executive_positions", + "membership", + "communication", + ], + memberBase: [ + "award", + "qualification", + "executive_position", + "membership_status", + "communication_type", + "salutation", + ], + protocol: [ + "protocol", + "protocol_agenda", + "protocol_decision", + "protocol_presence", + "protocol_printout", + "protocol_voting", + ], + newsletter: ["newsletter", "newsletter_dates", "newsletter_recipients", "newsletter_config"], + newsletter_config: ["newsletter_config"], + calendar: ["calendar", "calendar_type"], + query: ["query"], + template: ["template", "template_usage"], + user: ["user", "user_permission", "role", "role_permission", "invite"], + webapi: ["webapi", "webapi_permission"], + }; + + private static transactionManager: EntityManager; + + static async createBackup({ + filename, + path = "/backup", + collectIds = true, + }: { + filename?: string; + path?: string; + collectIds?: boolean; + }): Promise { + if (!filename) { + filename = new Date().toISOString().split("T")[0]; + } + + let json: BackupFileContent = { collectIds }; + for (const section of this.backupSection) { + json[section.type] = await this.getSectionData(section.type, collectIds); + } + + FileSystemHelper.writeFile(path, filename + ".json", JSON.stringify(json, null, 2)); + } + + static async loadBackup({ + filename, + path = "/backup", + include = [], + partial = false, + }: { + filename: string; + path?: string; + partial?: boolean; + include?: Array; + }): Promise { + this.transactionManager = undefined; + + let file = FileSystemHelper.readFile(`${path}/${filename}`); + let backup: BackupFileContent = JSON.parse(file); + + dataSource.manager + .transaction(async (transaction) => { + this.transactionManager = transaction; + + const sections = this.backupSection + .filter((bs) => (partial ? include.includes(bs.type) : true)) + .sort((a, b) => a.orderOnClear - b.orderOnClear); + for (const section of sections.filter((s) => Object.keys(backup).includes(s.type))) { + let refered = this.backupSectionRefered[section.type]; + for (const ref of refered) { + await this.transactionManager.getRepository(ref).delete({}); + } + } + + for (const section of sections + .filter((s) => Object.keys(backup).includes(s.type)) + .sort((a, b) => a.orderOnInsert - b.orderOnInsert)) { + await this.setSectionData(section.type, backup[section.type], backup.collectIds ?? false); + } + + this.transactionManager = undefined; + }) + .catch((err) => { + this.transactionManager = undefined; + throw new InternalException("failed to restore backup - rolling back actions"); + }); + } + + public static async autoRestoreBackup() { + let count = await UserService.count(); + if (count == 0) { + let files = FileSystemHelper.getFilesInDirectory("/backup", ".json"); + let newestFile = files.sort( + (a, b) => new Date(b.split(".")[0]).getTime() - new Date(a.split(".")[0]).getTime() + )[0]; + + if (!newestFile) { + console.log(`${new Date().toISOString()}: auto-restoring ${newestFile}`); + await this.loadBackup({ filename: newestFile }); + console.log(`${new Date().toISOString()}: finished auto-restore`); + } else { + console.log(`${new Date().toISOString()}: skip auto-restore as no backup was found`); + } + } else { + console.log(`${new Date().toISOString()}: skip auto-restore as users exist`); + } + } + + private static async getSectionData( + section: BackupSection, + collectIds: boolean + ): Promise | { [key: string]: any }> { + switch (section) { + case "member": + return await this.getMemberData(collectIds); + case "memberBase": + return await this.getMemberBase(); + case "protocol": + return await this.getProtocol(collectIds); + case "newsletter": + return await this.getNewsletter(collectIds); + case "newsletter_config": + return await this.getNewsletterConfig(); + case "calendar": + return await this.getCalendar(); + case "query": + return await this.getQueryStore(); + case "template": + return await this.getTemplate(); + case "user": + return await this.getUser(collectIds); + case "webapi": + return await this.getWebapi(); + default: + return []; + } + } + + private static async getMemberData(collectIds: boolean): Promise> { + return await dataSource + .getRepository("member") + .createQueryBuilder("member") + .leftJoin("member.salutation", "salutation") + .leftJoin("member.communications", "communication") + .leftJoin("communication.type", "communicationType") + .leftJoin("member.memberships", "memberships") + .leftJoin("memberships.status", "membershipStatus") + .leftJoin("member.awards", "awards") + .leftJoin("awards.award", "award") + .leftJoin("member.positions", "positions") + .leftJoin("positions.executivePosition", "executivePosition") + .leftJoin("member.qualifications", "qualifications") + .leftJoin("qualifications.qualification", "qualification") + .select([ + ...(collectIds ? ["member.id"] : []), + "member.firstname", + "member.lastname", + "member.nameaffix", + "member.birthdate", + "member.internalId", + ]) + .addSelect(["salutation.salutation"]) + .addSelect([ + "communication.preferred", + "communication.isSMSAlarming", + "communication.isSendNewsletter", + "communication.mobile", + "communication.email", + "communication.postalCode", + "communication.city", + "communication.street", + "communication.streetNumber", + "communication.streetNumberAddition", + "communicationType.type", + "communicationType.useColumns", + ]) + .addSelect(["memberships.start", "memberships.end", "memberships.terminationReason", "membershipStatus.status"]) + .addSelect(["awards.given", "awards.note", "awards.note", "awards.date", "award.award"]) + .addSelect(["positions.note", "positions.start", "positions.end", "executivePosition.position"]) + .addSelect([ + "qualifications.note", + "qualifications.start", + "qualifications.end", + "qualifications.terminationReason", + "qualification.qualification", + "qualification.description", + ]) + .getMany(); + } + private static async getMemberBase(): Promise<{ [key: string]: Array }> { + return { + award: await dataSource.getRepository("award").find({ select: { award: true } }), + communication_type: await dataSource + .getRepository("communication_type") + .find({ select: { type: true, useColumns: true } }), + executive_position: await dataSource.getRepository("executive_position").find({ select: { position: true } }), + membership_status: await dataSource.getRepository("membership_status").find({ select: { status: true } }), + salutation: await dataSource.getRepository("salutation").find({ select: { salutation: true } }), + qualification: await dataSource + .getRepository("qualification") + .find({ select: { qualification: true, description: true } }), + }; + } + private static async getProtocol(collectIds: boolean): Promise> { + return await dataSource + .getRepository("protocol") + .createQueryBuilder("protocol") + .leftJoin("protocol.agendas", "agendas") + .leftJoin("protocol.decisions", "decisions") + .leftJoin("protocol.presences", "presences") + .leftJoin("presences.member", "member") + .leftJoin("protocol.printouts", "printouts") + .leftJoin("protocol.votings", "votings") + .select(["protocol.title", "protocol.date", "protocol.starttime", "protocol.endtime", "protocol.summary"]) + .addSelect(["agendas.topic", "agendas.context"]) + .addSelect(["decisions.topic", "decisions.context"]) + .addSelect(["presences.absent", "presences.excused"]) + .addSelect([ + ...(collectIds ? ["member.id"] : []), + "member.firstname", + "member.lastname", + "member.nameaffix", + "member.birthdate", + "member.internalId", + ]) + .addSelect(["printouts.title", "printouts.iteration", "printouts.filename", "printouts.createdAt"]) + .addSelect(["votings.topic", "votings.context", "votings.favour", "votings.abstain", "votings.against"]) + .getMany(); + } + private static async getNewsletter(collectIds: boolean): Promise> { + return await dataSource + .getRepository("newsletter") + .createQueryBuilder("newsletter") + .leftJoin("newsletter.dates", "dates") + .leftJoin("newsletter.recipients", "recipients") + .leftJoin("recipients.member", "member") + .leftJoin("newsletter.recipientsByQuery", "recipientsByQuery") + .select([ + "newsletter.title", + "newsletter.description", + "newsletter.newsletterTitle", + "newsletter.newsletterText", + "newsletter.newsletterSignatur", + "newsletter.isSent", + ]) + .addSelect(["dates.calendarId", "dates.diffTitle", "dates.diffDescription"]) + .addSelect([ + ...(collectIds ? ["member.id"] : []), + "member.firstname", + "member.lastname", + "member.nameaffix", + "member.birthdate", + "member.internalId", + ]) + .addSelect(["recipientsByQuery.title", "recipientsByQuery.query"]) + .getMany(); + } + private static async getNewsletterConfig(): Promise> { + return await dataSource + .getRepository("newsletter_config") + .createQueryBuilder("newsletter_config") + .leftJoin("newsletter_config.comType", "comType") + .select(["newsletter_config.config"]) + .addSelect(["comType.type", "comType.useColumns"]) + .getMany(); + } + private static async getCalendar(): Promise<{ [key: string]: Array }> { + return { + calendar: await dataSource + .getRepository("calendar") + .createQueryBuilder("calendar") + .leftJoin("calendar.type", "type") + .select([ + "calendar.id", + "calendar.starttime", + "calendar.endtime", + "calendar.title", + "calendar.content", + "calendar.location", + "calendar.allDay", + "calendar.sequence", + "calendar.createdAt", + "calendar.updatedAt", + ]) + .addSelect(["type.type", "type.nscdr", "type.color", "type.passphrase"]) + .getMany(), + calendar_type: await dataSource + .getRepository("calendar_type") + .createQueryBuilder("calendar_type") + .select(["calendar_type.type", "calendar_type.nscdr", "calendar_type.color", "calendar_type.passphrase"]) + .getMany(), + }; + } + private static async getQueryStore(): Promise> { + return await dataSource.getRepository("query").find({ select: { title: true, query: true } }); + } + private static async getTemplate(): Promise<{ [key: string]: Array }> { + return { + template: await dataSource + .getRepository("template") + .find({ select: { template: true, description: true, design: true, html: true } }), + template_usage: await dataSource + .getRepository("template_usage") + .createQueryBuilder("template_usage") + .leftJoin("template_usage.header", "header") + .leftJoin("template_usage.body", "body") + .leftJoin("template_usage.footer", "footer") + .select(["template_usage.scope", "template_usage.headerHeight", "template_usage.footerHeight"]) + .addSelect(["header.template", "header.description", "header.design", "header.html"]) + .addSelect(["body.template", "body.description", "body.design", "body.html"]) + .addSelect(["footer.template", "footer.description", "footer.design", "footer.html"]) + .getMany(), + }; + } + private static async getUser(collectIds: boolean): Promise<{ [key: string]: Array }> { + return { + user: await dataSource + .getRepository("user") + .createQueryBuilder("user") + .leftJoin("user.roles", "roles") + .leftJoin("roles.permissions", "role_permissions") + .leftJoin("user.permissions", "permissions") + .select([ + ...(collectIds ? ["user.id"] : []), + "user.mail", + "user.username", + "user.firstname", + "user.lastname", + "user.secret", + "user.isOwner", + ]) + .addSelect(["permissions.permission"]) + .addSelect(["roles.role"]) + .addSelect(["role_permissions.permission"]) + .getMany(), + role: await dataSource + .getRepository("role") + .createQueryBuilder("role") + .leftJoin("role.permissions", "permissions") + .addSelect(["role.role"]) + .addSelect(["permissions.permission"]) + .getMany(), + invite: await dataSource.getRepository("invite").find(), + }; + } + private static async getWebapi(): Promise> { + return await dataSource + .getRepository("webapi") + .createQueryBuilder("webapi") + .leftJoin("webapi.permissions", "permissions") + .select(["webapi.token", "webapi.title", "webapi.createdAt", "webapi.lastUsage", "webapi.expiry"]) + .addSelect(["permissions.permission"]) + .getMany(); + } + + private static async setSectionData( + section: BackupSection, + data: BackupFileContentSection, + collectedIds: boolean + ): Promise { + if (section == "member" && Array.isArray(data)) await this.setMemberData(data); + if (section == "memberBase" && !Array.isArray(data)) await this.setMemberBase(data); + if (section == "protocol" && Array.isArray(data)) await this.setProtocol(data, collectedIds); + if (section == "newsletter" && Array.isArray(data)) await this.setNewsletter(data, collectedIds); + if (section == "newsletter_config" && Array.isArray(data)) await this.setNewsletterConfig(data); + if (section == "calendar" && !Array.isArray(data)) await this.setCalendar(data); + if (section == "query" && Array.isArray(data)) await this.setQueryStore(data); + if (section == "template" && !Array.isArray(data)) await this.setTemplate(data); + if (section == "user" && !Array.isArray(data)) await this.setUser(data); + if (section == "webapi" && Array.isArray(data)) await this.setWebapi(data); + } + + private static async setMemberData(data: Array): Promise { + let salutation = await this.transactionManager.getRepository("salutation").find(); + let communication = await this.transactionManager.getRepository("communication_type").find(); + let membership = await this.transactionManager.getRepository("membership_status").find(); + let award = await this.transactionManager.getRepository("award").find(); + let qualification = await this.transactionManager.getRepository("qualification").find(); + let position = await this.transactionManager.getRepository("executive_position").find(); + let dataWithMappedIds = data.map((d) => ({ + ...d, + salutation: { + ...d.salutation, + id: salutation.find((s) => s.salutation == d.salutation.salutation)?.id ?? undefined, + }, + communications: d.communications.map((c: any) => ({ + ...c, + type: { + ...c.type, + id: communication.find((s) => s.type == c.type.type)?.id ?? undefined, + }, + })), + memberships: d.memberships.map((m: any) => ({ + ...m, + status: { + ...m.status, + id: membership.find((ms) => ms.status == m.status.status)?.id ?? undefined, + }, + })), + awards: d.awards.map((a: any) => ({ + ...a, + award: { + ...a.award, + id: award.find((ia) => ia.award == a.award.award)?.id ?? undefined, + }, + })), + positions: d.positions.map((p: any) => ({ + ...p, + executivePosition: { + ...p.executivePosition, + id: position.find((ip) => ip.position == p.executivePosition.position)?.id ?? undefined, + }, + })), + qualifications: d.qualifications.map((q: any) => ({ + ...q, + qualification: { + ...q.qualification, + id: qualification.find((iq) => iq.qualification == q.qualification.qualification)?.id ?? undefined, + }, + })), + })); + await this.transactionManager.getRepository("member").save(dataWithMappedIds); + } + private static async setMemberBase(data: { [key: string]: Array }): Promise { + await this.transactionManager + .createQueryBuilder() + .insert() + .into("award") + .values(data["award"]) + .orIgnore() + .execute(); + await this.transactionManager + .createQueryBuilder() + .insert() + .into("communication_type") + .values(data["communication_type"]) + .orIgnore() + .execute(); + await this.transactionManager + .createQueryBuilder() + .insert() + .into("executive_position") + .values(data["executive_position"]) + .orIgnore() + .execute(); + await this.transactionManager + .createQueryBuilder() + .insert() + .into("membership_status") + .values(data["membership_status"]) + .orIgnore() + .execute(); + await this.transactionManager + .createQueryBuilder() + .insert() + .into("salutation") + .values(data["salutation"]) + .orIgnore() + .execute(); + await this.transactionManager + .createQueryBuilder() + .insert() + .into("qualification") + .values(data["qualification"]) + .orIgnore() + .execute(); + } + private static async setProtocol(data: Array, collectedIds: boolean): Promise { + let members = await this.transactionManager.getRepository("member").find(); + let dataWithMappedIds = data.map((d) => ({ + ...d, + ...(!collectedIds + ? { + presences: d.presences.map((p: any) => ({ + ...p, + memberId: + members.find( + (m) => + m.firstname == p.member.firstname && + m.lastname == p.member.lastname && + m.nameaffix == p.member.nameaffix && + m.birthdate == p.member.birthdate && + m.internalId == p.member.internalId + )?.id ?? undefined, + member: null, + })), + } + : {}), + })); + await this.transactionManager.getRepository("protocol").save(dataWithMappedIds); + } + private static async setNewsletter(data: Array, collectedIds: boolean): Promise { + let queries = await this.transactionManager.getRepository("query").find(); + let members = await this.transactionManager.getRepository("member").find(); + let dataWithMappedIds = data.map((d) => ({ + ...d, + recipientsByQueryId: { + ...d.recipientsByQueryId, + id: queries.find((s) => s.query == d.recipientsByQueryId.query)?.id ?? undefined, + }, + ...(!collectedIds + ? { + recipients: d.recipients.map((r: any) => ({ + ...r, + memberId: + members.find( + (m) => + m.firstname == r.member.firstname && + m.lastname == r.member.lastname && + m.nameaffix == r.member.nameaffix && + m.birthdate == r.member.birthdate && + m.internalId == r.member.internalId + )?.id ?? undefined, + member: null, + })), + } + : {}), + })); + await this.transactionManager.getRepository("newsletter").save(dataWithMappedIds); + } + private static async setNewsletterConfig(data: Array): Promise { + let types = await this.transactionManager.getRepository("communication_type").find(); + let dataWithMappedIds = data.map((d) => ({ + ...d, + comType: { + ...d.comType, + id: types.find((type) => type.type == d.comType.type)?.id ?? undefined, + }, + })); + await this.transactionManager.getRepository("newsletter_config").save(dataWithMappedIds); + } + private static async setCalendar(data: { [key: string]: Array }): Promise { + await this.transactionManager.getRepository("calendar_type").save(data["calendar_type"]); + let types = await this.transactionManager.getRepository("calendar_type").find(); + let dataWithMappedIds = data["calendar"].map((c) => ({ + ...c, + type: { + ...c.type, + id: types.find((type) => type.type == c.type.type)?.id ?? undefined, + }, + })); + await this.transactionManager.getRepository("calendar").save(dataWithMappedIds); + } + private static async setQueryStore(data: Array): Promise { + await this.transactionManager.createQueryBuilder().insert().into("query").values(data).orIgnore().execute(); + } + private static async setTemplate(data: { [key: string]: Array }): Promise { + await this.transactionManager + .createQueryBuilder() + .insert() + .into("template") + .values(data["template"]) + .orIgnore() + .execute(); + await this.transactionManager + .createQueryBuilder() + .insert() + .into("template_usage") + .values(data["template_usage"]) + .orIgnore() + .execute(); + } + private static async setUser(data: { [key: string]: Array }): Promise { + await this.transactionManager.createQueryBuilder().insert().into("role").values(data["role"]).orIgnore().execute(); + let roles = await this.transactionManager.getRepository("role").find(); + let dataWithMappedIds = data["user"].map((u) => ({ + ...u, + roles: u.roles.map((r: any) => ({ + ...r, + id: roles.find((role) => role.role == r.role)?.id ?? undefined, + })), + })); + await this.transactionManager.getRepository("user").save(dataWithMappedIds); + await this.transactionManager + .createQueryBuilder() + .insert() + .into("invite") + .values(data["invite"]) + .orIgnore() + .execute(); + } + private static async setWebapi(data: Array): Promise { + await this.transactionManager.getRepository("webapi").save(data); + } +} diff --git a/src/index.ts b/src/index.ts index 68eef4f..f3999e9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,7 @@ import "dotenv/config"; import express from "express"; -import { configCheck, SERVER_PORT } from "./env.defaults"; +import { BACKUP_AUTO_RESTORE, configCheck, SERVER_PORT } from "./env.defaults"; configCheck(); import { PermissionObject } from "./type/permissionTypes"; @@ -19,18 +19,25 @@ declare global { } import { dataSource } from "./data-source"; -dataSource.initialize(); +import BackupHelper from "./helpers/backupHelper"; +dataSource.initialize().then(async () => { + if ((BACKUP_AUTO_RESTORE as "true" | "false") == "true") { + await BackupHelper.autoRestoreBackup().catch((err) => { + console.log(`${new Date().toISOString()}: failed auto-restoring database`); + }); + } +}); const app = express(); import router from "./routes/index"; router(app); app.listen(process.env.NODE_ENV ? SERVER_PORT : 5000, () => { - console.log(`listening on *:${process.env.NODE_ENV ? SERVER_PORT : 5000}`); + console.log(`${new Date().toISOString()}: listening on port ${process.env.NODE_ENV ? SERVER_PORT : 5000}`); }); import schedule from "node-schedule"; import RefreshCommandHandler from "./command/refreshCommandHandler"; const job = schedule.scheduleJob("0 0 * * *", async () => { - console.log(`running Cron at ${new Date()}`); + console.log(`${new Date().toISOString()}: running Cron`); await RefreshCommandHandler.deleteExpired(); }); -- 2.45.3 From 57013132282385e503f3d7b236128945ee610982 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Wed, 29 Jan 2025 18:10:41 +0100 Subject: [PATCH 08/22] restart schema with clean database --- src/data-source.ts | 108 +++----- src/index.ts | 2 +- src/migrations/1724317398939-initial.ts | 55 ---- .../1724573307851-refreshPrimaryChange.ts | 28 -- src/migrations/1724579024939-invite.ts | 42 --- src/migrations/1724661484664-permissions.ts | 45 --- .../1724771491085-role_permission.ts | 116 -------- .../1725435669492-member_base_data.ts | 109 -------- src/migrations/1726301836849-memberdata.ts | 258 ------------------ .../1727439800630-communicationFields.ts | 140 ---------- src/migrations/1728313041449-ownership.ts | 56 ---- src/migrations/1729347911107-protocol.ts | 203 -------------- src/migrations/1729947763295-calendar.ts | 68 ----- src/migrations/1732358596823-resetToken.ts | 24 -- src/migrations/1732696919191-SMSAlarming.ts | 21 -- .../1733249553766-securingCalendarType.ts | 20 -- src/migrations/1734187754677-queryStore.ts | 26 -- .../1734520998539-memberDataViews.ts | 112 -------- src/migrations/1734854680201-template.ts | 28 -- src/migrations/1734949173739-templateUsage.ts | 77 ------ src/migrations/1735118780511-newsletter.ts | 144 ---------- .../1735207446910-newsletterConfig.ts | 39 --- src/migrations/1735822722235-internalId.ts | 45 --- src/migrations/1735927918979-postalCode.ts | 22 -- .../1736072179716-protocolAbsent.ts | 21 -- src/migrations/1736079005086-memberlist.ts | 26 -- .../1736084198860-extendViewValues.ts | 111 -------- .../1736505324488-finishInternalIdTransfer.ts | 23 -- .../1737287798828-protocolPresenceExcuse.ts | 21 -- .../1737453096674-addwebapiTokens.ts | 55 ---- .../1737796878058-salutationAsTable.ts | 91 ------ src/migrations/1737800468938-updateViews.ts | 233 ---------------- .../1737816852011-moveSendNewsletterFlag.ts | 71 ----- src/migrations/1738057119384-UniqueFields.ts | 119 -------- .../1738166124200-BackupAndResetDatabase.ts | 53 ++++ 35 files changed, 95 insertions(+), 2517 deletions(-) delete mode 100644 src/migrations/1724317398939-initial.ts delete mode 100644 src/migrations/1724573307851-refreshPrimaryChange.ts delete mode 100644 src/migrations/1724579024939-invite.ts delete mode 100644 src/migrations/1724661484664-permissions.ts delete mode 100644 src/migrations/1724771491085-role_permission.ts delete mode 100644 src/migrations/1725435669492-member_base_data.ts delete mode 100644 src/migrations/1726301836849-memberdata.ts delete mode 100644 src/migrations/1727439800630-communicationFields.ts delete mode 100644 src/migrations/1728313041449-ownership.ts delete mode 100644 src/migrations/1729347911107-protocol.ts delete mode 100644 src/migrations/1729947763295-calendar.ts delete mode 100644 src/migrations/1732358596823-resetToken.ts delete mode 100644 src/migrations/1732696919191-SMSAlarming.ts delete mode 100644 src/migrations/1733249553766-securingCalendarType.ts delete mode 100644 src/migrations/1734187754677-queryStore.ts delete mode 100644 src/migrations/1734520998539-memberDataViews.ts delete mode 100644 src/migrations/1734854680201-template.ts delete mode 100644 src/migrations/1734949173739-templateUsage.ts delete mode 100644 src/migrations/1735118780511-newsletter.ts delete mode 100644 src/migrations/1735207446910-newsletterConfig.ts delete mode 100644 src/migrations/1735822722235-internalId.ts delete mode 100644 src/migrations/1735927918979-postalCode.ts delete mode 100644 src/migrations/1736072179716-protocolAbsent.ts delete mode 100644 src/migrations/1736079005086-memberlist.ts delete mode 100644 src/migrations/1736084198860-extendViewValues.ts delete mode 100644 src/migrations/1736505324488-finishInternalIdTransfer.ts delete mode 100644 src/migrations/1737287798828-protocolPresenceExcuse.ts delete mode 100644 src/migrations/1737453096674-addwebapiTokens.ts delete mode 100644 src/migrations/1737796878058-salutationAsTable.ts delete mode 100644 src/migrations/1737800468938-updateViews.ts delete mode 100644 src/migrations/1737816852011-moveSendNewsletterFlag.ts delete mode 100644 src/migrations/1738057119384-UniqueFields.ts create mode 100644 src/migrations/1738166124200-BackupAndResetDatabase.ts diff --git a/src/data-source.ts b/src/data-source.ts index 145da3f..ec98baf 100644 --- a/src/data-source.ts +++ b/src/data-source.ts @@ -16,66 +16,36 @@ import { executivePosition } from "./entity/settings/executivePosition"; import { membershipStatus } from "./entity/settings/membershipStatus"; import { qualification } from "./entity/settings/qualification"; -import { Initial1724317398939 } from "./migrations/1724317398939-initial"; -import { RefreshPrimaryChange1724573307851 } from "./migrations/1724573307851-refreshPrimaryChange"; -import { Invite1724579024939 } from "./migrations/1724579024939-invite"; -import { Permissions1724661484664 } from "./migrations/1724661484664-permissions"; -import { RolePermission1724771491085 } from "./migrations/1724771491085-role_permission"; -import { MemberBaseData1725435669492 } from "./migrations/1725435669492-member_base_data"; import { member } from "./entity/club/member/member"; import { memberAwards } from "./entity/club/member/memberAwards"; import { memberExecutivePositions } from "./entity/club/member/memberExecutivePositions"; import { memberQualifications } from "./entity/club/member/memberQualifications"; import { membership } from "./entity/club/member/membership"; -import { Memberdata1726301836849 } from "./migrations/1726301836849-memberdata"; -import { CommunicationFields1727439800630 } from "./migrations/1727439800630-communicationFields"; -import { Ownership1728313041449 } from "./migrations/1728313041449-ownership"; import { protocol } from "./entity/club/protocol/protocol"; import { protocolAgenda } from "./entity/club/protocol/protocolAgenda"; import { protocolDecision } from "./entity/club/protocol/protocolDecision"; import { protocolPresence } from "./entity/club/protocol/protocolPresence"; import { protocolVoting } from "./entity/club/protocol/protocolVoting"; import { protocolPrintout } from "./entity/club/protocol/protocolPrintout"; -import { Protocol1729347911107 } from "./migrations/1729347911107-protocol"; import { calendar } from "./entity/club/calendar"; import { calendarType } from "./entity/settings/calendarType"; -import { Calendar1729947763295 } from "./migrations/1729947763295-calendar"; import { reset } from "./entity/reset"; -import { ResetToken1732358596823 } from "./migrations/1732358596823-resetToken"; -import { SMSAlarming1732696919191 } from "./migrations/1732696919191-SMSAlarming"; -import { SecuringCalendarType1733249553766 } from "./migrations/1733249553766-securingCalendarType"; import { query } from "./entity/settings/query"; -import { QueryStore1734187754677 } from "./migrations/1734187754677-queryStore"; import { memberView } from "./views/memberView"; import { memberExecutivePositionsView } from "./views/memberExecutivePositionView"; import { memberQualificationsView } from "./views/memberQualificationsView"; import { membershipView } from "./views/membershipsView"; -import { MemberDataViews1734520998539 } from "./migrations/1734520998539-memberDataViews"; import { template } from "./entity/settings/template"; -import { Template1734854680201 } from "./migrations/1734854680201-template"; import { templateUsage } from "./entity/settings/templateUsage"; -import { TemplateUsage1734949173739 } from "./migrations/1734949173739-templateUsage"; import { newsletter } from "./entity/club/newsletter/newsletter"; import { newsletterDates } from "./entity/club/newsletter/newsletterDates"; import { newsletterRecipients } from "./entity/club/newsletter/newsletterRecipients"; -import { Newsletter1735118780511 } from "./migrations/1735118780511-newsletter"; import { newsletterConfig } from "./entity/settings/newsletterConfig"; -import { NewsletterConfig1735207446910 } from "./migrations/1735207446910-newsletterConfig"; -import { InternalId1735822722235 } from "./migrations/1735822722235-internalId"; -import { PostalCode1735927918979 } from "./migrations/1735927918979-postalCode"; -import { ProtocolAbsent1736072179716 } from "./migrations/1736072179716-protocolAbsent"; -import { Memberlist1736079005086 } from "./migrations/1736079005086-memberlist"; -import { ExtendViewValues1736084198860 } from "./migrations/1736084198860-extendViewValues"; -import { FinishInternalIdTransfer1736505324488 } from "./migrations/1736505324488-finishInternalIdTransfer"; -import { ProtocolPresenceExcuse1737287798828 } from "./migrations/1737287798828-protocolPresenceExcuse"; import { webapi } from "./entity/user/webapi"; import { webapiPermission } from "./entity/user/webapi_permission"; -import { AddWebapiTokens1737453096674 } from "./migrations/1737453096674-addwebapiTokens"; import { salutation } from "./entity/settings/salutation"; -import { SalutationAsTable1737796878058 } from "./migrations/1737796878058-salutationAsTable"; -import { UpdateViews1737800468938 } from "./migrations/1737800468938-updateViews"; -import { MoveSendNewsletterFlag1737816852011 } from "./migrations/1737816852011-moveSendNewsletterFlag"; -import { UniqueFields1738057119384 } from "./migrations/1738057119384-UniqueFields"; + +import { BackupAndResetDatabase1738166124200 } from "./migrations/1738166124200-BackupAndResetDatabase"; const dataSource = new DataSource({ type: DB_TYPE as any, @@ -129,43 +99,47 @@ const dataSource = new DataSource({ webapi, webapiPermission, ], - migrations: [ - Initial1724317398939, - RefreshPrimaryChange1724573307851, - Invite1724579024939, - Permissions1724661484664, - RolePermission1724771491085, - MemberBaseData1725435669492, - Memberdata1726301836849, - CommunicationFields1727439800630, - Ownership1728313041449, - Protocol1729347911107, - Calendar1729947763295, - ResetToken1732358596823, - SMSAlarming1732696919191, - SecuringCalendarType1733249553766, - QueryStore1734187754677, - MemberDataViews1734520998539, - Template1734854680201, - TemplateUsage1734949173739, - Newsletter1735118780511, - NewsletterConfig1735207446910, - InternalId1735822722235, - PostalCode1735927918979, - ProtocolAbsent1736072179716, - Memberlist1736079005086, - ExtendViewValues1736084198860, - FinishInternalIdTransfer1736505324488, - ProtocolPresenceExcuse1737287798828, - AddWebapiTokens1737453096674, - SalutationAsTable1737796878058, - UpdateViews1737800468938, - MoveSendNewsletterFlag1737816852011, - UniqueFields1738057119384, - ], + migrations: [BackupAndResetDatabase1738166124200], migrationsRun: true, migrationsTransactionMode: "each", subscribers: [], }); -export { dataSource }; +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 }; diff --git a/src/index.ts b/src/index.ts index f3999e9..a189059 100644 --- a/src/index.ts +++ b/src/index.ts @@ -21,7 +21,7 @@ declare global { import { dataSource } from "./data-source"; import BackupHelper from "./helpers/backupHelper"; dataSource.initialize().then(async () => { - if ((BACKUP_AUTO_RESTORE as "true" | "false") == "true") { + if ((BACKUP_AUTO_RESTORE as "true" | "false") == "true" && (await dataSource.createQueryRunner().hasTable("user"))) { await BackupHelper.autoRestoreBackup().catch((err) => { console.log(`${new Date().toISOString()}: failed auto-restoring database`); }); diff --git a/src/migrations/1724317398939-initial.ts b/src/migrations/1724317398939-initial.ts deleted file mode 100644 index bbce107..0000000 --- a/src/migrations/1724317398939-initial.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { MigrationInterface, QueryRunner, Table, TableForeignKey } from "typeorm"; -import { DB_TYPE } from "../env.defaults"; - -export class Initial1724317398939 implements MigrationInterface { - name = "Initial1724317398939"; - - public async up(queryRunner: QueryRunner): Promise { - const variableType_int = DB_TYPE == "mysql" ? "int" : "integer"; - - await queryRunner.createTable( - new Table({ - name: "user", - columns: [ - { name: "id", type: variableType_int, isPrimary: true, isGenerated: true, generationStrategy: "increment" }, - { name: "mail", type: "varchar", length: "255", isNullable: false }, - { name: "username", type: "varchar", length: "255", isNullable: false }, - { name: "secret", type: "varchar", length: "255", isNullable: false }, - ], - }), - true - ); - - await queryRunner.createTable( - new Table({ - name: "refresh", - columns: [ - { name: "id", type: variableType_int, isPrimary: true, isGenerated: true, generationStrategy: "increment" }, - { name: "token", type: "varchar", length: "255", isNullable: false }, - { name: "expiry", type: "datetime", isNullable: false }, - { name: "userId", type: variableType_int, isNullable: false }, - ], - }), - true - ); - - await queryRunner.createForeignKey( - "refresh", - new TableForeignKey({ - columnNames: ["userId"], - referencedColumnNames: ["id"], - referencedTableName: "user", - onDelete: "CASCADE", - onUpdate: "RESTRICT", - }) - ); - } - - public async down(queryRunner: QueryRunner): Promise { - const table = await queryRunner.getTable("refresh"); - const foreignKey = table.foreignKeys.find((fk) => fk.columnNames.indexOf("userId") !== -1); - await queryRunner.dropForeignKey("refresh", foreignKey); - await queryRunner.dropTable("refresh"); - await queryRunner.dropTable("user"); - } -} diff --git a/src/migrations/1724573307851-refreshPrimaryChange.ts b/src/migrations/1724573307851-refreshPrimaryChange.ts deleted file mode 100644 index 8da65b3..0000000 --- a/src/migrations/1724573307851-refreshPrimaryChange.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { MigrationInterface, QueryRunner, TableColumn } from "typeorm"; -import { DB_TYPE } from "../env.defaults"; - -export class RefreshPrimaryChange1724573307851 implements MigrationInterface { - name = "RefreshPrimaryChange1724573307851"; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.dropColumn("refresh", "id"); - await queryRunner.createPrimaryKey("refresh", ["token", "userId"]); - } - - public async down(queryRunner: QueryRunner): Promise { - const variableType_int = DB_TYPE == "mysql" ? "int" : "integer"; - - await queryRunner.dropPrimaryKey("refresh"); - await queryRunner.addColumn( - "refresh", - new TableColumn({ - name: "id", - type: variableType_int, - isPrimary: true, - isNullable: false, - isGenerated: true, - generationStrategy: "increment", - }) - ); - } -} diff --git a/src/migrations/1724579024939-invite.ts b/src/migrations/1724579024939-invite.ts deleted file mode 100644 index 03309ac..0000000 --- a/src/migrations/1724579024939-invite.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { MigrationInterface, QueryRunner, Table, TableColumn } from "typeorm"; - -export class Invite1724579024939 implements MigrationInterface { - name = "Invite1724579024939"; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.createTable( - new Table({ - name: "invite", - columns: [ - { name: "mail", type: "varchar", length: "255", isPrimary: true, isNullable: false }, - { name: "token", type: "varchar", length: "255", isNullable: false }, - { name: "username", type: "varchar", length: "255", isNullable: false }, - { name: "firstname", type: "varchar", length: "255", isNullable: false }, - { name: "lastname", type: "varchar", length: "255", isNullable: false }, - { name: "secret", type: "varchar", length: "255", isNullable: false }, - ], - }), - true - ); - - await queryRunner.addColumns("user", [ - new TableColumn({ - name: "firstname", - type: "varchar", - length: "255", - isNullable: false, - }), - new TableColumn({ - name: "lastname", - type: "varchar", - length: "255", - isNullable: false, - }), - ]); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.dropColumns("user", ["lastname", "firstname"]); - await queryRunner.dropTable("invite"); - } -} diff --git a/src/migrations/1724661484664-permissions.ts b/src/migrations/1724661484664-permissions.ts deleted file mode 100644 index f14fe3f..0000000 --- a/src/migrations/1724661484664-permissions.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { MigrationInterface, QueryRunner, Table, TableForeignKey } from "typeorm"; -import { DB_TYPE } from "../env.defaults"; - -export class Permissions1724661484664 implements MigrationInterface { - name = "Permissions1724661484664"; - - public async up(queryRunner: QueryRunner): Promise { - const variableType_int = DB_TYPE == "mysql" ? "int" : "integer"; - - await queryRunner.createTable( - new Table({ - name: "permission", - columns: [ - { - name: "permission", - type: "varchar", - length: "255", - isPrimary: true, - isNullable: false, - }, - { name: "userId", type: variableType_int, isPrimary: true, isNullable: false }, - ], - }), - true - ); - - await queryRunner.createForeignKey( - "permission", - new TableForeignKey({ - columnNames: ["userId"], - referencedColumnNames: ["id"], - referencedTableName: "user", - onDelete: "CASCADE", - onUpdate: "RESTRICT", - }) - ); - } - - public async down(queryRunner: QueryRunner): Promise { - const table = await queryRunner.getTable("permission"); - const foreignKey = table.foreignKeys.find((fk) => fk.columnNames.indexOf("userId") !== -1); - await queryRunner.dropForeignKey("permission", foreignKey); - await queryRunner.dropTable("permission"); - } -} diff --git a/src/migrations/1724771491085-role_permission.ts b/src/migrations/1724771491085-role_permission.ts deleted file mode 100644 index dc8df18..0000000 --- a/src/migrations/1724771491085-role_permission.ts +++ /dev/null @@ -1,116 +0,0 @@ -import { MigrationInterface, QueryRunner, Table, TableForeignKey, TableIndex } from "typeorm"; -import { DB_TYPE } from "../env.defaults"; - -export class RolePermission1724771491085 implements MigrationInterface { - name = "RolePermission1724771491085"; - - public async up(queryRunner: QueryRunner): Promise { - const variableType_int = DB_TYPE == "mysql" ? "int" : "integer"; - - await queryRunner.createTable( - new Table({ - name: "role", - columns: [ - { name: "id", type: variableType_int, isPrimary: true, isGenerated: true, generationStrategy: "increment" }, - { name: "role", type: "varchar", length: "255", isNullable: false }, - ], - }), - true - ); - - await queryRunner.createTable( - new Table({ - name: "role_permission", - columns: [ - { - name: "permission", - type: "varchar", - length: "255", - isPrimary: true, - isNullable: false, - }, - { name: "roleId", type: variableType_int, isPrimary: true, isNullable: false }, - ], - }), - true - ); - - await queryRunner.renameTable("permission", "user_permission"); - - await queryRunner.createTable( - new Table({ - name: "user_roles", - columns: [ - { name: "userId", type: variableType_int, isPrimary: true, isNullable: false }, - { name: "roleId", type: variableType_int, isPrimary: true, isNullable: false }, - ], - }), - true - ); - - await queryRunner.createForeignKey( - "role_permission", - new TableForeignKey({ - columnNames: ["roleId"], - referencedColumnNames: ["id"], - referencedTableName: "role", - onDelete: "CASCADE", - onUpdate: "RESTRICT", - }) - ); - - await queryRunner.createForeignKey( - "user_roles", - new TableForeignKey({ - columnNames: ["userId"], - referencedColumnNames: ["id"], - referencedTableName: "user", - onDelete: "CASCADE", - onUpdate: "RESTRICT", - }) - ); - - await queryRunner.createForeignKey( - "user_roles", - new TableForeignKey({ - columnNames: ["roleId"], - referencedColumnNames: ["id"], - referencedTableName: "role", - onDelete: "CASCADE", - onUpdate: "RESTRICT", - }) - ); - - await queryRunner.createIndex( - "user_roles", - new TableIndex({ - columnNames: ["userId"], - }) - ); - - await queryRunner.createIndex( - "user_roles", - new TableIndex({ - columnNames: ["roleId"], - }) - ); - } - - public async down(queryRunner: QueryRunner): Promise { - const user_roles = await queryRunner.getTable("user_roles"); - const roles_foreignKey = user_roles.foreignKeys.find((fk) => fk.columnNames.indexOf("roleId") !== -1); - const user_foreignKey = user_roles.foreignKeys.find((fk) => fk.columnNames.indexOf("userId") !== -1); - await queryRunner.dropForeignKey("user_roles", roles_foreignKey); - await queryRunner.dropForeignKey("user_roles", user_foreignKey); - await queryRunner.dropTable("user_roles"); - - const role_permission = await queryRunner.getTable("role_permission"); - const permission_foreignKey = role_permission.foreignKeys.find((fk) => fk.columnNames.indexOf("roleId") !== -1); - await queryRunner.dropForeignKey("role_permission", permission_foreignKey); - await queryRunner.dropTable("role_permission"); - - await queryRunner.dropTable("role"); - - await queryRunner.renameTable("user_permission", "permission"); - } -} diff --git a/src/migrations/1725435669492-member_base_data.ts b/src/migrations/1725435669492-member_base_data.ts deleted file mode 100644 index ba4f870..0000000 --- a/src/migrations/1725435669492-member_base_data.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { MigrationInterface, QueryRunner, Table, TableForeignKey } from "typeorm"; -import { DB_TYPE } from "../env.defaults"; - -export class MemberBaseData1725435669492 implements MigrationInterface { - name = "MemberBaseData1725435669492"; - - public async up(queryRunner: QueryRunner): Promise { - const variableType_int = DB_TYPE == "mysql" ? "int" : "integer"; - - await queryRunner.createTable( - new Table({ - name: "award", - columns: [ - { name: "id", type: variableType_int, isPrimary: true, isGenerated: true, generationStrategy: "increment" }, - { name: "award", type: "varchar", length: "255", isNullable: false }, - ], - }), - true - ); - - await queryRunner.createTable( - new Table({ - name: "communication_type", - columns: [ - { name: "id", type: variableType_int, isPrimary: true, isGenerated: true, generationStrategy: "increment" }, - { name: "type", type: "varchar", length: "255", isNullable: false }, - { name: "useColumns", type: "varchar", length: "255", isNullable: false, default: "''" }, - ], - }), - true - ); - - await queryRunner.createTable( - new Table({ - name: "communication", - columns: [ - { name: "id", type: variableType_int, isPrimary: true, isGenerated: true, generationStrategy: "increment" }, - { name: "preferred", type: "tinyint", isNullable: false, default: 0 }, - { name: "mobile", type: "varchar", length: "255", isNullable: false }, - { name: "email", type: "varchar", length: "255", isNullable: false }, - { name: "city", type: "varchar", length: "255", isNullable: false }, - { name: "street", type: "varchar", length: "255", isNullable: false }, - { name: "streetNumber", type: variableType_int, isNullable: false }, - { name: "streetNumberAddition", type: "varchar", length: "255", isNullable: false }, - { name: "typeId", type: variableType_int, isNullable: false }, - ], - }), - true - ); - - await queryRunner.createTable( - new Table({ - name: "executive_position", - columns: [ - { name: "id", type: variableType_int, isPrimary: true, isGenerated: true, generationStrategy: "increment" }, - { name: "position", type: "varchar", length: "255", isNullable: false }, - ], - }), - true - ); - - await queryRunner.createTable( - new Table({ - name: "membership_status", - columns: [ - { name: "id", type: variableType_int, isPrimary: true, isGenerated: true, generationStrategy: "increment" }, - { name: "status", type: "varchar", length: "255", isNullable: false }, - ], - }), - true - ); - - await queryRunner.createTable( - new Table({ - name: "qualification", - columns: [ - { name: "id", type: variableType_int, isPrimary: true, isGenerated: true, generationStrategy: "increment" }, - { name: "qualification", type: "varchar", length: "255", isNullable: false }, - { name: "description", type: "varchar", length: "255", isNullable: true }, - ], - }), - true - ); - - await queryRunner.createForeignKey( - "communication", - new TableForeignKey({ - columnNames: ["typeId"], - referencedColumnNames: ["id"], - referencedTableName: "communication_type", - onDelete: "RESTRICT", - onUpdate: "RESTRICT", - }) - ); - } - - public async down(queryRunner: QueryRunner): Promise { - const communication = await queryRunner.getTable("communication"); - const foreignKey = communication.foreignKeys.find((fk) => fk.columnNames.indexOf("typeId") !== -1); - await queryRunner.dropForeignKey("communication", foreignKey); - - await queryRunner.dropTable("qualification"); - await queryRunner.dropTable("membership_status"); - await queryRunner.dropTable("executive_position"); - await queryRunner.dropTable("communication"); - await queryRunner.dropTable("communication_type"); - await queryRunner.dropTable("award"); - } -} diff --git a/src/migrations/1726301836849-memberdata.ts b/src/migrations/1726301836849-memberdata.ts deleted file mode 100644 index e49043c..0000000 --- a/src/migrations/1726301836849-memberdata.ts +++ /dev/null @@ -1,258 +0,0 @@ -import { MigrationInterface, QueryRunner, Table, TableColumn, TableForeignKey } from "typeorm"; -import { DB_TYPE } from "../env.defaults"; - -export class Memberdata1726301836849 implements MigrationInterface { - name = "Memberdata1726301836849"; - - public async up(queryRunner: QueryRunner): Promise { - const variableType_int = DB_TYPE == "mysql" ? "int" : "integer"; - - await queryRunner.createTable( - new Table({ - name: "membership", - columns: [ - { name: "id", type: variableType_int, isPrimary: true, isGenerated: true, generationStrategy: "increment" }, - { name: "internalId", type: "varchar", length: "255", isNullable: true }, - { name: "start", type: "date", isNullable: false }, - { name: "end", type: "date", isNullable: true }, - { name: "terminationReason", type: "varchar", length: "255", isNullable: true }, - { name: "memberId", type: variableType_int, isNullable: false }, - { name: "statusId", type: variableType_int, isNullable: false }, - ], - uniques: [{ name: "IDX_703f499fe3a9892e3a8790cdfc", columnNames: ["internalId"] }], - }), - true - ); - - await queryRunner.createTable( - new Table({ - name: "member_qualifications", - columns: [ - { name: "id", type: variableType_int, isPrimary: true, isGenerated: true, generationStrategy: "increment" }, - { name: "note", type: "varchar", length: "255", isNullable: true }, - { name: "start", type: "date", isNullable: false }, - { name: "end", type: "date", isNullable: true }, - { name: "terminationReason", type: "varchar", length: "255", isNullable: true }, - { name: "memberId", type: variableType_int, isNullable: false }, - { name: "qualificationId", type: variableType_int, isNullable: false }, - ], - }), - true - ); - - await queryRunner.createTable( - new Table({ - name: "member_executive_positions", - columns: [ - { name: "id", type: variableType_int, isPrimary: true, isGenerated: true, generationStrategy: "increment" }, - { name: "note", type: "varchar", length: "255", isNullable: true }, - { name: "start", type: "date", isNullable: false }, - { name: "end", type: "date", isNullable: true }, - { name: "memberId", type: variableType_int, isNullable: false }, - { name: "executivePositionId", type: variableType_int, isNullable: false }, - ], - }), - true - ); - - await queryRunner.createTable( - new Table({ - name: "member", - columns: [ - { name: "id", type: variableType_int, isPrimary: true, isGenerated: true, generationStrategy: "increment" }, - { name: "salutation", type: "varchar", length: "255", default: "'none'", isNullable: false }, - { name: "firstname", type: "varchar", length: "255", isNullable: false }, - { name: "lastname", type: "varchar", length: "255", isNullable: false }, - { name: "nameaffix", type: "varchar", length: "255", isNullable: false }, - { name: "birthdate", type: "date", isNullable: false }, - { name: "sendNewsletterId", type: variableType_int, isNullable: true }, - ], - uniques: [{ name: "REL_d57e160c4513cd949159217281", columnNames: ["sendNewsletterId"] }], - }), - true - ); - - await queryRunner.createTable( - new Table({ - name: "member_awards", - columns: [ - { name: "id", type: variableType_int, isPrimary: true, isGenerated: true, generationStrategy: "increment" }, - { name: "given", type: "tinyint", default: 1, isNullable: false }, - { name: "note", type: "varchar", length: "255", isNullable: true }, - { name: "date", type: "date", isNullable: false }, - { name: "memberId", type: variableType_int, isNullable: false }, - { name: "awardId", type: variableType_int, isNullable: false }, - ], - }), - true - ); - - await queryRunner.addColumn( - "communication", - new TableColumn({ name: "memberId", type: variableType_int, isNullable: false }) - ); - - await queryRunner.createForeignKey( - "membership", - new TableForeignKey({ - columnNames: ["memberId"], - referencedTableName: "member", - referencedColumnNames: ["id"], - onDelete: "CASCADE", - onUpdate: "RESTRICT", - }) - ); - - await queryRunner.createForeignKey( - "membership", - new TableForeignKey({ - columnNames: ["statusId"], - referencedTableName: "membership_status", - referencedColumnNames: ["id"], - onDelete: "RESTRICT", - onUpdate: "RESTRICT", - }) - ); - - await queryRunner.createForeignKey( - "member_qualifications", - new TableForeignKey({ - columnNames: ["memberId"], - referencedTableName: "member", - referencedColumnNames: ["id"], - onDelete: "CASCADE", - onUpdate: "RESTRICT", - }) - ); - - await queryRunner.createForeignKey( - "member_qualifications", - new TableForeignKey({ - columnNames: ["qualificationId"], - referencedTableName: "qualification", - referencedColumnNames: ["id"], - onDelete: "RESTRICT", - onUpdate: "RESTRICT", - }) - ); - - await queryRunner.createForeignKey( - "member_executive_positions", - new TableForeignKey({ - columnNames: ["memberId"], - referencedTableName: "member", - referencedColumnNames: ["id"], - onDelete: "CASCADE", - onUpdate: "RESTRICT", - }) - ); - - await queryRunner.createForeignKey( - "member_executive_positions", - new TableForeignKey({ - columnNames: ["executivePositionId"], - referencedTableName: "executive_position", - referencedColumnNames: ["id"], - onDelete: "RESTRICT", - onUpdate: "RESTRICT", - }) - ); - - await queryRunner.createForeignKey( - "communication", - new TableForeignKey({ - columnNames: ["memberId"], - referencedTableName: "member", - referencedColumnNames: ["id"], - onDelete: "CASCADE", - onUpdate: "RESTRICT", - }) - ); - - await queryRunner.createForeignKey( - "member", - new TableForeignKey({ - columnNames: ["sendNewsletterId"], - referencedTableName: "communication", - referencedColumnNames: ["id"], - onDelete: "SET NULL", - onUpdate: "RESTRICT", - }) - ); - - await queryRunner.createForeignKey( - "member_awards", - new TableForeignKey({ - columnNames: ["memberId"], - referencedTableName: "member", - referencedColumnNames: ["id"], - onDelete: "CASCADE", - onUpdate: "RESTRICT", - }) - ); - - await queryRunner.createForeignKey( - "member_awards", - new TableForeignKey({ - columnNames: ["awardId"], - referencedTableName: "award", - referencedColumnNames: ["id"], - onDelete: "RESTRICT", - onUpdate: "RESTRICT", - }) - ); - } - - public async down(queryRunner: QueryRunner): Promise { - const member = await queryRunner.getTable("member"); - const memeberForeignKey = member.foreignKeys.find((fk) => fk.columnNames.indexOf("sendNewsletterId") !== -1); - await queryRunner.dropForeignKey("member", memeberForeignKey); - - const member_awards = await queryRunner.getTable("member_awards"); - const memeberAwardsForeignKeyMember = member_awards.foreignKeys.find( - (fk) => fk.columnNames.indexOf("memberId") !== -1 - ); - const memeberAwardsForeignKeyAward = member_awards.foreignKeys.find( - (fk) => fk.columnNames.indexOf("awardId") !== -1 - ); - await queryRunner.dropForeignKey("member_awards", memeberAwardsForeignKeyMember); - await queryRunner.dropForeignKey("member_awards", memeberAwardsForeignKeyAward); - - const communication = await queryRunner.getTable("communication"); - const communicationForeignKey = communication.foreignKeys.find((fk) => fk.columnNames.indexOf("memberId") !== -1); - await queryRunner.dropForeignKey("communication", communicationForeignKey); - - const member_executive_positions = await queryRunner.getTable("member_executive_positions"); - const memeberExecutivePositionForeignKeyMember = member_executive_positions.foreignKeys.find( - (fk) => fk.columnNames.indexOf("memberId") !== -1 - ); - const memeberExecutivePositionForeignKeyPosition = member_executive_positions.foreignKeys.find( - (fk) => fk.columnNames.indexOf("executivePositionId") !== -1 - ); - await queryRunner.dropForeignKey("member_executive_positions", memeberExecutivePositionForeignKeyMember); - await queryRunner.dropForeignKey("member_executive_positions", memeberExecutivePositionForeignKeyPosition); - - const member_qualifications = await queryRunner.getTable("member_qualifications"); - const memeberQualificationForeignKeyMember = member_qualifications.foreignKeys.find( - (fk) => fk.columnNames.indexOf("memberId") !== -1 - ); - const memeberQualificationForeignKeyQualification = member_qualifications.foreignKeys.find( - (fk) => fk.columnNames.indexOf("qualificationId") !== -1 - ); - await queryRunner.dropForeignKey("member_qualifications", memeberQualificationForeignKeyMember); - await queryRunner.dropForeignKey("member_qualifications", memeberQualificationForeignKeyQualification); - - const membership = await queryRunner.getTable("membership"); - const membershipForeignKeyMember = membership.foreignKeys.find((fk) => fk.columnNames.indexOf("memberId") !== -1); - const membershipForeignKeyStatus = membership.foreignKeys.find((fk) => fk.columnNames.indexOf("statusId") !== -1); - await queryRunner.dropForeignKey("membership", membershipForeignKeyMember); - await queryRunner.dropForeignKey("membership", membershipForeignKeyStatus); - - await queryRunner.dropColumn("communication", "memberId"); - await queryRunner.dropTable("member_awards"); - await queryRunner.dropTable("member"); - await queryRunner.dropTable("member_executive_positions"); - await queryRunner.dropTable("member_qualifications"); - await queryRunner.dropTable("membership"); - } -} diff --git a/src/migrations/1727439800630-communicationFields.ts b/src/migrations/1727439800630-communicationFields.ts deleted file mode 100644 index 8901579..0000000 --- a/src/migrations/1727439800630-communicationFields.ts +++ /dev/null @@ -1,140 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; -import { TableColumn } from "typeorm"; - -export class CommunicationFields1727439800630 implements MigrationInterface { - name = "CommunicationFields1727439800630"; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.changeColumn( - "communication", - "mobile", - new TableColumn({ - name: "mobile", - type: "varchar", - length: "255", - isNullable: true, - }) - ); - - await queryRunner.changeColumn( - "communication", - "email", - new TableColumn({ - name: "email", - type: "varchar", - length: "255", - isNullable: true, - }) - ); - - await queryRunner.changeColumn( - "communication", - "city", - new TableColumn({ - name: "city", - type: "varchar", - length: "255", - isNullable: true, - }) - ); - - await queryRunner.changeColumn( - "communication", - "street", - new TableColumn({ - name: "street", - type: "varchar", - length: "255", - isNullable: true, - }) - ); - - await queryRunner.changeColumn( - "communication", - "streetNumber", - new TableColumn({ - name: "streetNumber", - type: "int", - isNullable: true, - }) - ); - - await queryRunner.changeColumn( - "communication", - "streetNumberAddition", - new TableColumn({ - name: "streetNumberAddition", - type: "varchar", - length: "255", - isNullable: true, - }) - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.changeColumn( - "communication", - "mobile", - new TableColumn({ - name: "mobile", - type: "varchar", - length: "255", - isNullable: false, - }) - ); - - await queryRunner.changeColumn( - "communication", - "email", - new TableColumn({ - name: "email", - type: "varchar", - length: "255", - isNullable: false, - }) - ); - - await queryRunner.changeColumn( - "communication", - "city", - new TableColumn({ - name: "city", - type: "varchar", - length: "255", - isNullable: false, - }) - ); - - await queryRunner.changeColumn( - "communication", - "street", - new TableColumn({ - name: "street", - type: "varchar", - length: "255", - isNullable: false, - }) - ); - - await queryRunner.changeColumn( - "communication", - "streetNumber", - new TableColumn({ - name: "streetNumber", - type: "int", - isNullable: false, - }) - ); - - await queryRunner.changeColumn( - "communication", - "streetNumberAddition", - new TableColumn({ - name: "streetNumberAddition", - type: "varchar", - length: "255", - isNullable: false, - }) - ); - } -} diff --git a/src/migrations/1728313041449-ownership.ts b/src/migrations/1728313041449-ownership.ts deleted file mode 100644 index bf744a2..0000000 --- a/src/migrations/1728313041449-ownership.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { MigrationInterface, QueryRunner, TableColumn } from "typeorm"; - -export class Ownership1728313041449 implements MigrationInterface { - name = "Ownership1728313041449"; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.addColumn( - "user", - new TableColumn({ - name: "isOwner", - type: "tinyint", - default: 0, - isNullable: false, - }) - ); - - await queryRunner.manager - .createQueryBuilder() - .update("user") - .set({ isOwner: 1 }) - .where((qb) => { - const subQuery = queryRunner.manager - .createQueryBuilder() - .select("1") - .from("user_permission", "up") - .where("user.id = up.userId") - .andWhere("up.permission = '*'") - .getQuery(); - return `EXISTS (${subQuery})`; - }) - .execute(); - - await queryRunner.manager.createQueryBuilder().delete().from("user_permission").where("permission = '*'").execute(); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.manager - .createQueryBuilder() - .insert() - .into("user_permission") - .values( - await queryRunner.manager - .createQueryBuilder() - .select("user.id", "userId") - .addSelect("'*'", "permission") - .from("user", "user") - .where("user.isOwner = 1") - .execute() - ) - .execute(); - - await queryRunner.manager.createQueryBuilder().update("user").set({ isOwner: 0 }).where("isOwner = 1").execute(); - - await queryRunner.dropColumn("user", "isOwner"); - } -} diff --git a/src/migrations/1729347911107-protocol.ts b/src/migrations/1729347911107-protocol.ts deleted file mode 100644 index 57c51cf..0000000 --- a/src/migrations/1729347911107-protocol.ts +++ /dev/null @@ -1,203 +0,0 @@ -import { MigrationInterface, QueryRunner, Table, TableForeignKey } from "typeorm"; -import { DB_TYPE } from "../env.defaults"; - -export class Protocol1729347911107 implements MigrationInterface { - name = "Protocol1729347911107"; - - public async up(queryRunner: QueryRunner): Promise { - const variableType_int = DB_TYPE == "mysql" ? "int" : "integer"; - - await queryRunner.createTable( - new Table({ - name: "protocol", - columns: [ - { name: "id", type: variableType_int, isPrimary: true, isGenerated: true, generationStrategy: "increment" }, - { name: "title", type: "varchar", length: "255", isNullable: false }, - { name: "date", type: "date", isNullable: false }, - { name: "starttime", type: "time", isNullable: true }, - { name: "endtime", type: "time", isNullable: true }, - { name: "summary", type: "text", isNullable: true }, - ], - }), - true - ); - - await queryRunner.createTable( - new Table({ - name: "protocol_agenda", - columns: [ - { name: "id", type: variableType_int, isPrimary: true, isGenerated: true, generationStrategy: "increment" }, - { name: "topic", type: "varchar", length: "255", isNullable: false }, - { name: "context", type: "text", default: "''", isNullable: false }, - { name: "protocolId", type: variableType_int, isNullable: false }, - ], - }), - true - ); - - await queryRunner.createTable( - new Table({ - name: "protocol_decision", - columns: [ - { name: "id", type: variableType_int, isPrimary: true, isGenerated: true, generationStrategy: "increment" }, - { name: "topic", type: "varchar", length: "255", isNullable: false }, - { name: "context", type: "text", default: "''", isNullable: false }, - { name: "protocolId", type: variableType_int, isNullable: false }, - ], - }), - true - ); - - await queryRunner.createTable( - new Table({ - name: "protocol_presence", - columns: [ - { name: "memberId", type: variableType_int, isPrimary: true, isNullable: false }, - { name: "protocolId", type: variableType_int, isPrimary: true, isNullable: false }, - ], - }), - true - ); - - await queryRunner.createTable( - new Table({ - name: "protocol_voting", - columns: [ - { name: "id", type: variableType_int, isPrimary: true, isGenerated: true, generationStrategy: "increment" }, - { name: "topic", type: "varchar", length: "255", isNullable: false }, - { name: "context", type: "text", default: "''", isNullable: false }, - { name: "favour", type: variableType_int, default: 0, isNullable: false }, - { name: "abstain", type: variableType_int, default: 0, isNullable: false }, - { name: "against", type: variableType_int, default: 0, isNullable: false }, - { name: "protocolId", type: variableType_int, isNullable: false }, - ], - }), - true - ); - - await queryRunner.createTable( - new Table({ - name: "protocol_printout", - columns: [ - { name: "id", type: variableType_int, isPrimary: true, isGenerated: true, generationStrategy: "increment" }, - { name: "title", type: "varchar", length: "255", isNullable: false }, - { name: "iteration", type: variableType_int, default: 1, isNullable: false }, - { name: "filename", type: "varchar", length: "255", isNullable: false }, - { name: "createdAt", type: "datetime(6)", isNullable: false, default: "CURRENT_TIMESTAMP(6)" }, - { name: "protocolId", type: variableType_int, isNullable: false }, - ], - }), - true - ); - - await queryRunner.createForeignKey( - "protocol_agenda", - new TableForeignKey({ - columnNames: ["protocolId"], - referencedColumnNames: ["id"], - referencedTableName: "protocol", - onDelete: "CASCADE", - onUpdate: "RESTRICT", - }) - ); - - await queryRunner.createForeignKey( - "protocol_decision", - new TableForeignKey({ - columnNames: ["protocolId"], - referencedColumnNames: ["id"], - referencedTableName: "protocol", - onDelete: "CASCADE", - onUpdate: "RESTRICT", - }) - ); - - await queryRunner.createForeignKey( - "protocol_voting", - new TableForeignKey({ - columnNames: ["protocolId"], - referencedColumnNames: ["id"], - referencedTableName: "protocol", - onDelete: "CASCADE", - onUpdate: "RESTRICT", - }) - ); - - await queryRunner.createForeignKey( - "protocol_presence", - new TableForeignKey({ - columnNames: ["protocolId"], - referencedColumnNames: ["id"], - referencedTableName: "protocol", - onDelete: "CASCADE", - onUpdate: "RESTRICT", - }) - ); - await queryRunner.createForeignKey( - "protocol_presence", - new TableForeignKey({ - columnNames: ["memberId"], - referencedColumnNames: ["id"], - referencedTableName: "member", - onDelete: "CASCADE", - onUpdate: "RESTRICT", - }) - ); - - await queryRunner.createForeignKey( - "protocol_printout", - new TableForeignKey({ - columnNames: ["protocolId"], - referencedColumnNames: ["id"], - referencedTableName: "protocol", - onDelete: "CASCADE", - onUpdate: "RESTRICT", - }) - ); - } - - public async down(queryRunner: QueryRunner): Promise { - const tableProtocolVotings = await queryRunner.getTable("protocol_voting"); - const foreignKeyProtocolVotings = tableProtocolVotings.foreignKeys.find( - (fk) => fk.columnNames.indexOf("protocolId") !== -1 - ); - await queryRunner.dropForeignKey("protocol_voting", foreignKeyProtocolVotings); - - const tableProtocolDecisions = await queryRunner.getTable("protocol_decision"); - const foreignKeyProtocolDecisions = tableProtocolDecisions.foreignKeys.find( - (fk) => fk.columnNames.indexOf("protocolId") !== -1 - ); - await queryRunner.dropForeignKey("protocol_decision", foreignKeyProtocolDecisions); - - const tableProtocolAgenda = await queryRunner.getTable("protocol_agenda"); - const foreignKeyProtocolAgenda = tableProtocolAgenda.foreignKeys.find( - (fk) => fk.columnNames.indexOf("protocolId") !== -1 - ); - await queryRunner.dropForeignKey("protocol_agenda", foreignKeyProtocolAgenda); - - const tableProtocolPresence_protcol = await queryRunner.getTable("protocol_presence"); - const foreignKeyProtocolPresence_protcol = tableProtocolPresence_protcol.foreignKeys.find( - (fk) => fk.columnNames.indexOf("protocolId") !== -1 - ); - await queryRunner.dropForeignKey("protocol_presence", foreignKeyProtocolPresence_protcol); - - const tableProtocolPresence_member = await queryRunner.getTable("protocol_presence"); - const foreignKeyProtocolPresence_member = tableProtocolPresence_member.foreignKeys.find( - (fk) => fk.columnNames.indexOf("memberId") !== -1 - ); - await queryRunner.dropForeignKey("protocol_presence", foreignKeyProtocolPresence_member); - - const tableProtocolPrintout = await queryRunner.getTable("protocol_printout"); - const foreignKeyProtocolPrintout = tableProtocolPrintout.foreignKeys.find( - (fk) => fk.columnNames.indexOf("protocolId") !== -1 - ); - await queryRunner.dropForeignKey("protocol_printout", foreignKeyProtocolPrintout); - - await queryRunner.dropTable("protocol_printout"); - await queryRunner.dropTable("protocol_voting"); - await queryRunner.dropTable("protocol_presence"); - await queryRunner.dropTable("protocol_decision"); - await queryRunner.dropTable("protocol_agenda"); - await queryRunner.dropTable("protocol"); - } -} diff --git a/src/migrations/1729947763295-calendar.ts b/src/migrations/1729947763295-calendar.ts deleted file mode 100644 index bd30062..0000000 --- a/src/migrations/1729947763295-calendar.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { MigrationInterface, QueryRunner, Table, TableForeignKey } from "typeorm"; -import { DB_TYPE } from "../env.defaults"; - -export class Calendar1729947763295 implements MigrationInterface { - name = "Calendar1729947763295"; - - public async up(queryRunner: QueryRunner): Promise { - const variableType_int = DB_TYPE == "mysql" ? "int" : "integer"; - - await queryRunner.createTable( - new Table({ - name: "calendar_type", - columns: [ - { name: "id", type: variableType_int, isPrimary: true, isGenerated: true, generationStrategy: "increment" }, - { name: "type", type: "varchar", length: "255", isNullable: false }, - { name: "nscdr", type: "tinyint", isNullable: false }, - { name: "color", type: "varchar", length: "255", isNullable: false }, - ], - }) - ); - - await queryRunner.createTable( - new Table({ - name: "calendar", - columns: [ - { name: "id", type: "varchar", length: "36", isPrimary: true, isGenerated: true, generationStrategy: "uuid" }, - { name: "starttime", type: "datetime", isNullable: false }, - { name: "endtime", type: "datetime", isNullable: false }, - { name: "title", type: "varchar", length: "255", isNullable: false }, - { name: "content", type: "text", isNullable: true }, - { name: "allDay", type: "tinyint", isNullable: false, default: 0 }, - { name: "location", type: "text", isNullable: true }, - { name: "sequence", type: variableType_int, default: 1 }, - { name: "createdAt", type: "datetime", precision: 6, isNullable: false, default: "CURRENT_TIMESTAMP(6)" }, - { - name: "updatedAt", - type: "datetime", - precision: 6, - isNullable: false, - default: "CURRENT_TIMESTAMP(6)", - onUpdate: "CURRENT_TIMESTAMP(6)", - }, - { name: "typeId", type: variableType_int, isNullable: false }, - ], - }) - ); - - await queryRunner.createForeignKey( - "calendar", - new TableForeignKey({ - columnNames: ["typeId"], - referencedColumnNames: ["id"], - referencedTableName: "calendar_type", - onDelete: "RESTRICT", - onUpdate: "RESTRICT", - }) - ); - } - - public async down(queryRunner: QueryRunner): Promise { - const table = await queryRunner.getTable("calendar"); - const foreignKey = table.foreignKeys.find((fk) => fk.columnNames.indexOf("typeId") !== -1); - await queryRunner.dropForeignKey("calendar", foreignKey); - - await queryRunner.dropTable("calendar"); - await queryRunner.dropTable("calendar_type"); - } -} diff --git a/src/migrations/1732358596823-resetToken.ts b/src/migrations/1732358596823-resetToken.ts deleted file mode 100644 index ba4b4d8..0000000 --- a/src/migrations/1732358596823-resetToken.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { MigrationInterface, QueryRunner, Table } from "typeorm"; - -export class ResetToken1732358596823 implements MigrationInterface { - name = "ResetToken1732358596823"; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.createTable( - new Table({ - name: "reset", - columns: [ - { name: "mail", type: "varchar", length: "255", isPrimary: true, isNullable: false }, - { name: "token", type: "varchar", length: "255", isNullable: false }, - { name: "username", type: "varchar", length: "255", isNullable: false }, - { name: "secret", type: "varchar", length: "255", isNullable: false }, - ], - }), - true - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.dropTable("reset"); - } -} diff --git a/src/migrations/1732696919191-SMSAlarming.ts b/src/migrations/1732696919191-SMSAlarming.ts deleted file mode 100644 index 2ef756e..0000000 --- a/src/migrations/1732696919191-SMSAlarming.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { MigrationInterface, QueryRunner, TableColumn } from "typeorm"; - -export class SMSAlarming1732696919191 implements MigrationInterface { - name = "SMSAlarming1732696919191"; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.addColumn( - "communication", - new TableColumn({ - name: "isSMSAlarming", - type: "tinyint", - default: 0, - isNullable: false, - }) - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.dropColumn("communication", "isSMSAlarming"); - } -} diff --git a/src/migrations/1733249553766-securingCalendarType.ts b/src/migrations/1733249553766-securingCalendarType.ts deleted file mode 100644 index 2b486de..0000000 --- a/src/migrations/1733249553766-securingCalendarType.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { MigrationInterface, QueryRunner, TableColumn } from "typeorm"; - -export class SecuringCalendarType1733249553766 implements MigrationInterface { - name = "SecuringCalendarType1733249553766"; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.addColumns("calendar_type", [ - new TableColumn({ - name: "passphrase", - type: "varchar", - length: "255", - isNullable: true, - }), - ]); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.dropColumn("calendar_type", "passphrase"); - } -} diff --git a/src/migrations/1734187754677-queryStore.ts b/src/migrations/1734187754677-queryStore.ts deleted file mode 100644 index 6912110..0000000 --- a/src/migrations/1734187754677-queryStore.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { MigrationInterface, QueryRunner, Table } from "typeorm"; -import { DB_TYPE } from "../env.defaults"; - -export class QueryStore1734187754677 implements MigrationInterface { - name = "QueryStore1734187754677"; - - public async up(queryRunner: QueryRunner): Promise { - const variableType_int = DB_TYPE == "mysql" ? "int" : "integer"; - - await queryRunner.createTable( - new Table({ - name: "query", - columns: [ - { name: "id", type: variableType_int, isPrimary: true, isGenerated: true, generationStrategy: "increment" }, - { name: "title", type: "varchar", length: "255", isNullable: false, isUnique: true }, - { name: "query", type: "text", isNullable: false, default: "''" }, - ], - }), - true - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.dropTable("query"); - } -} diff --git a/src/migrations/1734520998539-memberDataViews.ts b/src/migrations/1734520998539-memberDataViews.ts deleted file mode 100644 index b29b3e2..0000000 --- a/src/migrations/1734520998539-memberDataViews.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { DataSource, MigrationInterface, QueryRunner, View } from "typeorm"; -import { member } from "../entity/club/member/member"; -import { memberExecutivePositions } from "../entity/club/member/memberExecutivePositions"; -import { memberQualifications } from "../entity/club/member/memberQualifications"; -import { membership } from "../entity/club/member/membership"; - -export class MemberDataViews1734520998539 implements MigrationInterface { - name = "MemberDataViews1734520998539"; - - public async up(queryRunner: QueryRunner): Promise { - // await queryRunner.createView( - // new View({ - // name: "member_view", - // expression: (datasource: DataSource) => - // datasource - // .getRepository(member) - // .createQueryBuilder("member") - // .select("member.id", "id") - // .addSelect("member.salutation", "salutation") - // .addSelect("member.firstname", "firstname") - // .addSelect("member.lastname", "lastname") - // .addSelect("member.nameaffix", "nameaffix") - // .addSelect("member.birthdate", "birthdate") - // .addSelect("TIMESTAMPDIFF(YEAR, member.birthdate, CURDATE())", "todayAge") - // .addSelect("YEAR(CURDATE()) - YEAR(member.birthdate)", "ageThisYear"), - // }), - // true - // ); - // await queryRunner.createView( - // new View({ - // name: "member_executive_positions_view", - // expression: (datasource: DataSource) => - // datasource - // .getRepository(memberExecutivePositions) - // .createQueryBuilder("memberExecutivePositions") - // .select("executivePosition.id", "positionId") - // .addSelect("executivePosition.position", "position") - // .addSelect("member.id", "memberId") - // .addSelect("member.salutation", "memberSalutation") - // .addSelect("member.firstname", "memberFirstname") - // .addSelect("member.lastname", "memberLastname") - // .addSelect("member.nameaffix", "memberNameaffix") - // .addSelect("member.birthdate", "memberBirthdate") - // .addSelect( - // "SUM(TIMESTAMPDIFF(DAY, memberExecutivePositions.start, COALESCE(memberExecutivePositions.end, CURRENT_DATE)))", - // "durationInDays" - // ) - // .leftJoin("memberExecutivePositions.executivePosition", "executivePosition") - // .leftJoin("memberExecutivePositions.member", "member") - // .groupBy("executivePosition.id"), - // }), - // true - // ); - // await queryRunner.createView( - // new View({ - // name: "member_qualifications_view", - // expression: (datasource: DataSource) => - // datasource - // .getRepository(memberQualifications) - // .createQueryBuilder("memberQualifications") - // .select("qualification.id", "qualificationId") - // .addSelect("qualification.qualification", "qualification") - // .addSelect("member.id", "memberId") - // .addSelect("member.salutation", "memberSalutation") - // .addSelect("member.firstname", "memberFirstname") - // .addSelect("member.lastname", "memberLastname") - // .addSelect("member.nameaffix", "memberNameaffix") - // .addSelect("member.birthdate", "memberBirthdate") - // .addSelect( - // "SUM(TIMESTAMPDIFF(DAY, memberQualifications.start, COALESCE(memberQualifications.end, CURRENT_DATE)))", - // "durationInDays" - // ) - // .leftJoin("memberQualifications.qualification", "qualification") - // .leftJoin("memberQualifications.member", "member") - // .groupBy("qualification.id"), - // }), - // true - // ); - // await queryRunner.createView( - // new View({ - // name: "membership_view", - // expression: (datasource: DataSource) => - // datasource - // .getRepository(membership) - // .createQueryBuilder("membership") - // .select("status.id", "statusId") - // .addSelect("status.status", "status") - // .addSelect("member.id", "memberId") - // .addSelect("member.salutation", "memberSalutation") - // .addSelect("member.firstname", "memberFirstname") - // .addSelect("member.lastname", "memberLastname") - // .addSelect("member.nameaffix", "memberNameaffix") - // .addSelect("member.birthdate", "memberBirthdate") - // .addSelect( - // "SUM(TIMESTAMPDIFF(DAY, membership.start, COALESCE(membership.end, CURRENT_DATE)))", - // "durationInDays" - // ) - // .leftJoin("membership.status", "status") - // .leftJoin("membership.member", "member") - // .groupBy("status.id"), - // }), - // true - // ); - } - - public async down(queryRunner: QueryRunner): Promise { - // await queryRunner.dropView("membership_view"); - // await queryRunner.dropView("member_qualifications_view"); - // await queryRunner.dropView("member_executive_positions_view"); - // await queryRunner.dropView("member_view"); - } -} diff --git a/src/migrations/1734854680201-template.ts b/src/migrations/1734854680201-template.ts deleted file mode 100644 index 7ccd1e9..0000000 --- a/src/migrations/1734854680201-template.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { MigrationInterface, QueryRunner, Table } from "typeorm"; -import { DB_TYPE } from "../env.defaults"; - -export class Template1734854680201 implements MigrationInterface { - name = "Template1734854680201"; - - public async up(queryRunner: QueryRunner): Promise { - const variableType_int = DB_TYPE == "mysql" ? "int" : "integer"; - - await queryRunner.createTable( - new Table({ - name: "template", - columns: [ - { name: "id", type: variableType_int, isPrimary: true, isGenerated: true, generationStrategy: "increment" }, - { name: "template", type: "varchar", length: "255", isNullable: false }, - { name: "description", type: "varchar", length: "255", isNullable: true }, - { name: "design", type: "text", isNullable: false, default: "'{}'" }, - { name: "html", type: "text", isNullable: false, default: "''" }, - ], - }), - true - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.dropTable("template"); - } -} diff --git a/src/migrations/1734949173739-templateUsage.ts b/src/migrations/1734949173739-templateUsage.ts deleted file mode 100644 index de98006..0000000 --- a/src/migrations/1734949173739-templateUsage.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { MigrationInterface, QueryRunner, Table, TableForeignKey } from "typeorm"; -import { DB_TYPE } from "../env.defaults"; -import { templateUsage } from "../entity/settings/templateUsage"; - -export class TemplateUsage1734949173739 implements MigrationInterface { - name = "TemplateUsage1734949173739"; - - public async up(queryRunner: QueryRunner): Promise { - const variableType_int = DB_TYPE == "mysql" ? "int" : "integer"; - - await queryRunner.createTable( - new Table({ - name: "template_usage", - columns: [ - { name: "scope", type: "varchar", length: "255", isPrimary: true }, - { name: "headerId", type: variableType_int, isNullable: true }, - { name: "bodyId", type: variableType_int, isNullable: true }, - { name: "footerId", type: variableType_int, isNullable: true }, - { name: "headerHeight", type: variableType_int, default: null, isNullable: true }, - { name: "footerHeight", type: variableType_int, default: null, isNullable: true }, - ], - }), - true - ); - - await queryRunner.manager - .createQueryBuilder() - .insert() - .into(templateUsage) - .values({ scope: "protocol" }) - .orIgnore() - .execute(); - - await queryRunner.createForeignKey( - "template_usage", - new TableForeignKey({ - columnNames: ["headerId"], - referencedColumnNames: ["id"], - referencedTableName: "template", - onDelete: "RESTRICT", - onUpdate: "RESTRICT", - }) - ); - await queryRunner.createForeignKey( - "template_usage", - new TableForeignKey({ - columnNames: ["bodyId"], - referencedColumnNames: ["id"], - referencedTableName: "template", - onDelete: "RESTRICT", - onUpdate: "RESTRICT", - }) - ); - await queryRunner.createForeignKey( - "template_usage", - new TableForeignKey({ - columnNames: ["footerId"], - referencedColumnNames: ["id"], - referencedTableName: "template", - onDelete: "RESTRICT", - onUpdate: "RESTRICT", - }) - ); - } - - public async down(queryRunner: QueryRunner): Promise { - const template_usage = await queryRunner.getTable("template_usage"); - let foreignKey = template_usage.foreignKeys.find((fk) => fk.columnNames.indexOf("headerId") !== -1); - await queryRunner.dropForeignKey("template_usage", foreignKey); - foreignKey = template_usage.foreignKeys.find((fk) => fk.columnNames.indexOf("bodyId") !== -1); - await queryRunner.dropForeignKey("template_usage", foreignKey); - foreignKey = template_usage.foreignKeys.find((fk) => fk.columnNames.indexOf("footerId") !== -1); - await queryRunner.dropForeignKey("template_usage", foreignKey); - - await queryRunner.dropTable("template_usage"); - } -} diff --git a/src/migrations/1735118780511-newsletter.ts b/src/migrations/1735118780511-newsletter.ts deleted file mode 100644 index 16985f9..0000000 --- a/src/migrations/1735118780511-newsletter.ts +++ /dev/null @@ -1,144 +0,0 @@ -import { MigrationInterface, QueryRunner, Table, TableForeignKey } from "typeorm"; -import { DB_TYPE } from "../env.defaults"; -import { templateUsage } from "../entity/settings/templateUsage"; - -export class Newsletter1735118780511 implements MigrationInterface { - name = "Newsletter1735118780511"; - - public async up(queryRunner: QueryRunner): Promise { - const variableType_int = DB_TYPE == "mysql" ? "int" : "integer"; - - await queryRunner.createTable( - new Table({ - name: "newsletter_dates", - columns: [ - { name: "newsletterId", type: variableType_int, isPrimary: true }, - { name: "calendarId", type: "varchar", length: "255", isPrimary: true }, - { name: "diffTitle", type: "varchar", length: "255", isNullable: true }, - { name: "diffDescription", type: "text", isNullable: true }, - ], - }), - true - ); - - await queryRunner.createTable( - new Table({ - name: "newsletter_recipients", - columns: [ - { name: "newsletterId", type: variableType_int, isPrimary: true }, - { name: "memberId", type: variableType_int, isPrimary: true }, - ], - }), - true - ); - - await queryRunner.createTable( - new Table({ - name: "newsletter", - columns: [ - { name: "id", type: variableType_int, isPrimary: true, isGenerated: true, generationStrategy: "increment" }, - { name: "title", type: "varchar", length: "255" }, - { name: "description", type: "varchar", length: "255", default: "''" }, - { name: "newsletterTitle", type: "varchar", length: "255", default: "''" }, - { name: "newsletterText", type: "text", default: "''" }, - { name: "newsletterSignatur", type: "varchar", length: "255", default: "''" }, - { name: "isSent", type: "tinyint", default: "0" }, - { name: "recipientsByQueryId", type: variableType_int, isNullable: true }, - ], - }), - true - ); - - await queryRunner.createForeignKey( - "newsletter_dates", - new TableForeignKey({ - columnNames: ["newsletterId"], - referencedColumnNames: ["id"], - referencedTableName: "newsletter", - onDelete: "CASCADE", - onUpdate: "RESTRICT", - }) - ); - - await queryRunner.createForeignKey( - "newsletter_dates", - new TableForeignKey({ - columnNames: ["calendarId"], - referencedColumnNames: ["id"], - referencedTableName: "calendar", - onDelete: "RESTRICT", - onUpdate: "RESTRICT", - }) - ); - - await queryRunner.createForeignKey( - "newsletter_recipients", - new TableForeignKey({ - columnNames: ["newsletterId"], - referencedColumnNames: ["id"], - referencedTableName: "newsletter", - onDelete: "CASCADE", - onUpdate: "RESTRICT", - }) - ); - - await queryRunner.createForeignKey( - "newsletter_recipients", - new TableForeignKey({ - columnNames: ["memberId"], - referencedColumnNames: ["id"], - referencedTableName: "member", - onDelete: "CASCADE", - onUpdate: "RESTRICT", - }) - ); - - await queryRunner.createForeignKey( - "newsletter", - new TableForeignKey({ - columnNames: ["recipientsByQueryId"], - referencedColumnNames: ["id"], - referencedTableName: "query", - onDelete: "CASCADE", - onUpdate: "RESTRICT", - }) - ); - - await queryRunner.manager - .createQueryBuilder() - .insert() - .into(templateUsage) - .values({ scope: "newsletter" }) - .orIgnore() - .execute(); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.manager - .createQueryBuilder() - .delete() - .from(templateUsage) - .where({ scope: "newsletter" }) - .execute(); - - const tableN = await queryRunner.getTable("newsletter"); - const tableNR = await queryRunner.getTable("newsletter_recipients"); - const tableND = await queryRunner.getTable("newsletter_dates"); - - const foreignKeyN = tableN.foreignKeys.find((fk) => fk.columnNames.indexOf("recipientsByQueryId") !== -1); - const foreignKeyNR = tableNR.foreignKeys.find((fk) => fk.columnNames.indexOf("newsletterId") !== -1); - const foreignKeyNR2 = tableNR.foreignKeys.find((fk) => fk.columnNames.indexOf("memberId") !== -1); - const foreignKeyND1 = tableND.foreignKeys.find((fk) => fk.columnNames.indexOf("newsletterId") !== -1); - const foreignKeyND2 = tableND.foreignKeys.find((fk) => fk.columnNames.indexOf("calendarId") !== -1); - - await queryRunner.dropForeignKey("newsletter", foreignKeyN); - await queryRunner.dropForeignKey("newsletter_recipients", foreignKeyNR); - await queryRunner.dropForeignKey("newsletter_recipients", foreignKeyNR2); - await queryRunner.dropForeignKey("newsletter_dates", foreignKeyND1); - await queryRunner.dropForeignKey("newsletter_dates", foreignKeyND2); - - await queryRunner.dropTable("newsletter"); - await queryRunner.dropTable("newsletter_recipients"); - await queryRunner.dropTable("newsletter_dates"); - } -} diff --git a/src/migrations/1735207446910-newsletterConfig.ts b/src/migrations/1735207446910-newsletterConfig.ts deleted file mode 100644 index 8f893d9..0000000 --- a/src/migrations/1735207446910-newsletterConfig.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { MigrationInterface, QueryRunner, Table, TableForeignKey } from "typeorm"; -import { DB_TYPE } from "../env.defaults"; - -export class NewsletterConfig1735207446910 implements MigrationInterface { - name = "NewsletterConfig1735207446910"; - - public async up(queryRunner: QueryRunner): Promise { - const variableType_int = DB_TYPE == "mysql" ? "int" : "integer"; - - await queryRunner.createTable( - new Table({ - name: "newsletter_config", - columns: [ - { name: "comTypeId", type: variableType_int, isPrimary: true, isNullable: false }, - { name: "config", type: "varchar", length: "255", isNullable: false }, - ], - }), - true - ); - - await queryRunner.createForeignKey( - "newsletter_config", - new TableForeignKey({ - columnNames: ["comTypeId"], - referencedColumnNames: ["id"], - referencedTableName: "communication_type", - onDelete: "CASCADE", - onUpdate: "RESTRICT", - }) - ); - } - - public async down(queryRunner: QueryRunner): Promise { - const table = await queryRunner.getTable("newsletter_config"); - const foreignKey = table.foreignKeys.find((fk) => fk.columnNames.indexOf("comTypeId") !== -1); - await queryRunner.dropForeignKey("newsletter_config", foreignKey); - await queryRunner.dropTable("newsletter_config"); - } -} diff --git a/src/migrations/1735822722235-internalId.ts b/src/migrations/1735822722235-internalId.ts deleted file mode 100644 index 40aacca..0000000 --- a/src/migrations/1735822722235-internalId.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { MigrationInterface, QueryRunner, TableColumn } from "typeorm"; -import { membership } from "../entity/club/member/membership"; -import { member } from "../entity/club/member/member"; - -export class InternalId1735822722235 implements MigrationInterface { - name = "InternalId1735822722235"; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.addColumn( - "member", - new TableColumn({ - name: "internalId", - type: "varchar", - length: "255", - default: null, - isNullable: true, - isUnique: true, - }) - ); - - // let memberships = await queryRunner.manager.getRepository(membership).find(); - // let internalIds = memberships.reduce<{ [key: number]: Array }>((acc, cur) => { - // let memberId = cur.memberId; - // let setIds = acc[memberId] ?? []; - // if (cur?.internalId) { - // setIds.push(cur.internalId); - // } - // acc[memberId] = setIds; - // return acc; - // }, {}); - - // for (const [id, value] of Object.entries(internalIds)) { - // const ids = value.filter((v) => v != null).join(", "); - // if (ids) { - // let m = await queryRunner.manager.getRepository(member).findOneByOrFail({ id: parseInt(id) }); - // m.internalId = ids; - // await queryRunner.manager.getRepository(member).save(m); - // } - // } - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.dropColumn("member", "internalId"); - } -} diff --git a/src/migrations/1735927918979-postalCode.ts b/src/migrations/1735927918979-postalCode.ts deleted file mode 100644 index e0dbbe5..0000000 --- a/src/migrations/1735927918979-postalCode.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { MigrationInterface, QueryRunner, TableColumn } from "typeorm"; - -export class PostalCode1735927918979 implements MigrationInterface { - name = "PostalCode1735927918979"; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.addColumn( - "communication", - new TableColumn({ - name: "postalCode", - type: "varchar", - length: "255", - default: null, - isNullable: true, - }) - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.dropColumn("communication", "postalCode"); - } -} diff --git a/src/migrations/1736072179716-protocolAbsent.ts b/src/migrations/1736072179716-protocolAbsent.ts deleted file mode 100644 index d6a859f..0000000 --- a/src/migrations/1736072179716-protocolAbsent.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { MigrationInterface, QueryRunner, TableColumn } from "typeorm"; - -export class ProtocolAbsent1736072179716 implements MigrationInterface { - name = "ProtocolAbsent1736072179716"; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.addColumn( - "protocol_presence", - new TableColumn({ - name: "absent", - type: "tinyint", - default: "0", - isNullable: false, - }) - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.dropColumn("protocol_presence", "absent"); - } -} diff --git a/src/migrations/1736079005086-memberlist.ts b/src/migrations/1736079005086-memberlist.ts deleted file mode 100644 index 6cb6241..0000000 --- a/src/migrations/1736079005086-memberlist.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; -import { templateUsage } from "../entity/settings/templateUsage"; -import {PermissionModule} from "../type/permissionTypes"; - -export class Memberlist1736079005086 implements MigrationInterface { - name = "Memberlist1736079005086"; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.manager - .createQueryBuilder() - .insert() - .into(templateUsage) - .values({ scope: "member.list" }) - .orIgnore() - .execute(); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.manager - .createQueryBuilder() - .delete() - .from(templateUsage) - .where({ scope: "member.list" }) - .execute(); - } -} diff --git a/src/migrations/1736084198860-extendViewValues.ts b/src/migrations/1736084198860-extendViewValues.ts deleted file mode 100644 index 4a024c9..0000000 --- a/src/migrations/1736084198860-extendViewValues.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { DataSource, MigrationInterface, QueryRunner, View } from "typeorm"; -import { member } from "../entity/club/member/member"; -import { memberExecutivePositions } from "../entity/club/member/memberExecutivePositions"; -import { memberQualifications } from "../entity/club/member/memberQualifications"; -import { membership } from "../entity/club/member/membership"; - -export class ExtendViewValues1736084198860 implements MigrationInterface { - name = "ExtendViewValues1736084198860"; - - public async up(queryRunner: QueryRunner): Promise { - // await queryRunner.dropView("membership_view"); - // await queryRunner.dropView("member_view"); - // await queryRunner.createView( - // new View({ - // name: "member_view", - // expression: (datasource: DataSource) => - // datasource - // .getRepository(member) - // .createQueryBuilder("member") - // .select("member.id", "id") - // .addSelect("member.salutation", "salutation") - // .addSelect("member.firstname", "firstname") - // .addSelect("member.lastname", "lastname") - // .addSelect("member.nameaffix", "nameaffix") - // .addSelect("member.birthdate", "birthdate") - // .addSelect("TIMESTAMPDIFF(YEAR, member.birthdate, CURDATE())", "todayAge") - // .addSelect("YEAR(CURDATE()) - YEAR(member.birthdate)", "ageThisYear") - // .addSelect("CONCAT('_', FROM_DAYS(TIMESTAMPDIFF(DAY, member.birthdate, CURDATE())))", "exactAge"), - // }), - // true - // ); - // await queryRunner.createView( - // new View({ - // name: "membership_view", - // expression: (datasource: DataSource) => - // datasource - // .getRepository(membership) - // .createQueryBuilder("membership") - // .select("status.id", "statusId") - // .addSelect("status.status", "status") - // .addSelect("member.id", "memberId") - // .addSelect("member.salutation", "memberSalutation") - // .addSelect("member.firstname", "memberFirstname") - // .addSelect("member.lastname", "memberLastname") - // .addSelect("member.nameaffix", "memberNameaffix") - // .addSelect("member.birthdate", "memberBirthdate") - // .addSelect( - // "SUM(TIMESTAMPDIFF(DAY, membership.start, COALESCE(membership.end, CURRENT_DATE)))", - // "durationInDays" - // ) - // .addSelect( - // "CONCAT('_', FROM_DAYS(SUM(TIMESTAMPDIFF(DAY, membership.start, COALESCE(membership.end, CURRENT_DATE)))))", - // "durationInYears" - // ) - // .leftJoin("membership.status", "status") - // .leftJoin("membership.member", "member") - // .groupBy("status.id") - // .addGroupBy("member.id"), - // }), - // true - // ); - } - - public async down(queryRunner: QueryRunner): Promise { - // await queryRunner.dropView("membership_view"); - // await queryRunner.dropView("member_view"); - // await queryRunner.createView( - // new View({ - // name: "member_view", - // expression: (datasource: DataSource) => - // datasource - // .getRepository(member) - // .createQueryBuilder("member") - // .select("member.id", "id") - // .addSelect("member.salutation", "salutation") - // .addSelect("member.firstname", "firstname") - // .addSelect("member.lastname", "lastname") - // .addSelect("member.nameaffix", "nameaffix") - // .addSelect("member.birthdate", "birthdate") - // .addSelect("TIMESTAMPDIFF(YEAR, member.birthdate, CURDATE())", "todayAge") - // .addSelect("YEAR(CURDATE()) - YEAR(member.birthdate)", "ageThisYear"), - // }), - // true - // ); - // await queryRunner.createView( - // new View({ - // name: "membership_view", - // expression: (datasource: DataSource) => - // datasource - // .getRepository(membership) - // .createQueryBuilder("membership") - // .select("status.id", "statusId") - // .addSelect("status.status", "status") - // .addSelect("member.id", "memberId") - // .addSelect("member.salutation", "memberSalutation") - // .addSelect("member.firstname", "memberFirstname") - // .addSelect("member.lastname", "memberLastname") - // .addSelect("member.nameaffix", "memberNameaffix") - // .addSelect("member.birthdate", "memberBirthdate") - // .addSelect( - // "SUM(TIMESTAMPDIFF(DAY, membership.start, COALESCE(membership.end, CURRENT_DATE)))", - // "durationInDays" - // ) - // .leftJoin("membership.status", "status") - // .leftJoin("membership.member", "member") - // .groupBy("status.id"), - // }), - // true - // ); - } -} diff --git a/src/migrations/1736505324488-finishInternalIdTransfer.ts b/src/migrations/1736505324488-finishInternalIdTransfer.ts deleted file mode 100644 index 3c51c94..0000000 --- a/src/migrations/1736505324488-finishInternalIdTransfer.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { MigrationInterface, QueryRunner, TableColumn } from "typeorm"; - -export class FinishInternalIdTransfer1736505324488 implements MigrationInterface { - name = "FinishInternalIdTransfer1736505324488"; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.dropColumn("membership", "internalId"); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.addColumn( - "membership", - new TableColumn({ - name: "internalId", - type: "varchar", - length: "255", - default: null, - isNullable: true, - isUnique: true, - }) - ); - } -} diff --git a/src/migrations/1737287798828-protocolPresenceExcuse.ts b/src/migrations/1737287798828-protocolPresenceExcuse.ts deleted file mode 100644 index c3954e1..0000000 --- a/src/migrations/1737287798828-protocolPresenceExcuse.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { MigrationInterface, QueryRunner, TableColumn } from "typeorm"; - -export class ProtocolPresenceExcuse1737287798828 implements MigrationInterface { - name = "ProtocolPresenceExcuse1737287798828"; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.addColumn( - "protocol_presence", - new TableColumn({ - name: "excused", - type: "tinyint", - default: "1", - isNullable: false, - }) - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.dropColumn("protocol_presence", "excused"); - } -} diff --git a/src/migrations/1737453096674-addwebapiTokens.ts b/src/migrations/1737453096674-addwebapiTokens.ts deleted file mode 100644 index 55f37fb..0000000 --- a/src/migrations/1737453096674-addwebapiTokens.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { MigrationInterface, QueryRunner, Table, TableForeignKey } from "typeorm"; -import { DB_TYPE } from "../env.defaults"; - -export class AddWebapiTokens1737453096674 implements MigrationInterface { - name = "AddWebApiTokens1737453096674"; - - public async up(queryRunner: QueryRunner): Promise { - const variableType_int = DB_TYPE == "mysql" ? "int" : "integer"; - - await queryRunner.createTable( - new Table({ - name: "webapi", - columns: [ - { name: "id", type: variableType_int, isPrimary: true, isGenerated: true, generationStrategy: "increment" }, - { name: "token", type: "text", isUnique: true, isNullable: false }, - { name: "title", type: "varchar", isUnique: true, length: "255", isNullable: false }, - { name: "createdAt", type: "datetime", default: "CURRENT_TIMESTAMP(6)", isNullable: false }, - { name: "lastUsage", type: "datetime", isNullable: true, default: null }, - { name: "expiry", type: "date", isNullable: true, default: null }, - ], - }), - true - ); - - await queryRunner.createTable( - new Table({ - name: "webapi_permission", - columns: [ - { name: "webapiId", type: variableType_int, isPrimary: true, isNullable: false }, - { name: "permission", type: "varchar", length: "255", isPrimary: true, isNullable: false }, - ], - }), - true - ); - - await queryRunner.createForeignKey( - "webapi_permission", - new TableForeignKey({ - columnNames: ["webapiId"], - referencedColumnNames: ["id"], - referencedTableName: "webapi", - onDelete: "CASCADE", - onUpdate: "RESTRICT", - }) - ); - } - - public async down(queryRunner: QueryRunner): Promise { - const table = await queryRunner.getTable("webapi_permission"); - const foreignKey = table.foreignKeys.find((fk) => fk.columnNames.indexOf("webapiId") !== -1); - await queryRunner.dropForeignKey("webapi_permission", foreignKey); - await queryRunner.dropTable("webapi_permission"); - await queryRunner.dropTable("webapi"); - } -} diff --git a/src/migrations/1737796878058-salutationAsTable.ts b/src/migrations/1737796878058-salutationAsTable.ts deleted file mode 100644 index df84a91..0000000 --- a/src/migrations/1737796878058-salutationAsTable.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { MigrationInterface, QueryRunner, Table, TableForeignKey } from "typeorm"; -import { DB_TYPE } from "../env.defaults"; -import { member } from "../entity/club/member/member"; -import { salutation } from "../entity/settings/salutation"; - -export class SalutationAsTable1737796878058 implements MigrationInterface { - name = "SalutationAsTable1737796878058"; - - public async up(queryRunner: QueryRunner): Promise { - const variableType_int = DB_TYPE == "mysql" ? "int" : "integer"; - - await queryRunner.createTable( - new Table({ - name: "salutation", - columns: [ - { name: "id", type: variableType_int, isPrimary: true, isGenerated: true, generationStrategy: "increment" }, - { name: "salutation", type: "varchar", length: "255", isUnique: true, isNullable: false }, - ], - }), - true - ); - - // ! has to be sql. Else the column would be dropped and created - resulting in data loss. - await queryRunner.query( - `ALTER TABLE \`member\` CHANGE \`salutation\` \`salutationId\` varchar(255) NOT NULL DEFAULT ''` - ); - - // ! has to be sql. Else no data is returned. - const existing_salutations = await queryRunner.query( - "SELECT DISTINCT salutationId FROM `member` `member` GROUP BY salutationId" - ); - - for (let s of existing_salutations.map((s: any) => s.salutationId) as Array) { - await queryRunner.manager.getRepository(salutation).save({ salutation: s }); - } - - const salutations = await queryRunner.manager.getRepository(salutation).find(); - - for (let salutation of salutations) { - await queryRunner.manager - .getRepository(member) - .createQueryBuilder("member") - .update({ salutationId: salutation.id }) - .where({ salutationId: salutation.salutation }) - .execute(); - } - - await queryRunner.query( - `ALTER TABLE \`member\` CHANGE \`salutationId\` \`salutationId\` ${variableType_int} NOT NULL` - ); - - await queryRunner.createForeignKey( - "member", - new TableForeignKey({ - columnNames: ["salutationId"], - referencedColumnNames: ["id"], - referencedTableName: "salutation", - onDelete: "RESTRICT", - onUpdate: "RESTRICT", - }) - ); - } - - public async down(queryRunner: QueryRunner): Promise { - const table = await queryRunner.getTable("member"); - const foreignKey = table.foreignKeys.find((fk) => fk.columnNames.indexOf("salutationId") !== -1); - await queryRunner.dropForeignKey("member", foreignKey); - - // ! has to be sql. Else the column would be dropped and created - resulting in data loss. - await queryRunner.query( - `ALTER TABLE \`member\` CHANGE \`salutationId\` \`salutationId\` varchar(255) NOT NULL DEFAULT ''` - ); - - const salutations = await queryRunner.manager.getRepository(salutation).find(); - - for (let salutation of salutations) { - await queryRunner.manager - .getRepository(member) - .createQueryBuilder("member") - .update({ salutationId: salutation.salutation as unknown as number }) - .where({ salutationId: salutation.id }) - .execute(); - } - - await queryRunner.query( - `ALTER TABLE \`member\` CHANGE \`salutationId\` \`salutation\` varchar(255) NOT NULL DEFAULT 'none'` - ); - - await queryRunner.dropTable("webapi"); - } -} diff --git a/src/migrations/1737800468938-updateViews.ts b/src/migrations/1737800468938-updateViews.ts deleted file mode 100644 index 76dd7cc..0000000 --- a/src/migrations/1737800468938-updateViews.ts +++ /dev/null @@ -1,233 +0,0 @@ -import { DataSource, MigrationInterface, QueryRunner, View } from "typeorm"; -import { member } from "../entity/club/member/member"; -import { memberExecutivePositions } from "../entity/club/member/memberExecutivePositions"; -import { memberQualifications } from "../entity/club/member/memberQualifications"; -import { membership } from "../entity/club/member/membership"; - -export class UpdateViews1737800468938 implements MigrationInterface { - name = "UpdateViews1737800468938"; - - public async up(queryRunner: QueryRunner): Promise { - // await queryRunner.dropView("membership_view"); - // await queryRunner.dropView("member_executive_positions_view"); - // await queryRunner.dropView("member_qualifications_view"); - // await queryRunner.dropView("member_view"); - await queryRunner.query(`DROP VIEW IF EXISTS membership_view`); - await queryRunner.query(`DROP VIEW IF EXISTS member_executive_positions_view`); - await queryRunner.query(`DROP VIEW IF EXISTS member_qualifications_view`); - await queryRunner.query(`DROP VIEW IF EXISTS member_view`); - - await queryRunner.createView( - new View({ - name: "member_view", - expression: (datasource: DataSource) => - datasource - .getRepository(member) - .createQueryBuilder("member") - .select("member.id", "id") - .addSelect("member.firstname", "firstname") - .addSelect("member.lastname", "lastname") - .addSelect("member.nameaffix", "nameaffix") - .addSelect("member.birthdate", "birthdate") - .addSelect("salutation.salutation", "salutation") - .addSelect("TIMESTAMPDIFF(YEAR, member.birthdate, CURDATE())", "todayAge") - .addSelect("YEAR(CURDATE()) - YEAR(member.birthdate)", "ageThisYear") - .addSelect("CONCAT('_', FROM_DAYS(TIMESTAMPDIFF(DAY, member.birthdate, CURDATE())))", "exactAge") - .leftJoin("member.salutation", "salutation"), - }), - true - ); - await queryRunner.createView( - new View({ - name: "member_executive_positions_view", - expression: (datasource: DataSource) => - datasource - .getRepository(memberExecutivePositions) - .createQueryBuilder("memberExecutivePositions") - .select("executivePosition.id", "positionId") - .addSelect("executivePosition.position", "position") - .addSelect("member.id", "memberId") - .addSelect("member.firstname", "memberFirstname") - .addSelect("member.lastname", "memberLastname") - .addSelect("member.nameaffix", "memberNameaffix") - .addSelect("member.birthdate", "memberBirthdate") - .addSelect("salutation.salutation", "memberSalutation") - .addSelect( - "SUM(TIMESTAMPDIFF(DAY, memberExecutivePositions.start, COALESCE(memberExecutivePositions.end, CURRENT_DATE)))", - "durationInDays" - ) - .leftJoin("memberExecutivePositions.executivePosition", "executivePosition") - .leftJoin("memberExecutivePositions.member", "member") - .leftJoin("member.salutation", "salutation") - .groupBy("executivePosition.id") - .addGroupBy("member.id"), - }), - true - ); - await queryRunner.createView( - new View({ - name: "member_qualifications_view", - expression: (datasource: DataSource) => - datasource - .getRepository(memberQualifications) - .createQueryBuilder("memberQualifications") - .select("qualification.id", "qualificationId") - .addSelect("qualification.qualification", "qualification") - .addSelect("member.id", "memberId") - .addSelect("member.firstname", "memberFirstname") - .addSelect("member.lastname", "memberLastname") - .addSelect("member.nameaffix", "memberNameaffix") - .addSelect("member.birthdate", "memberBirthdate") - .addSelect("salutation.salutation", "memberSalutation") - .addSelect( - "SUM(TIMESTAMPDIFF(DAY, memberQualifications.start, COALESCE(memberQualifications.end, CURRENT_DATE)))", - "durationInDays" - ) - .leftJoin("memberQualifications.qualification", "qualification") - .leftJoin("memberQualifications.member", "member") - .leftJoin("member.salutation", "salutation") - .groupBy("qualification.id") - .addGroupBy("member.id"), - }), - true - ); - await queryRunner.createView( - new View({ - name: "membership_view", - expression: (datasource: DataSource) => - datasource - .getRepository(membership) - .createQueryBuilder("membership") - .select("status.id", "statusId") - .addSelect("status.status", "status") - .addSelect("member.id", "memberId") - .addSelect("member.firstname", "memberFirstname") - .addSelect("member.lastname", "memberLastname") - .addSelect("member.nameaffix", "memberNameaffix") - .addSelect("member.birthdate", "memberBirthdate") - .addSelect("salutation.salutation", "memberSalutation") - .addSelect( - "SUM(TIMESTAMPDIFF(DAY, membership.start, COALESCE(membership.end, CURRENT_DATE)))", - "durationInDays" - ) - .addSelect( - "CONCAT('_', FROM_DAYS(SUM(TIMESTAMPDIFF(DAY, membership.start, COALESCE(membership.end, CURRENT_DATE)))))", - "durationInYears" - ) - .leftJoin("membership.status", "status") - .leftJoin("membership.member", "member") - .leftJoin("member.salutation", "salutation") - .groupBy("status.id") - .addGroupBy("member.id"), - }), - true - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.dropView("membership_view"); - await queryRunner.dropView("member_executive_positions_view"); - await queryRunner.dropView("member_qualifications_view"); - await queryRunner.dropView("member_view"); - - await queryRunner.createView( - new View({ - name: "member_view", - expression: (datasource: DataSource) => - datasource - .getRepository(member) - .createQueryBuilder("member") - .select("member.id", "id") - .addSelect("member.salutation", "salutation") - .addSelect("member.firstname", "firstname") - .addSelect("member.lastname", "lastname") - .addSelect("member.nameaffix", "nameaffix") - .addSelect("member.birthdate", "birthdate") - .addSelect("TIMESTAMPDIFF(YEAR, member.birthdate, CURDATE())", "todayAge") - .addSelect("YEAR(CURDATE()) - YEAR(member.birthdate)", "ageThisYear") - .addSelect("CONCAT('_', FROM_DAYS(TIMESTAMPDIFF(DAY, member.birthdate, CURDATE())))", "exactAge"), - }), - true - ); - await queryRunner.createView( - new View({ - name: "member_executive_positions_view", - expression: (datasource: DataSource) => - datasource - .getRepository(memberExecutivePositions) - .createQueryBuilder("memberExecutivePositions") - .select("executivePosition.id", "positionId") - .addSelect("executivePosition.position", "position") - .addSelect("member.id", "memberId") - .addSelect("member.salutation", "memberSalutation") - .addSelect("member.firstname", "memberFirstname") - .addSelect("member.lastname", "memberLastname") - .addSelect("member.nameaffix", "memberNameaffix") - .addSelect("member.birthdate", "memberBirthdate") - .addSelect( - "SUM(TIMESTAMPDIFF(DAY, memberExecutivePositions.start, COALESCE(memberExecutivePositions.end, CURRENT_DATE)))", - "durationInDays" - ) - .leftJoin("memberExecutivePositions.executivePosition", "executivePosition") - .leftJoin("memberExecutivePositions.member", "member") - .groupBy("executivePosition.id"), - }), - true - ); - await queryRunner.createView( - new View({ - name: "member_qualifications_view", - expression: (datasource: DataSource) => - datasource - .getRepository(memberQualifications) - .createQueryBuilder("memberQualifications") - .select("qualification.id", "qualificationId") - .addSelect("qualification.qualification", "qualification") - .addSelect("member.id", "memberId") - .addSelect("member.salutation", "memberSalutation") - .addSelect("member.firstname", "memberFirstname") - .addSelect("member.lastname", "memberLastname") - .addSelect("member.nameaffix", "memberNameaffix") - .addSelect("member.birthdate", "memberBirthdate") - .addSelect( - "SUM(TIMESTAMPDIFF(DAY, memberQualifications.start, COALESCE(memberQualifications.end, CURRENT_DATE)))", - "durationInDays" - ) - .leftJoin("memberQualifications.qualification", "qualification") - .leftJoin("memberQualifications.member", "member") - .groupBy("qualification.id"), - }), - true - ); - await queryRunner.createView( - new View({ - name: "membership_view", - expression: (datasource: DataSource) => - datasource - .getRepository(membership) - .createQueryBuilder("membership") - .select("status.id", "statusId") - .addSelect("status.status", "status") - .addSelect("member.id", "memberId") - .addSelect("member.salutation", "memberSalutation") - .addSelect("member.firstname", "memberFirstname") - .addSelect("member.lastname", "memberLastname") - .addSelect("member.nameaffix", "memberNameaffix") - .addSelect("member.birthdate", "memberBirthdate") - .addSelect( - "SUM(TIMESTAMPDIFF(DAY, membership.start, COALESCE(membership.end, CURRENT_DATE)))", - "durationInDays" - ) - .addSelect( - "CONCAT('_', FROM_DAYS(SUM(TIMESTAMPDIFF(DAY, membership.start, COALESCE(membership.end, CURRENT_DATE)))))", - "durationInYears" - ) - .leftJoin("membership.status", "status") - .leftJoin("membership.member", "member") - .groupBy("status.id") - .addGroupBy("member.id"), - }), - true - ); - } -} diff --git a/src/migrations/1737816852011-moveSendNewsletterFlag.ts b/src/migrations/1737816852011-moveSendNewsletterFlag.ts deleted file mode 100644 index d294337..0000000 --- a/src/migrations/1737816852011-moveSendNewsletterFlag.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { MigrationInterface, QueryRunner, TableColumn, TableForeignKey, TableIndex } from "typeorm"; -import { communication } from "../entity/club/member/communication"; -import { member } from "../entity/club/member/member"; - -export class MoveSendNewsletterFlag1737816852011 implements MigrationInterface { - name = "MoveSendNewsletterFlag1737816852011"; - - public async up(queryRunner: QueryRunner): Promise { - const table = await queryRunner.getTable("member"); - const foreignKey = table.foreignKeys.find((fk) => fk.columnNames.indexOf("sendNewsletterId") !== -1); - await queryRunner.dropForeignKey("member", foreignKey); - - await queryRunner.addColumn( - "communication", - new TableColumn({ name: "isSendNewsletter", type: "tinyint", isNullable: false, default: 0 }) - ); - - // ! has to be sql. Else no data is returned. - const member_newsletter_send = await queryRunner.query("SELECT sendNewsletterId, id FROM `member` `member`"); - - for (let assigned of member_newsletter_send.map((mns: any) => ({ - id: mns.id, - sendNewsletterId: mns.sendNewsletterId, - })) as Array<{ id: number; sendNewsletterId: number }>) { - await queryRunner.manager - .getRepository(communication) - .createQueryBuilder("communication") - .update({ isSendNewsletter: true }) - .where({ memberId: assigned.id, id: assigned.sendNewsletterId }) - .execute(); - } - - await queryRunner.dropColumn("member", "sendNewsletterId"); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.addColumn( - "member", - new TableColumn({ name: "sendNewsletterId", type: "int", isNullable: true, isUnique: true, default: null }) - ); - - const member_newsletter_send = await queryRunner.manager - .getRepository(communication) - .createQueryBuilder("communication") - .where("communication.isSendNewsletter = :isSendNewsletter", { isSendNewsletter: true }) - .getMany(); - - for (let assigned of member_newsletter_send.map((mns: any) => ({ - id: mns.id, - memberId: mns.memberId, - })) as Array<{ id: number; memberId: number }>) { - await queryRunner.query("UPDATE `member` SET sendNewsletterId = ? WHERE id = ?", [ - assigned.id, - assigned.memberId, - ]); - } - - await queryRunner.createForeignKey( - "member", - new TableForeignKey({ - columnNames: ["sendNewsletterId"], - referencedColumnNames: ["id"], - referencedTableName: "communication", - onDelete: "SET NULL", - onUpdate: "RESTRICT", - }) - ); - - await queryRunner.dropColumn("communication", "isSendNewsletter"); - } -} diff --git a/src/migrations/1738057119384-UniqueFields.ts b/src/migrations/1738057119384-UniqueFields.ts deleted file mode 100644 index f60def2..0000000 --- a/src/migrations/1738057119384-UniqueFields.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class UniqueFields1738057119384 implements MigrationInterface { - name = "UniqueFields1738057119384"; - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP INDEX \`UQ_6c6bdcaeb808549ae66c2493efd\` ON \`salutation\``); - await queryRunner.query(`DROP INDEX \`UQ_9ffb36037fb8d7456689956ed80\` ON \`query\``); - await queryRunner.query(`DROP INDEX \`UQ_424ac388ca888a1dc67f5fe3a80\` ON \`webapi\``); - await queryRunner.query(`DROP INDEX \`UQ_55c99c82f45936d8d34c48f5515\` ON \`webapi\``); - - await queryRunner.query(`ALTER TABLE \`award\` ADD UNIQUE INDEX \`IDX_646bbdb45883cc39c55740d7b6\` (\`award\`)`); - await queryRunner.query(`ALTER TABLE \`protocol\` ADD UNIQUE INDEX \`IDX_e60159678b8dad84b2e7dfa20c\` (\`title\`)`); - await queryRunner.query( - `ALTER TABLE \`calendar_type\` ADD UNIQUE INDEX \`IDX_fbcbfda68b80800afa78e21c88\` (\`type\`)` - ); - await queryRunner.query(`ALTER TABLE \`query\` ADD UNIQUE INDEX \`IDX_9ffb36037fb8d7456689956ed8\` (\`title\`)`); - await queryRunner.query( - `ALTER TABLE \`template\` ADD UNIQUE INDEX \`IDX_9d0ad817708f4c1a7c78e4abf6\` (\`template\`)` - ); - await queryRunner.query( - `ALTER TABLE \`newsletter\` ADD UNIQUE INDEX \`IDX_80b2037e9f015eb2cf597bae6c\` (\`title\`)` - ); - await queryRunner.query( - `ALTER TABLE \`membership_status\` ADD UNIQUE INDEX \`IDX_9d71cc98acfae7a8276718f052\` (\`status\`)` - ); - await queryRunner.query( - `ALTER TABLE \`qualification\` ADD UNIQUE INDEX \`IDX_2d2540f8b970a5a43a905278da\` (\`qualification\`)` - ); - await queryRunner.query( - `ALTER TABLE \`executive_position\` ADD UNIQUE INDEX \`IDX_427169e456c217a317273c31d7\` (\`position\`)` - ); - await queryRunner.query( - `ALTER TABLE \`communication_type\` ADD UNIQUE INDEX \`IDX_63a3b23374fc0dd80e5ab32e09\` (\`type\`)` - ); - await queryRunner.query( - `ALTER TABLE \`salutation\` ADD UNIQUE INDEX \`IDX_6c6bdcaeb808549ae66c2493ef\` (\`salutation\`)` - ); - await queryRunner.query(`ALTER TABLE \`webapi\` ADD UNIQUE INDEX \`IDX_55c99c82f45936d8d34c48f551\` (\`token\`)`); - await queryRunner.query(`ALTER TABLE \`webapi\` ADD UNIQUE INDEX \`IDX_424ac388ca888a1dc67f5fe3a8\` (\`title\`)`); - await queryRunner.query(`ALTER TABLE \`role\` ADD UNIQUE INDEX \`IDX_367aad98203bd8afaed0d70409\` (\`role\`)`); - await queryRunner.query(`ALTER TABLE \`user\` ADD UNIQUE INDEX \`IDX_7395ecde6cda2e7fe90253ec59\` (\`mail\`)`); - await queryRunner.query(`ALTER TABLE \`user\` ADD UNIQUE INDEX \`IDX_78a916df40e02a9deb1c4b75ed\` (\`username\`)`); - - await queryRunner.query(`ALTER TABLE \`user_permission\` DROP FOREIGN KEY \`FK_deb59c09715314aed1866e18a81\``); - await queryRunner.query(`ALTER TABLE \`refresh\` DROP FOREIGN KEY \`FK_b39e4ed3bfa789758e476870ec2\``); - await queryRunner.query(`ALTER TABLE \`user_roles\` DROP FOREIGN KEY \`FK_472b25323af01488f1f66a06b67\``); - - await queryRunner.query(`ALTER TABLE \`user\` MODIFY COLUMN \`id\` varchar(36) NOT NULL`); - - await queryRunner.query(`ALTER TABLE \`user_permission\` MODIFY COLUMN \`userId\` varchar(36) NOT NULL`); - await queryRunner.query( - `ALTER TABLE \`user_permission\` ADD CONSTRAINT \`FK_deb59c09715314aed1866e18a81\` FOREIGN KEY (\`userId\`) REFERENCES \`user\`(\`id\`) ON DELETE CASCADE ON UPDATE RESTRICT` - ); - - await queryRunner.query(`ALTER TABLE \`refresh\` MODIFY COLUMN \`userId\` varchar(36) NOT NULL`); - await queryRunner.query( - `ALTER TABLE \`refresh\` ADD CONSTRAINT \`FK_b39e4ed3bfa789758e476870ec2\` FOREIGN KEY (\`userId\`) REFERENCES \`user\`(\`id\`) ON DELETE CASCADE ON UPDATE RESTRICT` - ); - - await queryRunner.query(`DROP INDEX \`IDX_472b25323af01488f1f66a06b6\` ON \`user_roles\``); - await queryRunner.query(`ALTER TABLE \`user_roles\` MODIFY COLUMN \`userId\` varchar(36) NOT NULL`); - await queryRunner.query(`CREATE INDEX \`IDX_472b25323af01488f1f66a06b6\` ON \`user_roles\` (\`userId\`)`); - await queryRunner.query( - `ALTER TABLE \`user_roles\` ADD CONSTRAINT \`FK_472b25323af01488f1f66a06b67\` FOREIGN KEY (\`userId\`) REFERENCES \`user\`(\`id\`) ON DELETE CASCADE ON UPDATE RESTRICT` - ); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`ALTER TABLE \`webapi\` DROP INDEX \`IDX_424ac388ca888a1dc67f5fe3a8\``); - await queryRunner.query(`ALTER TABLE \`webapi\` DROP INDEX \`IDX_55c99c82f45936d8d34c48f551\``); - await queryRunner.query(`ALTER TABLE \`newsletter\` DROP INDEX \`IDX_80b2037e9f015eb2cf597bae6c\``); - await queryRunner.query(`ALTER TABLE \`template\` DROP INDEX \`IDX_9d0ad817708f4c1a7c78e4abf6\``); - await queryRunner.query(`ALTER TABLE \`query\` DROP INDEX \`IDX_9ffb36037fb8d7456689956ed8\``); - await queryRunner.query(`ALTER TABLE \`calendar_type\` DROP INDEX \`IDX_fbcbfda68b80800afa78e21c88\``); - await queryRunner.query(`ALTER TABLE \`protocol\` DROP INDEX \`IDX_e60159678b8dad84b2e7dfa20c\``); - await queryRunner.query(`ALTER TABLE \`award\` DROP INDEX \`IDX_646bbdb45883cc39c55740d7b6\``); - await queryRunner.query(`ALTER TABLE \`salutation\` DROP INDEX \`IDX_6c6bdcaeb808549ae66c2493ef\``); - await queryRunner.query(`ALTER TABLE \`communication_type\` DROP INDEX \`IDX_63a3b23374fc0dd80e5ab32e09\``); - await queryRunner.query(`ALTER TABLE \`executive_position\` DROP INDEX \`IDX_427169e456c217a317273c31d7\``); - await queryRunner.query(`ALTER TABLE \`qualification\` DROP INDEX \`IDX_2d2540f8b970a5a43a905278da\``); - await queryRunner.query(`ALTER TABLE \`membership_status\` DROP INDEX \`IDX_9d71cc98acfae7a8276718f052\``); - - await queryRunner.query(`ALTER TABLE \`user_roles\` DROP FOREIGN KEY \`FK_472b25323af01488f1f66a06b67\``); - await queryRunner.query(`ALTER TABLE \`user_permission\` DROP FOREIGN KEY \`FK_deb59c09715314aed1866e18a81\``); - await queryRunner.query(`ALTER TABLE \`refresh\` DROP FOREIGN KEY \`FK_b39e4ed3bfa789758e476870ec2\``); - - await queryRunner.query(`ALTER TABLE \`user\` DROP INDEX \`IDX_78a916df40e02a9deb1c4b75ed\``); - await queryRunner.query(`ALTER TABLE \`user\` DROP INDEX \`IDX_7395ecde6cda2e7fe90253ec59\``); - await queryRunner.query(`ALTER TABLE \`user\` MODIFY COLUMN \`id\` int NOT NULL AUTO_INCREMENT`); - - await queryRunner.query(`DROP INDEX \`IDX_472b25323af01488f1f66a06b6\` ON \`user_roles\``); - - await queryRunner.query(`ALTER TABLE \`refresh\` MODIFY COLUMN \`userId\` int NOT NULL`); - await queryRunner.query( - `ALTER TABLE \`refresh\` ADD CONSTRAINT \`FK_b39e4ed3bfa789758e476870ec2\` FOREIGN KEY (\`userId\`) REFERENCES \`user\`(\`id\`) ON DELETE CASCADE ON UPDATE RESTRICT` - ); - - await queryRunner.query(`ALTER TABLE \`user_permission\` MODIFY COLUMN \`userId\` int NOT NULL`); - await queryRunner.query( - `ALTER TABLE \`user_permission\` ADD CONSTRAINT \`FK_deb59c09715314aed1866e18a81\` FOREIGN KEY (\`userId\`) REFERENCES \`user\`(\`id\`) ON DELETE CASCADE ON UPDATE RESTRICT` - ); - - await queryRunner.query(`ALTER TABLE \`user_roles\` MODIFY COLUMN \`userId\` int NOT NULL`); - await queryRunner.query(`CREATE INDEX \`IDX_472b25323af01488f1f66a06b6\` ON \`user_roles\` (\`userId\`)`); - await queryRunner.query( - `ALTER TABLE \`user_roles\` ADD CONSTRAINT \`FK_472b25323af01488f1f66a06b67\` FOREIGN KEY (\`userId\`) REFERENCES \`user\`(\`id\`) ON DELETE CASCADE ON UPDATE RESTRICT` - ); - - await queryRunner.query(`ALTER TABLE \`role\` DROP INDEX \`IDX_367aad98203bd8afaed0d70409\``); - - await queryRunner.query(`CREATE UNIQUE INDEX \`UQ_55c99c82f45936d8d34c48f5515\` ON \`webapi\` (\`token\`)`); - await queryRunner.query(`CREATE UNIQUE INDEX \`UQ_424ac388ca888a1dc67f5fe3a80\` ON \`webapi\` (\`title\`)`); - await queryRunner.query(`CREATE UNIQUE INDEX \`UQ_9ffb36037fb8d7456689956ed80\` ON \`query\` (\`title\`)`); - await queryRunner.query( - `CREATE UNIQUE INDEX \`UQ_6c6bdcaeb808549ae66c2493efd\` ON \`salutation\` (\`salutation\`)` - ); - } -} diff --git a/src/migrations/1738166124200-BackupAndResetDatabase.ts b/src/migrations/1738166124200-BackupAndResetDatabase.ts new file mode 100644 index 0000000..930d7cc --- /dev/null +++ b/src/migrations/1738166124200-BackupAndResetDatabase.ts @@ -0,0 +1,53 @@ +import { MigrationInterface, QueryRunner, Table } from "typeorm"; +import BackupHelper from "../helpers/backupHelper"; +import { getTypeByORM } from "../data-source"; +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"))) + 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 }); + + await queryRunner.clearDatabase(); + + await queryRunner.createTable( + new Table({ + name: "migrations", + columns: [ + { + name: "id", + type: getTypeByORM("int"), + isPrimary: true, + isGenerated: true, + generationStrategy: "increment", + }, + { name: "timestamp", type: getTypeByORM("bigint"), isNullable: false }, + { name: "name", type: getTypeByORM("varchar"), length: "255", isNullable: false }, + ], + }), + true + ); + + await queryRunner.createTable( + new Table({ + name: "typeorm_metadata", + columns: [ + { name: "type", type: getTypeByORM("varchar"), length: "255", isNullable: false }, + { name: "database", type: getTypeByORM("varchar"), length: "255", isNullable: true, default: null }, + { name: "schema", type: getTypeByORM("varchar"), length: "255", isNullable: true, default: null }, + { name: "table", type: getTypeByORM("varchar"), length: "255", isNullable: true, default: null }, + { name: "name", type: getTypeByORM("varchar"), length: "255", isNullable: true, default: null }, + { name: "value", type: getTypeByORM("text"), length: "255", isNullable: true, default: null }, + ], + }), + true + ); + } + + public async down(queryRunner: QueryRunner): Promise {} +} -- 2.45.3 From a91b723f045e674873a84eb03a97b9f5c27aa93d Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Thu, 30 Jan 2025 14:02:42 +0100 Subject: [PATCH 09/22] default arrays and none existant data inserts --- src/helpers/backupHelper.ts | 82 ++++++++++++++++++++++++++++++------- 1 file changed, 68 insertions(+), 14 deletions(-) diff --git a/src/helpers/backupHelper.ts b/src/helpers/backupHelper.ts index 8deff02..6d18f7a 100644 --- a/src/helpers/backupHelper.ts +++ b/src/helpers/backupHelper.ts @@ -20,7 +20,10 @@ export type BackupSectionRefered = { [key in BackupSection]?: Array; }; -export type BackupFileContent = { [key in BackupSection]?: BackupFileContentSection } & { collectIds: boolean }; +export type BackupFileContent = { [key in BackupSection]?: BackupFileContentSection } & { + collectIds: boolean; + version: 1; +}; export type BackupFileContentSection = Array | { [key: string]: Array }; export default abstract class BackupHelper { @@ -87,7 +90,7 @@ export default abstract class BackupHelper { filename = new Date().toISOString().split("T")[0]; } - let json: BackupFileContent = { collectIds }; + let json: BackupFileContent = { collectIds, version: 1 }; for (const section of this.backupSection) { json[section.type] = await this.getSectionData(section.type, collectIds); } @@ -424,6 +427,30 @@ export default abstract class BackupHelper { } private static async setMemberData(data: Array): Promise { + await this.setMemberBase({ + award: data + .map((d) => d.awards.map((c: any) => c.award)) + .flat() + .map((d) => ({ ...d, id: undefined })), + communication_type: data + .map((d) => d.communications.map((c: any) => c.type)) + .flat() + .map((d) => ({ ...d, id: undefined })), + executive_position: data + .map((d) => d.positions.map((c: any) => c.executivePosition)) + .flat() + .map((d) => ({ ...d, id: undefined })), + membership_status: data + .map((d) => d.memberships.map((c: any) => c.status)) + .flat() + .map((d) => ({ ...d, id: undefined })), + salutation: data.map((d) => d.salutation).map((d) => ({ ...d, id: undefined })), + qualification: data + .map((d) => d.qualifications.map((c: any) => c.qualification)) + .flat() + .map((d) => ({ ...d, id: undefined })), + }); + let salutation = await this.transactionManager.getRepository("salutation").find(); let communication = await this.transactionManager.getRepository("communication_type").find(); let membership = await this.transactionManager.getRepository("membership_status").find(); @@ -479,42 +506,42 @@ export default abstract class BackupHelper { .createQueryBuilder() .insert() .into("award") - .values(data["award"]) + .values(data?.["award"] ?? []) .orIgnore() .execute(); await this.transactionManager .createQueryBuilder() .insert() .into("communication_type") - .values(data["communication_type"]) + .values(data?.["communication_type"] ?? []) .orIgnore() .execute(); await this.transactionManager .createQueryBuilder() .insert() .into("executive_position") - .values(data["executive_position"]) + .values(data?.["executive_position"] ?? []) .orIgnore() .execute(); await this.transactionManager .createQueryBuilder() .insert() .into("membership_status") - .values(data["membership_status"]) + .values(data?.["membership_status"] ?? []) .orIgnore() .execute(); await this.transactionManager .createQueryBuilder() .insert() .into("salutation") - .values(data["salutation"]) + .values(data?.["salutation"] ?? []) .orIgnore() .execute(); await this.transactionManager .createQueryBuilder() .insert() .into("qualification") - .values(data["qualification"]) + .values(data?.["qualification"] ?? []) .orIgnore() .execute(); } @@ -543,6 +570,8 @@ export default abstract class BackupHelper { await this.transactionManager.getRepository("protocol").save(dataWithMappedIds); } private static async setNewsletter(data: Array, collectedIds: boolean): Promise { + await this.setQueryStore(data.map((d) => d.recipientsByQueryId).map((d) => ({ ...d, id: undefined }))); + let queries = await this.transactionManager.getRepository("query").find(); let members = await this.transactionManager.getRepository("member").find(); let dataWithMappedIds = data.map((d) => ({ @@ -572,6 +601,10 @@ export default abstract class BackupHelper { await this.transactionManager.getRepository("newsletter").save(dataWithMappedIds); } private static async setNewsletterConfig(data: Array): Promise { + await this.setMemberBase({ + communication_type: data.map((d) => d.comType).map((d) => ({ ...d, id: undefined })), + }); + let types = await this.transactionManager.getRepository("communication_type").find(); let dataWithMappedIds = data.map((d) => ({ ...d, @@ -583,9 +616,18 @@ export default abstract class BackupHelper { await this.transactionManager.getRepository("newsletter_config").save(dataWithMappedIds); } private static async setCalendar(data: { [key: string]: Array }): Promise { - await this.transactionManager.getRepository("calendar_type").save(data["calendar_type"]); + let usedTypes = (data?.["calendar"] ?? []).map((d) => d.type).map((d) => ({ ...d, id: undefined })); + + await this.transactionManager + .createQueryBuilder() + .insert() + .into("award") + .values([...(data?.["calendar_type"] ?? []), ...usedTypes]) + .orIgnore() + .execute(); + let types = await this.transactionManager.getRepository("calendar_type").find(); - let dataWithMappedIds = data["calendar"].map((c) => ({ + let dataWithMappedIds = (data?.["calendar"] ?? []).map((c) => ({ ...c, type: { ...c.type, @@ -602,21 +644,33 @@ export default abstract class BackupHelper { .createQueryBuilder() .insert() .into("template") - .values(data["template"]) + .values(data?.["template"] ?? []) .orIgnore() .execute(); await this.transactionManager .createQueryBuilder() .insert() .into("template_usage") - .values(data["template_usage"]) + .values(data?.["template_usage"] ?? []) .orIgnore() .execute(); } private static async setUser(data: { [key: string]: Array }): Promise { - await this.transactionManager.createQueryBuilder().insert().into("role").values(data["role"]).orIgnore().execute(); + let usedRoles = (data?.["user"] ?? []) + .map((d) => d.roles) + .flat() + .map((d) => ({ ...d, id: undefined })); + + await this.transactionManager + .createQueryBuilder() + .insert() + .into("role") + .values([...(data?.["role"] ?? []), ...usedRoles]) + .orIgnore() + .execute(); + let roles = await this.transactionManager.getRepository("role").find(); - let dataWithMappedIds = data["user"].map((u) => ({ + let dataWithMappedIds = (data?.["user"] ?? []).map((u) => ({ ...u, roles: u.roles.map((r: any) => ({ ...r, -- 2.45.3 From b7b6694407f40c5710659cdbb28c1f76ddd7ded4 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Thu, 30 Jan 2025 15:58:34 +0100 Subject: [PATCH 10/22] backup and base schema creation --- src/data-source.ts | 42 +-- src/helpers/backupHelper.ts | 4 +- .../1738166124200-BackupAndResetDatabase.ts | 11 +- src/migrations/1738166167472-CreateSchema.ts | 160 +++++++++ src/migrations/baseSchemaTables/admin.ts | 168 +++++++++ src/migrations/baseSchemaTables/calendar.ts | 59 ++++ src/migrations/baseSchemaTables/member.ts | 322 ++++++++++++++++++ src/migrations/baseSchemaTables/newsletter.ts | 92 +++++ src/migrations/baseSchemaTables/protocol.ts | 121 +++++++ .../baseSchemaTables/query_template.ts | 57 ++++ src/migrations/ormHelper.ts | 40 +++ 11 files changed, 1030 insertions(+), 46 deletions(-) create mode 100644 src/migrations/1738166167472-CreateSchema.ts create mode 100644 src/migrations/baseSchemaTables/admin.ts create mode 100644 src/migrations/baseSchemaTables/calendar.ts create mode 100644 src/migrations/baseSchemaTables/member.ts create mode 100644 src/migrations/baseSchemaTables/newsletter.ts create mode 100644 src/migrations/baseSchemaTables/protocol.ts create mode 100644 src/migrations/baseSchemaTables/query_template.ts create mode 100644 src/migrations/ormHelper.ts 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; +} -- 2.45.3 From 0b7de992c8bd89a0fe2e916c45e18fc2fc71209d Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Fri, 31 Jan 2025 11:06:02 +0100 Subject: [PATCH 11/22] extend base schema for future features --- src/entity/club/calendar.ts | 3 +++ src/entity/settings/calendarType.ts | 5 ++++- src/entity/user/user.ts | 3 +++ src/migrations/baseSchemaTables/admin.ts | 1 + src/migrations/baseSchemaTables/calendar.ts | 11 ++++++++++- 5 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/entity/club/calendar.ts b/src/entity/club/calendar.ts index 63ebb4d..a91fc5a 100644 --- a/src/entity/club/calendar.ts +++ b/src/entity/club/calendar.ts @@ -43,6 +43,9 @@ export class calendar { @UpdateDateColumn() updatedAt: Date; + @Column({ type: "varchar", nullable: true, default: null, unique: true }) + webpageId: string; + @ManyToOne(() => calendarType, (t) => t.calendar, { nullable: false, onDelete: "RESTRICT", diff --git a/src/entity/settings/calendarType.ts b/src/entity/settings/calendarType.ts index acef5e5..735dacf 100644 --- a/src/entity/settings/calendarType.ts +++ b/src/entity/settings/calendarType.ts @@ -9,7 +9,7 @@ export class calendarType { @Column({ type: "varchar", length: 255, unique: true }) type: string; - @Column({ type: "boolean" }) // none specified cal dav request + @Column({ type: "boolean", default: false }) // none specified cal dav request nscdr: boolean; @Column({ type: "varchar", length: 255 }) @@ -18,6 +18,9 @@ export class calendarType { @Column({ type: "varchar", length: 255, nullable: true, default: null }) passphrase: string | null; + @Column({ type: "boolean", default: false }) + sendToWebpage: boolean; + @OneToMany(() => calendar, (c) => c.type, { nullable: false, onDelete: "RESTRICT", diff --git a/src/entity/user/user.ts b/src/entity/user/user.ts index 0a0a7a7..94ab3a3 100644 --- a/src/entity/user/user.ts +++ b/src/entity/user/user.ts @@ -22,6 +22,9 @@ export class user { @Column({ type: "varchar", length: 255 }) secret: string; + @Column({ type: "boolean", default: false }) + static: boolean; + @Column({ type: "boolean", default: false }) isOwner: boolean; diff --git a/src/migrations/baseSchemaTables/admin.ts b/src/migrations/baseSchemaTables/admin.ts index 23dbca9..9ad6437 100644 --- a/src/migrations/baseSchemaTables/admin.ts +++ b/src/migrations/baseSchemaTables/admin.ts @@ -59,6 +59,7 @@ export const user_table = new Table({ { 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: "static", type: getTypeByORM("boolean"), isNullable: false, default: false }, { name: "isOwner", type: getTypeByORM("boolean"), isNullable: false, default: false }, ], }); diff --git a/src/migrations/baseSchemaTables/calendar.ts b/src/migrations/baseSchemaTables/calendar.ts index ed49571..5abc49b 100644 --- a/src/migrations/baseSchemaTables/calendar.ts +++ b/src/migrations/baseSchemaTables/calendar.ts @@ -6,9 +6,10 @@ export const calendar_type_table = new Table({ 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: "nscdr", type: getTypeByORM("boolean"), isNullable: false, default: false }, { name: "color", type: getTypeByORM("varchar"), length: "255", isNullable: false }, { name: "passphrase", type: getTypeByORM("varchar"), length: "255", isNullable: true }, + { name: "sendToWebpage", type: getTypeByORM("boolean"), isNullable: false, default: false }, ], }); @@ -45,6 +46,14 @@ export const calendar_table = new Table({ default: "CURRENT_TIMESTAMP(6)", onUpdate: "CURRENT_TIMESTAMP(6)", }, + { + name: "webpageId", + type: getTypeByORM("varchar"), + length: "255", + isNullable: true, + default: null, + isUnique: true, + }, { name: "typeId", type: getTypeByORM("int"), isNullable: false }, ], foreignKeys: [ -- 2.45.3 From 668d8448da1a9d56b917e4b3e9e92a6c9994cef5 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Fri, 31 Jan 2025 11:07:10 +0100 Subject: [PATCH 12/22] fix: newsletter recipients with type --- src/service/club/newsletter/newsletterRecipientsService.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/service/club/newsletter/newsletterRecipientsService.ts b/src/service/club/newsletter/newsletterRecipientsService.ts index 28bed59..ec6d846 100644 --- a/src/service/club/newsletter/newsletterRecipientsService.ts +++ b/src/service/club/newsletter/newsletterRecipientsService.ts @@ -14,7 +14,12 @@ export default abstract class NewsletterRecipientsService { .getRepository(newsletterRecipients) .createQueryBuilder("newsletterRecipients") .leftJoinAndSelect("newsletterRecipients.member", "member") - .leftJoinAndSelect("member.sendNewsletter", "sendNewsletter") + .leftJoinAndMapOne( + "member.sendNewsletter", + "member.communications", + "sendNewsletter", + "sendNewsletter.isSendNewsletter = 1" + ) .leftJoinAndSelect("sendNewsletter.type", "communicationtype") .leftJoinAndSelect("newsletterRecipients.newsletter", "newsletter") .where("newsletterRecipients.newsletterId = :id", { id: newsletterId }) -- 2.45.3 From e17eb30aed10d9f869acd300eaa31d2177ed1972 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Fri, 31 Jan 2025 11:07:58 +0100 Subject: [PATCH 13/22] change: allow read to related data from allowed modules --- src/helpers/permissionHelper.ts | 77 ++++++++++++++++++++--- src/routes/admin/index.ts | 104 ++++++++++++++++++++++++++++---- 2 files changed, 158 insertions(+), 23 deletions(-) diff --git a/src/helpers/permissionHelper.ts b/src/helpers/permissionHelper.ts index 990e97d..86b27e2 100644 --- a/src/helpers/permissionHelper.ts +++ b/src/helpers/permissionHelper.ts @@ -32,6 +32,19 @@ export default class PermissionHelper { return false; } + static canSome( + permissions: PermissionObject, + checks: Array<{ + requiredPermissions: PermissionType | "admin"; + section: PermissionSection; + module?: PermissionModule; + }> + ) { + checks.reduce((prev, curr) => { + return prev || this.can(permissions, curr.requiredPermissions, curr.section, curr.module); + }, false); + } + static canSection( permissions: PermissionObject, type: PermissionType | "admin", @@ -48,6 +61,18 @@ export default class PermissionHelper { return false; } + static canSomeSection( + permissions: PermissionObject, + checks: Array<{ + requiredPermissions: PermissionType | "admin"; + section: PermissionSection; + }> + ): boolean { + return checks.reduce((prev, curr) => { + return prev || this.can(permissions, curr.requiredPermissions, curr.section); + }, false); + } + static passCheckMiddleware( requiredPermissions: PermissionType | "admin", section: PermissionSection, @@ -60,11 +85,29 @@ export default class PermissionHelper { if (isOwner || this.can(permissions, requiredPermissions, section, module)) { next(); } else { - throw new ForbiddenRequestException( - `missing permission for ${section}.${module}.${ - Array.isArray(requiredPermissions) ? requiredPermissions.join("|") : requiredPermissions - }` - ); + throw new ForbiddenRequestException(`missing permission for ${section}.${module}.${requiredPermissions}`); + } + }; + } + + static passCheckSomeMiddleware( + checks: Array<{ + requiredPermissions: PermissionType | "admin"; + section: PermissionSection; + module?: PermissionModule; + }> + ): (req: Request, res: Response, next: Function) => void { + return (req: Request, res: Response, next: Function) => { + const permissions = req.permissions; + const isOwner = req.isOwner; + + if (isOwner || this.canSome(permissions, checks)) { + next(); + } else { + let permissionsToPass = checks.reduce((prev, curr) => { + return prev + (prev != " or " ? "" : "") + `${curr.section}.${curr.module}.${curr.requiredPermissions}`; + }, ""); + throw new ForbiddenRequestException(`missing permission for ${permissionsToPass}`); } }; } @@ -80,11 +123,25 @@ export default class PermissionHelper { if (isOwner || this.canSection(permissions, requiredPermissions, section)) { next(); } else { - throw new ForbiddenRequestException( - `missing permission for ${section}.${module}.${ - Array.isArray(requiredPermissions) ? requiredPermissions.join("|") : requiredPermissions - }` - ); + throw new ForbiddenRequestException(`missing permission for ${section}.${module}.${requiredPermissions}`); + } + }; + } + + static sectionPassCheckSomeMiddleware( + checks: Array<{ requiredPermissions: PermissionType | "admin"; section: PermissionSection }> + ): (req: Request, res: Response, next: Function) => void { + return (req: Request, res: Response, next: Function) => { + const permissions = req.permissions; + const isOwner = req.isOwner; + + if (isOwner || this.canSomeSection(permissions, checks)) { + next(); + } else { + let permissionsToPass = checks.reduce((prev, curr) => { + return prev + (prev != " or " ? "" : "") + `${curr.section}.${curr.requiredPermissions}`; + }, ""); + throw new ForbiddenRequestException(`missing permission for ${permissionsToPass}`); } }; } diff --git a/src/routes/admin/index.ts b/src/routes/admin/index.ts index 016db3e..c291c15 100644 --- a/src/routes/admin/index.ts +++ b/src/routes/admin/index.ts @@ -27,42 +27,120 @@ import preventWebapiAccess from "../../middleware/preventWebApiAccess"; var router = express.Router({ mergeParams: true }); -router.use("/award", PermissionHelper.passCheckMiddleware("read", "settings", "award"), award); +router.use( + "/award", + PermissionHelper.passCheckSomeMiddleware([ + { requiredPermissions: "read", section: "settings", module: "award" }, + { requiredPermissions: "read", section: "club", module: "member" }, + ]), + award +); router.use( "/communicationtype", - PermissionHelper.passCheckMiddleware("read", "settings", "communication_type"), + PermissionHelper.passCheckSomeMiddleware([ + { requiredPermissions: "read", section: "settings", module: "communication_type" }, + { requiredPermissions: "read", section: "club", module: "member" }, + ]), communicationType ); router.use( "/executiveposition", - PermissionHelper.passCheckMiddleware("read", "settings", "executive_position"), + PermissionHelper.passCheckSomeMiddleware([ + { requiredPermissions: "read", section: "settings", module: "executive_position" }, + { requiredPermissions: "read", section: "club", module: "member" }, + ]), executivePosition ); router.use( "/membershipstatus", - PermissionHelper.passCheckMiddleware("read", "settings", "membership_status"), + PermissionHelper.passCheckSomeMiddleware([ + { requiredPermissions: "read", section: "settings", module: "membership_status" }, + { requiredPermissions: "read", section: "club", module: "member" }, + ]), membershipStatus ); -router.use("/qualification", PermissionHelper.passCheckMiddleware("read", "settings", "qualification"), qualification); -router.use("/salutation", PermissionHelper.passCheckMiddleware("read", "settings", "salutation"), salutation); -router.use("/calendartype", PermissionHelper.passCheckMiddleware("read", "settings", "calendar_type"), calendarType); +router.use( + "/qualification", + PermissionHelper.passCheckSomeMiddleware([ + { requiredPermissions: "read", section: "settings", module: "qualification" }, + { requiredPermissions: "read", section: "club", module: "member" }, + ]), + qualification +); +router.use( + "/salutation", + PermissionHelper.passCheckSomeMiddleware([ + { requiredPermissions: "read", section: "settings", module: "salutation" }, + { requiredPermissions: "read", section: "club", module: "member" }, + ]), + salutation +); +router.use( + "/calendartype", + PermissionHelper.passCheckSomeMiddleware([ + { requiredPermissions: "read", section: "settings", module: "calendar_type" }, + { requiredPermissions: "read", section: "club", module: "calendar" }, + ]), + calendarType +); router.use("/querystore", PermissionHelper.passCheckMiddleware("read", "settings", "query_store"), queryStore); router.use("/template", PermissionHelper.passCheckMiddleware("read", "settings", "template"), template); -router.use("/templateusage", PermissionHelper.passCheckMiddleware("read", "settings", "template_usage"), templateUsage); +router.use( + "/templateusage", + PermissionHelper.passCheckSomeMiddleware([ + { requiredPermissions: "read", section: "settings", module: "template_usage" }, + { requiredPermissions: "read", section: "settings", module: "template" }, + ]), + templateUsage +); router.use( "/newsletterconfig", - PermissionHelper.passCheckMiddleware("read", "settings", "newsletter_config"), + PermissionHelper.passCheckSomeMiddleware([ + { requiredPermissions: "read", section: "settings", module: "newsletter_config" }, + { requiredPermissions: "read", section: "settings", module: "communication_type" }, + ]), newsletterConfig ); router.use("/member", PermissionHelper.passCheckMiddleware("read", "club", "member"), member); -router.use("/protocol", PermissionHelper.passCheckMiddleware("read", "club", "protocol"), protocol); +router.use( + "/protocol", + PermissionHelper.passCheckSomeMiddleware([ + { requiredPermissions: "read", section: "club", module: "protocol" }, + { requiredPermissions: "read", section: "club", module: "member" }, + ]), + protocol +); router.use("/calendar", PermissionHelper.passCheckMiddleware("read", "club", "calendar"), calendar); -router.use("/querybuilder", PermissionHelper.passCheckMiddleware("read", "club", "query"), queryBuilder); -router.use("/newsletter", PermissionHelper.passCheckMiddleware("read", "club", "newsletter"), newsletter); +router.use( + "/querybuilder", + PermissionHelper.passCheckSomeMiddleware([ + { requiredPermissions: "read", section: "club", module: "query" }, + { requiredPermissions: "read", section: "settings", module: "query_store" }, + ]), + queryBuilder +); +router.use( + "/newsletter", + PermissionHelper.passCheckSomeMiddleware([ + { requiredPermissions: "read", section: "club", module: "newsletter" }, + { requiredPermissions: "read", section: "club", module: "member" }, + { requiredPermissions: "read", section: "club", module: "calendar" }, + { requiredPermissions: "read", section: "club", module: "query" }, + { requiredPermissions: "read", section: "settings", module: "query_store" }, + ]), + newsletter +); router.use("/role", PermissionHelper.passCheckMiddleware("read", "user", "role"), role); -router.use("/user", PermissionHelper.passCheckMiddleware("read", "user", "user"), user); +router.use( + "/user", + PermissionHelper.passCheckSomeMiddleware([ + { requiredPermissions: "read", section: "user", module: "user" }, + { requiredPermissions: "read", section: "user", module: "role" }, + ]), + user +); router.use("/invite", PermissionHelper.passCheckMiddleware("read", "user", "user"), invite); router.use("/webapi", preventWebapiAccess, PermissionHelper.passCheckMiddleware("read", "user", "webapi"), api); -- 2.45.3 From 9a1f4a298577ca5c2a66c38f5e1f413dac0ca464 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Fri, 31 Jan 2025 11:10:08 +0100 Subject: [PATCH 14/22] extend backup file with file details --- src/helpers/backupHelper.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/helpers/backupHelper.ts b/src/helpers/backupHelper.ts index a7fbc1d..4507542 100644 --- a/src/helpers/backupHelper.ts +++ b/src/helpers/backupHelper.ts @@ -21,8 +21,7 @@ export type BackupSectionRefered = { }; export type BackupFileContent = { [key in BackupSection]?: BackupFileContentSection } & { - collectIds: boolean; - version: 1; + backup_file_details: { collectIds: boolean; createdAt: Date; version: 1 }; }; export type BackupFileContentSection = Array | { [key: string]: Array }; @@ -90,7 +89,7 @@ export default abstract class BackupHelper { filename = new Date().toISOString().split("T")[0]; } - let json: BackupFileContent = { collectIds, version: 1 }; + let json: BackupFileContent = { backup_file_details: { collectIds, createdAt: new Date(), version: 1 } }; for (const section of this.backupSection) { json[section.type] = await this.getSectionData(section.type, collectIds); } @@ -131,7 +130,7 @@ export default abstract class BackupHelper { for (const section of sections .filter((s) => Object.keys(backup).includes(s.type)) .sort((a, b) => a.orderOnInsert - b.orderOnInsert)) { - await this.setSectionData(section.type, backup[section.type], backup.collectIds ?? false); + await this.setSectionData(section.type, backup[section.type], backup.backup_file_details.collectIds ?? false); } this.transactionManager = undefined; -- 2.45.3 From 0994274c0c957339d0b5d3a1360a09c7b0cba97d Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Fri, 31 Jan 2025 11:25:00 +0100 Subject: [PATCH 15/22] update prevention message --- src/migrations/1738166124200-BackupAndResetDatabase.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/migrations/1738166124200-BackupAndResetDatabase.ts b/src/migrations/1738166124200-BackupAndResetDatabase.ts index f394fc8..0c07dca 100644 --- a/src/migrations/1738166124200-BackupAndResetDatabase.ts +++ b/src/migrations/1738166124200-BackupAndResetDatabase.ts @@ -8,7 +8,9 @@ export class BackupAndResetDatabase1738166124200 implements MigrationInterface { public async up(queryRunner: QueryRunner): Promise { if ((await queryRunner.hasTable("user")) && !(await queryRunner.hasTable("salutation"))) { - throw new InternalException("Cannot update due to skiped version, resulting in data loss"); + throw new InternalException( + "Cannot update due to skiped version. Update to v1.2.2 Version first to prevent data loss and get access to the newer Versions." + ); } if (await queryRunner.hasTable("user")) { -- 2.45.3 From ee60f497fa4ce1ca3ee718a690cecbbaa66c6c7a Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Fri, 31 Jan 2025 13:58:07 +0100 Subject: [PATCH 16/22] improved backup restore --- package-lock.json | 25 ++++ package.json | 2 + src/helpers/backupHelper.ts | 110 ++++++++++++------ .../1738166124200-BackupAndResetDatabase.ts | 6 +- 4 files changed, 109 insertions(+), 34 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7b6f159..39f6b73 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "handlebars": "^4.7.8", "ics": "^3.8.1", "jsonwebtoken": "^9.0.2", + "lodash.uniqby": "^4.7.0", "moment": "^2.30.1", "ms": "^2.1.3", "mysql": "^2.18.1", @@ -34,6 +35,7 @@ "@types/cors": "^2.8.14", "@types/express": "^4.17.17", "@types/jsonwebtoken": "^9.0.6", + "@types/lodash.uniqby": "^4.7.9", "@types/ms": "^0.7.34", "@types/mysql": "^2.15.21", "@types/node": "^16.18.41", @@ -478,6 +480,23 @@ "@types/node": "*" } }, + "node_modules/@types/lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-w/P33JFeySuhN6JLkysYUK2gEmy9kHHFN7E8ro0tkfmlDOgxBDzWEZ/J8cWA+fHqFevpswDTFZnDx+R9lbL6xw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/lodash.uniqby": { + "version": "4.7.9", + "resolved": "https://registry.npmjs.org/@types/lodash.uniqby/-/lodash.uniqby-4.7.9.tgz", + "integrity": "sha512-rjrXji/seS6BZJRgXrU2h6FqxRVufsbq/HE0Tx0SdgbtlWr2YmD/M64BlYEYYlaMcpZwy32IYVkMfUMYlPuv0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/lodash": "*" + } + }, "node_modules/@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", @@ -2310,6 +2329,12 @@ "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", "license": "MIT" }, + "node_modules/lodash.uniqby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz", + "integrity": "sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==", + "license": "MIT" + }, "node_modules/long-timeout": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/long-timeout/-/long-timeout-0.1.1.tgz", diff --git a/package.json b/package.json index bb2e766..3eace88 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "handlebars": "^4.7.8", "ics": "^3.8.1", "jsonwebtoken": "^9.0.2", + "lodash.uniqby": "^4.7.0", "moment": "^2.30.1", "ms": "^2.1.3", "mysql": "^2.18.1", @@ -49,6 +50,7 @@ "@types/cors": "^2.8.14", "@types/express": "^4.17.17", "@types/jsonwebtoken": "^9.0.6", + "@types/lodash.uniqby": "^4.7.9", "@types/ms": "^0.7.34", "@types/mysql": "^2.15.21", "@types/node": "^16.18.41", diff --git a/src/helpers/backupHelper.ts b/src/helpers/backupHelper.ts index 4507542..1471974 100644 --- a/src/helpers/backupHelper.ts +++ b/src/helpers/backupHelper.ts @@ -1,6 +1,7 @@ import { dataSource } from "../data-source"; import { FileSystemHelper } from "./fileSystemHelper"; import { EntityManager } from "typeorm"; +import uniqBy from "lodash.uniqby"; import InternalException from "../exceptions/internalException"; import UserService from "../service/user/userService"; @@ -137,7 +138,7 @@ export default abstract class BackupHelper { }) .catch((err) => { this.transactionManager = undefined; - throw new InternalException("failed to restore backup - rolling back actions"); + throw new InternalException("failed to restore backup - rolling back actions", err); }); } @@ -427,27 +428,45 @@ export default abstract class BackupHelper { private static async setMemberData(data: Array): Promise { await this.setMemberBase({ - award: data - .map((d) => d.awards.map((c: any) => c.award)) - .flat() - .map((d) => ({ ...d, id: undefined })), - communication_type: data - .map((d) => d.communications.map((c: any) => c.type)) - .flat() - .map((d) => ({ ...d, id: undefined })), - executive_position: data - .map((d) => d.positions.map((c: any) => c.executivePosition)) - .flat() - .map((d) => ({ ...d, id: undefined })), - membership_status: data - .map((d) => d.memberships.map((c: any) => c.status)) - .flat() - .map((d) => ({ ...d, id: undefined })), - salutation: data.map((d) => d.salutation).map((d) => ({ ...d, id: undefined })), - qualification: data - .map((d) => d.qualifications.map((c: any) => c.qualification)) - .flat() - .map((d) => ({ ...d, id: undefined })), + award: uniqBy( + data + .map((d) => d.awards.map((c: any) => c.award)) + .flat() + .map((d) => ({ ...d, id: undefined })), + "award" + ), + communication_type: uniqBy( + data + .map((d) => d.communications.map((c: any) => c.type)) + .flat() + .map((d) => ({ ...d, id: undefined })), + "type" + ), + executive_position: uniqBy( + data + .map((d) => d.positions.map((c: any) => c.executivePosition)) + .flat() + .map((d) => ({ ...d, id: undefined })), + "position" + ), + membership_status: uniqBy( + data + .map((d) => d.memberships.map((c: any) => c.status)) + .flat() + .map((d) => ({ ...d, id: undefined })), + "status" + ), + salutation: uniqBy( + data.map((d) => d.salutation).map((d) => ({ ...d, id: undefined })), + "salutation" + ), + qualification: uniqBy( + data + .map((d) => d.qualifications.map((c: any) => c.qualification)) + .flat() + .map((d) => ({ ...d, id: undefined })), + "qualification" + ), }); let salutation = await this.transactionManager.getRepository("salutation").find(); @@ -501,46 +520,55 @@ export default abstract class BackupHelper { await this.transactionManager.getRepository("member").save(dataWithMappedIds); } private static async setMemberBase(data: { [key: string]: Array }): Promise { + let salutation = await this.transactionManager.getRepository("salutation").find(); + let communication = await this.transactionManager.getRepository("communication_type").find(); + let membership = await this.transactionManager.getRepository("membership_status").find(); + let award = await this.transactionManager.getRepository("award").find(); + let qualification = await this.transactionManager.getRepository("qualification").find(); + let position = await this.transactionManager.getRepository("executive_position").find(); + await this.transactionManager .createQueryBuilder() .insert() .into("award") - .values(data?.["award"] ?? []) + .values((data?.["award"] ?? []).filter((d) => !award.map((a) => a.award).includes(d.award))) .orIgnore() .execute(); await this.transactionManager .createQueryBuilder() .insert() .into("communication_type") - .values(data?.["communication_type"] ?? []) + .values((data?.["communication_type"] ?? []).filter((d) => !communication.map((c) => c.type).includes(d.type))) .orIgnore() .execute(); await this.transactionManager .createQueryBuilder() .insert() .into("executive_position") - .values(data?.["executive_position"] ?? []) + .values((data?.["executive_position"] ?? []).filter((d) => !position.map((p) => p.position).includes(d.position))) .orIgnore() .execute(); await this.transactionManager .createQueryBuilder() .insert() .into("membership_status") - .values(data?.["membership_status"] ?? []) + .values((data?.["membership_status"] ?? []).filter((d) => !membership.map((m) => m.status).includes(d.status))) .orIgnore() .execute(); await this.transactionManager .createQueryBuilder() .insert() .into("salutation") - .values(data?.["salutation"] ?? []) + .values((data?.["salutation"] ?? []).filter((d) => !salutation.map((s) => s.salutation).includes(d.salutation))) .orIgnore() .execute(); await this.transactionManager .createQueryBuilder() .insert() .into("qualification") - .values(data?.["qualification"] ?? []) + .values( + (data?.["qualification"] ?? []).filter((d) => !qualification.map((q) => q.award).includes(d.qualification)) + ) .orIgnore() .execute(); } @@ -569,7 +597,12 @@ export default abstract class BackupHelper { await this.transactionManager.getRepository("protocol").save(dataWithMappedIds); } private static async setNewsletter(data: Array, collectedIds: boolean): Promise { - await this.setQueryStore(data.map((d) => d.recipientsByQueryId).map((d) => ({ ...d, id: undefined }))); + await this.setQueryStore( + uniqBy( + data.map((d) => d.recipientsByQueryId).map((d) => ({ ...d, id: undefined })), + "query" + ) + ); let queries = await this.transactionManager.getRepository("query").find(); let members = await this.transactionManager.getRepository("member").find(); @@ -601,7 +634,10 @@ export default abstract class BackupHelper { } private static async setNewsletterConfig(data: Array): Promise { await this.setMemberBase({ - communication_type: data.map((d) => d.comType).map((d) => ({ ...d, id: undefined })), + communication_type: uniqBy( + data.map((d) => d.comType).map((d) => ({ ...d, id: undefined })), + "type" + ), }); let types = await this.transactionManager.getRepository("communication_type").find(); @@ -621,7 +657,7 @@ export default abstract class BackupHelper { .createQueryBuilder() .insert() .into("award") - .values([...(data?.["calendar_type"] ?? []), ...usedTypes]) + .values(uniqBy([...(data?.["calendar_type"] ?? []), ...usedTypes], "type")) .orIgnore() .execute(); @@ -636,7 +672,15 @@ export default abstract class BackupHelper { await this.transactionManager.getRepository("calendar").save(dataWithMappedIds); } private static async setQueryStore(data: Array): Promise { - await this.transactionManager.createQueryBuilder().insert().into("query").values(data).orIgnore().execute(); + let query = await this.transactionManager.getRepository("query").find(); + + await this.transactionManager + .createQueryBuilder() + .insert() + .into("query") + .values(data.filter((d) => !query.map((s) => s.query).includes(d.query))) + .orIgnore() + .execute(); } private static async setTemplate(data: { [key: string]: Array }): Promise { await this.transactionManager @@ -664,7 +708,7 @@ export default abstract class BackupHelper { .createQueryBuilder() .insert() .into("role") - .values([...(data?.["role"] ?? []), ...usedRoles]) + .values(uniqBy([...(data?.["role"] ?? []), ...usedRoles], "role")) .orIgnore() .execute(); diff --git a/src/migrations/1738166124200-BackupAndResetDatabase.ts b/src/migrations/1738166124200-BackupAndResetDatabase.ts index 0c07dca..4a190c5 100644 --- a/src/migrations/1738166124200-BackupAndResetDatabase.ts +++ b/src/migrations/1738166124200-BackupAndResetDatabase.ts @@ -7,7 +7,11 @@ export class BackupAndResetDatabase1738166124200 implements MigrationInterface { name = "BackupAndResetDatabase1738166124200"; public async up(queryRunner: QueryRunner): Promise { - if ((await queryRunner.hasTable("user")) && !(await queryRunner.hasTable("salutation"))) { + let migrations = await queryRunner.query("SELECT `name` FROM `migrations`"); + if ( + (await queryRunner.hasTable("user")) && + migrations.findIndex((m: any) => m.name == "MoveSendNewsletterFlag1737816852011") == -1 + ) { throw new InternalException( "Cannot update due to skiped version. Update to v1.2.2 Version first to prevent data loss and get access to the newer Versions." ); -- 2.45.3 From 2cee8b5119d8833c6b1e5d8fdfd62e567cea7fd4 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Fri, 31 Jan 2025 17:48:20 +0100 Subject: [PATCH 17/22] calendar externa prefix --- src/entity/settings/calendarType.ts | 3 +++ src/migrations/baseSchemaTables/calendar.ts | 1 + 2 files changed, 4 insertions(+) diff --git a/src/entity/settings/calendarType.ts b/src/entity/settings/calendarType.ts index 735dacf..d126ccd 100644 --- a/src/entity/settings/calendarType.ts +++ b/src/entity/settings/calendarType.ts @@ -18,6 +18,9 @@ export class calendarType { @Column({ type: "varchar", length: 255, nullable: true, default: null }) passphrase: string | null; + @Column({ type: "varchar", length: 255, nullable: false, default: "" }) + externalPrefix: string; + @Column({ type: "boolean", default: false }) sendToWebpage: boolean; diff --git a/src/migrations/baseSchemaTables/calendar.ts b/src/migrations/baseSchemaTables/calendar.ts index 5abc49b..793b2cc 100644 --- a/src/migrations/baseSchemaTables/calendar.ts +++ b/src/migrations/baseSchemaTables/calendar.ts @@ -9,6 +9,7 @@ export const calendar_type_table = new Table({ { name: "nscdr", type: getTypeByORM("boolean"), isNullable: false, default: false }, { name: "color", type: getTypeByORM("varchar"), length: "255", isNullable: false }, { name: "passphrase", type: getTypeByORM("varchar"), length: "255", isNullable: true }, + { name: "externalPrefix", type: getTypeByORM("varchar"), length: "255", isNullable: false, default: "''" }, { name: "sendToWebpage", type: getTypeByORM("boolean"), isNullable: false, default: false }, ], }); -- 2.45.3 From a73c7126269eb58d8cb147f0b02ab69381c215e0 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Sat, 1 Feb 2025 13:11:10 +0100 Subject: [PATCH 18/22] transform migrations to work with mysql, postgres and sqlite sqlite currently fails syntax of views --- .env.example | 16 +- .gitignore | 3 +- README.md | 20 +- package-lock.json | 1170 ++++++++++++++++- package.json | 1 + src/data-source.ts | 2 +- src/entity/club/calendar.ts | 6 +- src/entity/club/member/member.ts | 5 +- src/entity/club/member/memberAwards.ts | 5 +- .../club/member/memberExecutivePositions.ts | 7 +- .../club/member/memberQualifications.ts | 7 +- src/entity/club/member/membership.ts | 7 +- src/entity/club/protocol/protocol.ts | 9 +- src/entity/refresh.ts | 5 +- src/entity/user/webapi.ts | 7 +- src/env.defaults.ts | 14 +- .../1738166124200-BackupAndResetDatabase.ts | 30 +- src/migrations/1738166167472-CreateSchema.ts | 17 +- src/migrations/baseSchemaTables/admin.ts | 95 +- src/migrations/baseSchemaTables/calendar.ts | 66 +- src/migrations/baseSchemaTables/member.ts | 238 ++-- src/migrations/baseSchemaTables/newsletter.ts | 34 +- src/migrations/baseSchemaTables/protocol.ts | 64 +- .../baseSchemaTables/query_template.ts | 30 +- src/migrations/ormHelper.ts | 79 +- src/views/memberExecutivePositionView.ts | 17 +- src/views/memberQualificationsView.ts | 17 +- src/views/memberView.ts | 22 +- src/views/membershipsView.ts | 15 +- 29 files changed, 1626 insertions(+), 382 deletions(-) diff --git a/.env.example b/.env.example index 4dc7854..d71fb73 100644 --- a/.env.example +++ b/.env.example @@ -1,10 +1,22 @@ -DB_TYPE = (mysql|sqlite|...) -DB_PORT = number +DB_TYPE = (mysql|sqlite|postgres) + +## BSP für mysql +DB_PORT = 3306 DB_HOST = database_host DB_NAME = database_name DB_USERNAME = database_username DB_PASSWORD = database_password +## BSP für postgres +DB_PORT = 5432 +DB_HOST = database_host +DB_NAME = database_name +DB_USERNAME = database_username +DB_PASSWORD = database_password + +## BSP für sqlite +DB_HOST = filename.db + SERVER_PORT = portnumber JWT_SECRET = ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 diff --git a/.gitignore b/.gitignore index 7efd6b5..7410d05 100644 --- a/.gitignore +++ b/.gitignore @@ -132,4 +132,5 @@ dist files -.idea \ No newline at end of file +.idea +*.db \ No newline at end of file diff --git a/README.md b/README.md index 93add1a..432cc37 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ services: container_name: ff_member_administration_server restart: unless-stopped environment: - - DB_TYPE= # default ist auf mysql gesetzt + - DB_TYPE= # default ist auf mysql gesetzt - DB_HOST=ff-db - DB_PORT= # default ist auf 3306 gesetzt - DB_NAME=ffadmin @@ -44,7 +44,7 @@ services: - CLUB_WEBSITE= - BACKUP_INTERVAL= # alle x Tage, sonst keine - BACKUP_COPIES= # Anzahl parallel bestehender Backups - - BACKUP_AUTO_RESTORE= # default ist auf false gesetzt + - BACKUP_AUTO_RESTORE= # default ist auf true gesetzt volumes: - :/app/files networks: @@ -65,11 +65,27 @@ services: - :/var/lib/mysql networks: - ff_internal + # OR + image: postgres:16 + container_name: ff_db + restart: unless-stopped + environment: + - POSTGRES_DB=ffadmin + - POSTGRES_USER=administration_backend + - POSTGRES_PASSWORD= + volumes: + - :/var/lib/postgresql/data + networks: + - ff_internal networks: ff_internal: ``` +Die Verwendung von postgres wird aufgrund des Verhaltens bei Struktur-Update-Fehlern und genauerer Datum-Berechnungen empfohlen. + +Die Verwendung von SQLite wird nur für die Entwicklung oder lokale Tests empfohlen. + Führen Sie dann den folgenden Befehl im Verzeichnis der compose-Datei aus, um den Container zu starten: ```sh diff --git a/package-lock.json b/package-lock.json index 39f6b73..75ebbc6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,6 +28,7 @@ "rss-parser": "^3.13.0", "socket.io": "^4.7.5", "speakeasy": "^2.0.0", + "sqlite3": "^5.1.7", "typeorm": "^0.3.20", "uuid": "^10.0.0" }, @@ -92,6 +93,13 @@ "node": ">=12" } }, + "node_modules/@gar/promisify": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", + "license": "MIT", + "optional": true + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -181,6 +189,45 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/@npmcli/fs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", + "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" + } + }, + "node_modules/@npmcli/move-file": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "deprecated": "This functionality has been moved to @npmcli/fs", + "license": "MIT", + "optional": true, + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/move-file/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "license": "MIT", + "optional": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@pdf-lib/standard-fonts": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@pdf-lib/standard-fonts/-/standard-fonts-1.0.0.tgz", @@ -378,6 +425,16 @@ "resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.5.tgz", "integrity": "sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==" }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/@tootallnate/quickjs-emscripten": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", @@ -610,6 +667,13 @@ "@types/node": "*" } }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "license": "ISC", + "optional": true + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -655,6 +719,33 @@ "node": ">= 14" } }, + "node_modules/agentkeepalive": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", + "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "license": "MIT", + "optional": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -690,6 +781,43 @@ "node": ">= 6.0.0" } }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "license": "ISC", + "optional": true + }, + "node_modules/are-we-there-yet": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", + "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "optional": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/are-we-there-yet/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "optional": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -842,8 +970,6 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "optional": true, - "peer": true, "dependencies": { "file-uri-to-path": "1.0.0" } @@ -852,8 +978,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "optional": true, - "peer": true, "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", @@ -878,8 +1002,6 @@ "url": "https://feross.org/support" } ], - "optional": true, - "peer": true, "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" @@ -889,8 +1011,6 @@ "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "optional": true, - "peer": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -989,6 +1109,131 @@ "node": ">= 0.8" } }, + "node_modules/cacache": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", + "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "@npmcli/fs": "^1.0.0", + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cacache/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "license": "MIT", + "optional": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/cacache/node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "license": "ISC", + "optional": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/cacache/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "optional": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/cacache/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cacache/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "optional": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/cacache/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cacache/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "license": "MIT", + "optional": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/call-bind": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", @@ -1042,9 +1287,7 @@ "node_modules/chownr": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "optional": true, - "peer": true + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" }, "node_modules/chromium-bidi": { "version": "0.11.0", @@ -1059,6 +1302,16 @@ "devtools-protocol": "*" } }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } + }, "node_modules/cli-highlight": { "version": "2.1.11", "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.11.tgz", @@ -1164,6 +1417,30 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "license": "ISC", + "optional": true, + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "license": "MIT", + "optional": true + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "license": "ISC", + "optional": true + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -1309,8 +1586,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "optional": true, - "peer": true, "dependencies": { "mimic-response": "^3.1.0" }, @@ -1325,8 +1600,6 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "optional": true, - "peer": true, "engines": { "node": ">=4.0.0" } @@ -1361,6 +1634,13 @@ "node": ">= 14" } }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "license": "MIT", + "optional": true + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -1382,8 +1662,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", - "optional": true, - "peer": true, "engines": { "node": ">=8" } @@ -1451,6 +1729,29 @@ "node": ">= 0.8" } }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -1534,6 +1835,13 @@ "node": ">=6" } }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "license": "MIT", + "optional": true + }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -1639,8 +1947,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "optional": true, - "peer": true, "engines": { "node": ">=6" } @@ -1720,9 +2026,7 @@ "node_modules/file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "optional": true, - "peer": true + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" }, "node_modules/finalhandler": { "version": "1.2.0", @@ -1787,9 +2091,38 @@ "node_modules/fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "optional": true, - "peer": true + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "license": "ISC", + "optional": true }, "node_modules/function-bind": { "version": "1.1.2", @@ -1799,6 +2132,34 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gauge": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "optional": true, + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/gauge/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC", + "optional": true + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -1874,9 +2235,7 @@ "node_modules/github-from-package": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", - "optional": true, - "peer": true + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==" }, "node_modules/glob": { "version": "10.4.5", @@ -1908,6 +2267,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC", + "optional": true + }, "node_modules/handlebars": { "version": "4.7.8", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", @@ -1970,6 +2336,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "license": "ISC", + "optional": true + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -1989,6 +2362,13 @@ "node": "*" } }, + "node_modules/http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", + "license": "BSD-2-Clause", + "optional": true + }, "node_modules/http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", @@ -2064,6 +2444,16 @@ } } }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "ms": "^2.0.0" + } + }, "node_modules/iconv-lite": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.2.tgz", @@ -2121,6 +2511,45 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "license": "ISC", + "optional": true + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "license": "ISC", + "optional": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -2129,9 +2558,7 @@ "node_modules/ini": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "optional": true, - "peer": true + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" }, "node_modules/ip-address": { "version": "9.0.5", @@ -2174,6 +2601,13 @@ "node": ">=8" } }, + "node_modules/is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", + "license": "MIT", + "optional": true + }, "node_modules/is-promise": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", @@ -2359,6 +2793,135 @@ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "devOptional": true }, + "node_modules/make-fetch-happen": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", + "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", + "license": "ISC", + "optional": true, + "dependencies": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.2.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.2", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^6.0.0", + "ssri": "^8.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/make-fetch-happen/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/make-fetch-happen/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "license": "MIT", + "optional": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/make-fetch-happen/node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/make-fetch-happen/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "optional": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/make-fetch-happen/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-fetch-happen/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/make-fetch-happen/node_modules/socks-proxy-agent": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", + "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -2403,8 +2966,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "optional": true, - "peer": true, "engines": { "node": ">=10" }, @@ -2442,6 +3003,166 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-collect/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-fetch": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", + "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", + "license": "MIT", + "optional": true, + "dependencies": { + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "optionalDependencies": { + "encoding": "^0.1.12" + } + }, + "node_modules/minipass-fetch/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/mitt": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", @@ -2465,9 +3186,7 @@ "node_modules/mkdirp-classic": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "optional": true, - "peer": true + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" }, "node_modules/moment": { "version": "2.30.1", @@ -2534,9 +3253,7 @@ "node_modules/napi-build-utils": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", - "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", - "optional": true, - "peer": true + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==" }, "node_modules/negotiator": { "version": "0.6.3", @@ -2565,8 +3282,6 @@ "version": "3.65.0", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.65.0.tgz", "integrity": "sha512-ThjYBfoDNr08AWx6hGaRbfPwxKV9kVzAzOzlLKbk2CuqXE2xnCh+cbAGnwM3t8Lq4v9rUB7VfondlkBckcJrVA==", - "optional": true, - "peer": true, "dependencies": { "semver": "^7.3.5" }, @@ -2574,6 +3289,83 @@ "node": ">=10" } }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "license": "MIT" + }, + "node_modules/node-gyp": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", + "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", + "license": "MIT", + "optional": true, + "dependencies": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^9.1.0", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": ">= 10.12.0" + } + }, + "node_modules/node-gyp/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "license": "MIT", + "optional": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/node-gyp/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "optional": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/node-gyp/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "optional": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/node-schedule": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/node-schedule/-/node-schedule-2.1.1.tgz", @@ -2595,6 +3387,39 @@ "node": ">=6.0.0" } }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/npmlog": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "optional": true, + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -2658,6 +3483,22 @@ "node": ">=8" } }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -2868,8 +3709,6 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz", "integrity": "sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==", - "optional": true, - "peer": true, "dependencies": { "detect-libc": "^2.0.0", "expand-template": "^2.0.3", @@ -2896,6 +3735,27 @@ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "license": "ISC", + "optional": true + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "license": "MIT", + "optional": true, + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/property-expr": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz", @@ -3112,8 +3972,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "optional": true, - "peer": true, "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", @@ -3170,6 +4028,79 @@ "node": ">=4" } }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "license": "ISC", + "optional": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "license": "MIT", + "optional": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "optional": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "optional": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/router": { "version": "2.0.0-beta.2", "resolved": "https://registry.npmjs.org/router/-/router-2.0.0-beta.2.tgz", @@ -3395,9 +4326,7 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "optional": true, - "peer": true + ] }, "node_modules/simple-get": { "version": "4.0.1", @@ -3417,8 +4346,6 @@ "url": "https://feross.org/support" } ], - "optional": true, - "peer": true, "dependencies": { "decompress-response": "^6.0.0", "once": "^1.3.1", @@ -3613,6 +4540,30 @@ "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", "license": "BSD-3-Clause" }, + "node_modules/sqlite3": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.1.7.tgz", + "integrity": "sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "bindings": "^1.5.0", + "node-addon-api": "^7.0.0", + "prebuild-install": "^7.1.1", + "tar": "^6.1.11" + }, + "optionalDependencies": { + "node-gyp": "8.x" + }, + "peerDependencies": { + "node-gyp": "8.x" + }, + "peerDependenciesMeta": { + "node-gyp": { + "optional": true + } + } + }, "node_modules/sqlstring": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz", @@ -3621,6 +4572,32 @@ "node": ">= 0.6" } }, + "node_modules/ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/ssri/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -3710,8 +4687,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "optional": true, - "peer": true, "engines": { "node": ">=0.10.0" } @@ -3727,12 +4702,27 @@ "node": ">=8" } }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/tar-fs": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", - "optional": true, - "peer": true, "dependencies": { "chownr": "^1.1.1", "mkdirp-classic": "^0.5.2", @@ -3744,8 +4734,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "optional": true, - "peer": true, "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", @@ -3761,8 +4749,6 @@ "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "optional": true, - "peer": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -3772,6 +4758,36 @@ "node": ">= 6" } }, + "node_modules/tar/node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/tar/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/text-decoder": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", @@ -3878,8 +4894,6 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "optional": true, - "peer": true, "dependencies": { "safe-buffer": "^5.0.1" }, @@ -4177,6 +5191,26 @@ "ieee754": "^1.1.13" } }, + "node_modules/unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node_modules/unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "license": "ISC", + "optional": true, + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -4243,6 +5277,16 @@ "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==" }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "license": "ISC", + "optional": true, + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, "node_modules/wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", @@ -4331,6 +5375,12 @@ "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, "node_modules/yargs": { "version": "15.4.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", diff --git a/package.json b/package.json index 3eace88..7c289f7 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "rss-parser": "^3.13.0", "socket.io": "^4.7.5", "speakeasy": "^2.0.0", + "sqlite3": "^5.1.7", "typeorm": "^0.3.20", "uuid": "^10.0.0" }, diff --git a/src/data-source.ts b/src/data-source.ts index 51d6659..e775503 100644 --- a/src/data-source.ts +++ b/src/data-source.ts @@ -50,7 +50,7 @@ import { CreateSchema1738166167472 } from "./migrations/1738166167472-CreateSche const dataSource = new DataSource({ type: DB_TYPE as any, - host: process.env.NODE_ENV || process.env.DBMODE ? "localhost" : DB_HOST, + host: DB_HOST, port: DB_PORT, username: DB_USERNAME, password: DB_PASSWORD, diff --git a/src/entity/club/calendar.ts b/src/entity/club/calendar.ts index a91fc5a..e6488f6 100644 --- a/src/entity/club/calendar.ts +++ b/src/entity/club/calendar.ts @@ -8,18 +8,20 @@ import { UpdateDateColumn, AfterUpdate, BeforeUpdate, + ColumnType, } from "typeorm"; import { calendarType } from "../settings/calendarType"; +import { getTypeByORM } from "../../migrations/ormHelper"; @Entity() export class calendar { @PrimaryGeneratedColumn("uuid") id: string; - @Column({ type: "datetime", nullable: false }) + @Column({ type: getTypeByORM("datetime").type as ColumnType }) starttime: Date; - @Column({ type: "datetime", nullable: false }) + @Column({ type: getTypeByORM("datetime").type as ColumnType }) endtime: Date; @Column({ type: "varchar", length: 255, nullable: false }) diff --git a/src/entity/club/member/member.ts b/src/entity/club/member/member.ts index 228eebb..d387469 100644 --- a/src/entity/club/member/member.ts +++ b/src/entity/club/member/member.ts @@ -1,10 +1,11 @@ -import { Column, Entity, JoinColumn, ManyToOne, OneToMany, OneToOne, PrimaryColumn } from "typeorm"; +import { Column, ColumnType, Entity, JoinColumn, ManyToOne, OneToMany, OneToOne, PrimaryColumn } from "typeorm"; import { membership } from "./membership"; import { memberAwards } from "./memberAwards"; import { memberQualifications } from "./memberQualifications"; import { memberExecutivePositions } from "./memberExecutivePositions"; import { communication } from "./communication"; import { salutation } from "../../settings/salutation"; +import { getTypeByORM } from "../../../migrations/ormHelper"; @Entity() export class member { @@ -20,7 +21,7 @@ export class member { @Column({ type: "varchar", length: 255 }) nameaffix: string; - @Column({ type: "date" }) + @Column({ type: getTypeByORM("date").type as ColumnType }) birthdate: Date; @Column({ type: "varchar", length: 255, unique: true, nullable: true }) diff --git a/src/entity/club/member/memberAwards.ts b/src/entity/club/member/memberAwards.ts index 1a453a2..3559024 100644 --- a/src/entity/club/member/memberAwards.ts +++ b/src/entity/club/member/memberAwards.ts @@ -1,6 +1,7 @@ -import { Column, Entity, ManyToOne, PrimaryColumn } from "typeorm"; +import { Column, ColumnType, Entity, ManyToOne, PrimaryColumn } from "typeorm"; import { member } from "./member"; import { award } from "../../settings/award"; +import { getTypeByORM } from "../../../migrations/ormHelper"; @Entity() export class memberAwards { @@ -13,7 +14,7 @@ export class memberAwards { @Column({ type: "varchar", length: 255, nullable: true }) note?: string; - @Column({ type: "date" }) + @Column({ type: getTypeByORM("date").type as ColumnType }) date: Date; @Column() diff --git a/src/entity/club/member/memberExecutivePositions.ts b/src/entity/club/member/memberExecutivePositions.ts index f09e9a8..c972d85 100644 --- a/src/entity/club/member/memberExecutivePositions.ts +++ b/src/entity/club/member/memberExecutivePositions.ts @@ -1,6 +1,7 @@ -import { Column, Entity, ManyToOne, PrimaryColumn } from "typeorm"; +import { Column, ColumnType, Entity, ManyToOne, PrimaryColumn } from "typeorm"; import { member } from "./member"; import { executivePosition } from "../../settings/executivePosition"; +import { getTypeByORM } from "../../../migrations/ormHelper"; @Entity() export class memberExecutivePositions { @@ -10,10 +11,10 @@ export class memberExecutivePositions { @Column({ type: "varchar", length: 255, nullable: true }) note?: string; - @Column({ type: "date" }) + @Column({ type: getTypeByORM("date").type as ColumnType }) start: Date; - @Column({ type: "date", nullable: true }) + @Column({ type: getTypeByORM("date").type as ColumnType, nullable: true }) end?: Date; @Column() diff --git a/src/entity/club/member/memberQualifications.ts b/src/entity/club/member/memberQualifications.ts index fe97ba5..c4e5a46 100644 --- a/src/entity/club/member/memberQualifications.ts +++ b/src/entity/club/member/memberQualifications.ts @@ -1,6 +1,7 @@ -import { Column, Entity, ManyToOne, PrimaryColumn } from "typeorm"; +import { Column, ColumnType, Entity, ManyToOne, PrimaryColumn } from "typeorm"; import { member } from "./member"; import { qualification } from "../../settings/qualification"; +import { getTypeByORM } from "../../../migrations/ormHelper"; @Entity() export class memberQualifications { @@ -10,10 +11,10 @@ export class memberQualifications { @Column({ type: "varchar", length: 255, nullable: true }) note?: string; - @Column({ type: "date" }) + @Column({ type: getTypeByORM("date").type as ColumnType }) start: Date; - @Column({ type: "date", nullable: true }) + @Column({ type: getTypeByORM("date").type as ColumnType, nullable: true }) end?: Date; @Column({ type: "varchar", length: 255, nullable: true }) diff --git a/src/entity/club/member/membership.ts b/src/entity/club/member/membership.ts index 63d2d52..1186f9c 100644 --- a/src/entity/club/member/membership.ts +++ b/src/entity/club/member/membership.ts @@ -1,16 +1,17 @@ -import { Column, Entity, JoinColumn, ManyToOne, PrimaryColumn } from "typeorm"; +import { Column, ColumnType, Entity, JoinColumn, ManyToOne, PrimaryColumn } from "typeorm"; import { member } from "./member"; import { membershipStatus } from "../../settings/membershipStatus"; +import { getTypeByORM } from "../../../migrations/ormHelper"; @Entity() export class membership { @PrimaryColumn({ generated: "increment", type: "int" }) id: number; - @Column({ type: "date" }) + @Column({ type: getTypeByORM("date").type as ColumnType }) start: Date; - @Column({ type: "date", nullable: true }) + @Column({ type: getTypeByORM("date").type as ColumnType, nullable: true }) end?: Date; @Column({ type: "varchar", length: 255, nullable: true }) diff --git a/src/entity/club/protocol/protocol.ts b/src/entity/club/protocol/protocol.ts index feaa4d9..18f672a 100644 --- a/src/entity/club/protocol/protocol.ts +++ b/src/entity/club/protocol/protocol.ts @@ -1,9 +1,10 @@ -import { Column, Entity, OneToMany, PrimaryColumn } from "typeorm"; +import { Column, ColumnType, Entity, OneToMany, PrimaryColumn } from "typeorm"; import { protocolAgenda } from "./protocolAgenda"; import { protocolDecision } from "./protocolDecision"; import { protocolPresence } from "./protocolPresence"; import { protocolPrintout } from "./protocolPrintout"; import { protocolVoting } from "./protocolVoting"; +import { getTypeByORM } from "../../../migrations/ormHelper"; @Entity() export class protocol { @@ -13,13 +14,13 @@ export class protocol { @Column({ type: "varchar", length: 255, unique: true }) title: string; - @Column({ type: "date" }) + @Column({ type: getTypeByORM("date").type as ColumnType }) date: Date; - @Column({ type: "time", nullable: true }) + @Column({ type: getTypeByORM("time").type as ColumnType, nullable: true }) starttime: Date; - @Column({ type: "time", nullable: true }) + @Column({ type: getTypeByORM("time").type as ColumnType, nullable: true }) endtime: Date; @Column({ type: "text", nullable: true }) diff --git a/src/entity/refresh.ts b/src/entity/refresh.ts index 646b3bd..c9a82e0 100644 --- a/src/entity/refresh.ts +++ b/src/entity/refresh.ts @@ -1,5 +1,6 @@ -import { Column, Entity, ManyToOne, PrimaryColumn } from "typeorm"; +import { Column, ColumnType, Entity, ManyToOne, PrimaryColumn } from "typeorm"; import { user } from "./user/user"; +import { getTypeByORM } from "../migrations/ormHelper"; @Entity() export class refresh { @@ -9,7 +10,7 @@ export class refresh { @PrimaryColumn() userId: string; - @Column({ type: "datetime" }) + @Column({ type: getTypeByORM("datetime").type as ColumnType }) expiry: Date; @ManyToOne(() => user, { diff --git a/src/entity/user/webapi.ts b/src/entity/user/webapi.ts index 2e8292c..c7aad4b 100644 --- a/src/entity/user/webapi.ts +++ b/src/entity/user/webapi.ts @@ -1,5 +1,6 @@ -import { Column, CreateDateColumn, Entity, OneToMany, PrimaryColumn } from "typeorm"; +import { Column, ColumnType, CreateDateColumn, Entity, OneToMany, PrimaryColumn } from "typeorm"; import { webapiPermission } from "./webapi_permission"; +import { getTypeByORM } from "../../migrations/ormHelper"; @Entity() export class webapi { @@ -15,10 +16,10 @@ export class webapi { @CreateDateColumn() createdAt: Date; - @Column({ type: "datetime", nullable: true }) + @Column({ type: getTypeByORM("datetime").type as ColumnType, nullable: true }) lastUsage?: Date; - @Column({ type: "date", nullable: true }) + @Column({ type: getTypeByORM("date").type as ColumnType, nullable: true }) expiry?: Date; @OneToMany(() => webapiPermission, (apiPermission) => apiPermission.webapi, { cascade: ["insert"] }) diff --git a/src/env.defaults.ts b/src/env.defaults.ts index 96968cb..61562b5 100644 --- a/src/env.defaults.ts +++ b/src/env.defaults.ts @@ -26,14 +26,18 @@ export const CLUB_WEBSITE = process.env.CLUB_WEBSITE ?? ""; export const BACKUP_INTERVAL = Number(process.env.BACKUP_INTERVAL ?? "0"); export const BACKUP_COPIES = Number(process.env.BACKUP_COPIES ?? "0"); -export const BACKUP_AUTO_RESTORE = process.env.BACKUP_AUTO_RESTORE ?? "false"; +export const BACKUP_AUTO_RESTORE = process.env.BACKUP_AUTO_RESTORE ?? "true"; export function configCheck() { - if (DB_TYPE != "mysql" && DB_TYPE != "sqlite") throw new Error("set valid value to DB_TYPE (mysql|sqlite)"); - if (DB_HOST == "" || typeof DB_HOST != "string") throw new Error("set valid value to DB_HOST"); + if (DB_TYPE != "mysql" && DB_TYPE != "sqlite" && DB_TYPE != "postgres") + throw new Error("set valid value to DB_TYPE (mysql|sqlite|postgres)"); + if ((DB_HOST == "" || typeof DB_HOST != "string") && DB_TYPE != "sqlite") + throw new Error("set valid value to DB_HOST"); if (DB_NAME == "" || typeof DB_NAME != "string") throw new Error("set valid value to DB_NAME"); - if (DB_USERNAME == "" || typeof DB_USERNAME != "string") throw new Error("set valid value to DB_USERNAME"); - if (DB_PASSWORD == "" || typeof DB_PASSWORD != "string") throw new Error("set valid value to DB_PASSWORD"); + if ((DB_USERNAME == "" || typeof DB_USERNAME != "string") && DB_TYPE != "sqlite") + throw new Error("set valid value to DB_USERNAME"); + if ((DB_PASSWORD == "" || typeof DB_PASSWORD != "string") && DB_TYPE != "sqlite") + throw new Error("set valid value to DB_PASSWORD"); if (typeof SERVER_PORT != "number") throw new Error("set valid numeric value to SERVER_PORT"); diff --git a/src/migrations/1738166124200-BackupAndResetDatabase.ts b/src/migrations/1738166124200-BackupAndResetDatabase.ts index 4a190c5..f0656ba 100644 --- a/src/migrations/1738166124200-BackupAndResetDatabase.ts +++ b/src/migrations/1738166124200-BackupAndResetDatabase.ts @@ -1,13 +1,15 @@ import { MigrationInterface, QueryRunner, Table } from "typeorm"; import BackupHelper from "../helpers/backupHelper"; -import { getTypeByORM } from "./ormHelper"; +import { getDefaultByORM, getTypeByORM, isIncrementPrimary } from "./ormHelper"; import InternalException from "../exceptions/internalException"; +import { DB_TYPE } from "../env.defaults"; export class BackupAndResetDatabase1738166124200 implements MigrationInterface { name = "BackupAndResetDatabase1738166124200"; public async up(queryRunner: QueryRunner): Promise { - let migrations = await queryRunner.query("SELECT `name` FROM `migrations`"); + let query = DB_TYPE == "postgres" ? "SELECT name FROM migrations" : "SELECT `name` FROM `migrations`"; + let migrations = await queryRunner.query(query); if ( (await queryRunner.hasTable("user")) && migrations.findIndex((m: any) => m.name == "MoveSendNewsletterFlag1737816852011") == -1 @@ -26,15 +28,9 @@ export class BackupAndResetDatabase1738166124200 implements MigrationInterface { new Table({ name: "migrations", columns: [ - { - name: "id", - type: getTypeByORM("int"), - isPrimary: true, - isGenerated: true, - generationStrategy: "increment", - }, - { name: "timestamp", type: getTypeByORM("bigint"), isNullable: false }, - { name: "name", type: getTypeByORM("varchar"), length: "255", isNullable: false }, + { name: "id", ...getTypeByORM("int"), ...isIncrementPrimary }, + { name: "timestamp", ...getTypeByORM("bigint") }, + { name: "name", ...getTypeByORM("varchar") }, ], }), true @@ -44,12 +40,12 @@ export class BackupAndResetDatabase1738166124200 implements MigrationInterface { new Table({ name: "typeorm_metadata", columns: [ - { name: "type", type: getTypeByORM("varchar"), length: "255", isNullable: false }, - { name: "database", type: getTypeByORM("varchar"), length: "255", isNullable: true, default: null }, - { name: "schema", type: getTypeByORM("varchar"), length: "255", isNullable: true, default: null }, - { name: "table", type: getTypeByORM("varchar"), length: "255", isNullable: true, default: null }, - { name: "name", type: getTypeByORM("varchar"), length: "255", isNullable: true, default: null }, - { name: "value", type: getTypeByORM("text"), length: "255", isNullable: true, default: null }, + { name: "type", ...getTypeByORM("varchar") }, + { name: "database", ...getTypeByORM("varchar", true), default: getDefaultByORM("null") }, + { name: "schema", ...getTypeByORM("varchar", true), default: getDefaultByORM("null") }, + { name: "table", ...getTypeByORM("varchar", true), default: getDefaultByORM("null") }, + { name: "name", ...getTypeByORM("varchar", true), default: getDefaultByORM("null") }, + { name: "value", ...getTypeByORM("text", true), default: getDefaultByORM("null") }, ], }), true diff --git a/src/migrations/1738166167472-CreateSchema.ts b/src/migrations/1738166167472-CreateSchema.ts index 0eafa18..f5995b7 100644 --- a/src/migrations/1738166167472-CreateSchema.ts +++ b/src/migrations/1738166167472-CreateSchema.ts @@ -20,13 +20,17 @@ import { member_communication_table, member_executive_positions_table, member_executive_positions_view, + member_executive_positions_view_postgres, member_qualifications_table, member_qualifications_view, + member_qualifications_view_postgres, member_table, member_view, + member_view_postgres, membership_status_table, membership_table, membership_view, + membership_view_postgres, qualification_table, salutation_table, } from "./baseSchemaTables/member"; @@ -46,6 +50,7 @@ import { newsletter_recipients_table, newsletter_table, } from "./baseSchemaTables/newsletter"; +import { DB_TYPE } from "../env.defaults"; export class CreateSchema1738166167472 implements MigrationInterface { name = "CreateSchema1738166167472"; @@ -75,10 +80,14 @@ export class CreateSchema1738166167472 implements MigrationInterface { 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); + if (DB_TYPE == "postgres") await queryRunner.createView(member_view_postgres, true); + else await queryRunner.createView(member_view, true); + if (DB_TYPE == "postgres") await queryRunner.createView(membership_view_postgres, true); + else await queryRunner.createView(membership_view, true); + if (DB_TYPE == "postgres") await queryRunner.createView(member_qualifications_view_postgres, true); + else await queryRunner.createView(member_qualifications_view, true); + if (DB_TYPE == "postgres") await queryRunner.createView(member_executive_positions_view_postgres, true); + else await queryRunner.createView(member_executive_positions_view, true); await queryRunner.createTable(query_table, true, true, true); await queryRunner.createTable(template_table, true, true, true); diff --git a/src/migrations/baseSchemaTables/admin.ts b/src/migrations/baseSchemaTables/admin.ts index 9ad6437..c3eb94b 100644 --- a/src/migrations/baseSchemaTables/admin.ts +++ b/src/migrations/baseSchemaTables/admin.ts @@ -1,36 +1,30 @@ import { Table, TableForeignKey } from "typeorm"; -import { getTypeByORM } from "../ormHelper"; +import { getDefaultByORM, getTypeByORM, isIncrementPrimary, isUUIDPrimary } 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 }, + { name: "mail", ...getTypeByORM("varchar"), isPrimary: true }, + { name: "token", ...getTypeByORM("varchar") }, + { name: "username", ...getTypeByORM("varchar") }, + { name: "firstname", ...getTypeByORM("varchar") }, + { name: "lastname", ...getTypeByORM("varchar") }, + { name: "secret", ...getTypeByORM("varchar") }, ], }); 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 }, + { name: "id", ...getTypeByORM("int"), ...isIncrementPrimary }, + { name: "role", ...getTypeByORM("varchar"), 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 }, + { name: "roleId", ...getTypeByORM("int"), isPrimary: true }, + { name: "permission", ...getTypeByORM("varchar"), isPrimary: true }, ], foreignKeys: [ new TableForeignKey({ @@ -46,29 +40,22 @@ export const role_permission_table = new Table({ 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: "static", type: getTypeByORM("boolean"), isNullable: false, default: false }, - { name: "isOwner", type: getTypeByORM("boolean"), isNullable: false, default: false }, + { name: "id", ...getTypeByORM("uuid"), ...isUUIDPrimary }, + { name: "mail", ...getTypeByORM("varchar"), isUnique: true }, + { name: "username", ...getTypeByORM("varchar"), isUnique: true }, + { name: "firstname", ...getTypeByORM("varchar") }, + { name: "lastname", ...getTypeByORM("varchar") }, + { name: "secret", ...getTypeByORM("varchar") }, + { name: "static", ...getTypeByORM("boolean"), default: getDefaultByORM("boolean", false) }, + { name: "isOwner", ...getTypeByORM("boolean"), default: getDefaultByORM("boolean", 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 }, + { name: "userId", ...getTypeByORM("uuid"), isPrimary: true }, + { name: "roleId", ...getTypeByORM("int"), isPrimary: true }, ], foreignKeys: [ new TableForeignKey({ @@ -91,8 +78,8 @@ export const user_roles_table = new Table({ 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 }, + { name: "userId", ...getTypeByORM("uuid"), isPrimary: true }, + { name: "permission", ...getTypeByORM("varchar"), isPrimary: true }, ], foreignKeys: [ new TableForeignKey({ @@ -108,9 +95,9 @@ export const user_permission_table = new Table({ 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 }, + { name: "token", ...getTypeByORM("varchar"), isPrimary: true }, + { name: "expiry", ...getTypeByORM("datetime", false, 6) }, + { name: "userId", ...getTypeByORM("uuid"), isPrimary: true }, ], foreignKeys: [ new TableForeignKey({ @@ -126,26 +113,20 @@ export const refresh_table = new Table({ 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 }, + { name: "id", ...getTypeByORM("int"), ...isIncrementPrimary }, + { name: "token", ...getTypeByORM("varchar"), isUnique: true }, + { name: "title", ...getTypeByORM("varchar"), isUnique: true }, + { name: "createdAt", ...getTypeByORM("datetime", false, 6), default: getDefaultByORM("currentTimestamp", 6) }, + { name: "lastUsage", ...getTypeByORM("datetime", true, 6), default: getDefaultByORM("null") }, + { name: "expiry", ...getTypeByORM("date", true), default: getDefaultByORM("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 }, + { name: "webapiId", ...getTypeByORM("int"), isPrimary: true }, + { name: "permission", ...getTypeByORM("varchar"), isPrimary: true }, ], foreignKeys: [ new TableForeignKey({ @@ -161,9 +142,9 @@ export const webapi_permission_table = new Table({ 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 }, + { name: "mail", ...getTypeByORM("varchar"), isPrimary: true }, + { name: "token", ...getTypeByORM("varchar") }, + { name: "username", ...getTypeByORM("varchar") }, + { name: "secret", ...getTypeByORM("varchar") }, ], }); diff --git a/src/migrations/baseSchemaTables/calendar.ts b/src/migrations/baseSchemaTables/calendar.ts index 793b2cc..155dc46 100644 --- a/src/migrations/baseSchemaTables/calendar.ts +++ b/src/migrations/baseSchemaTables/calendar.ts @@ -1,61 +1,39 @@ import { Table, TableForeignKey } from "typeorm"; -import { getTypeByORM } from "../ormHelper"; +import { getDefaultByORM, getTypeByORM, isIncrementPrimary, isUUIDPrimary } 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, default: false }, - { name: "color", type: getTypeByORM("varchar"), length: "255", isNullable: false }, - { name: "passphrase", type: getTypeByORM("varchar"), length: "255", isNullable: true }, - { name: "externalPrefix", type: getTypeByORM("varchar"), length: "255", isNullable: false, default: "''" }, - { name: "sendToWebpage", type: getTypeByORM("boolean"), isNullable: false, default: false }, + { name: "id", ...getTypeByORM("int"), ...isIncrementPrimary }, + { name: "type", ...getTypeByORM("varchar"), isUnique: true }, + { name: "nscdr", ...getTypeByORM("boolean"), default: getDefaultByORM("boolean", false) }, + { name: "color", ...getTypeByORM("varchar") }, + { name: "passphrase", ...getTypeByORM("varchar", true) }, + { name: "externalPrefix", ...getTypeByORM("varchar"), default: getDefaultByORM("string") }, + { name: "sendToWebpage", ...getTypeByORM("boolean"), default: getDefaultByORM("boolean", false) }, ], }); 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: "id", ...getTypeByORM("uuid"), ...isUUIDPrimary }, + { name: "starttime", ...getTypeByORM("datetime", false, 6) }, + { name: "endtime", ...getTypeByORM("datetime", false, 6) }, + { name: "title", ...getTypeByORM("varchar") }, + { name: "content", ...getTypeByORM("text", true) }, + { name: "allDay", ...getTypeByORM("boolean"), default: getDefaultByORM("boolean", false) }, + { name: "location", ...getTypeByORM("text", true) }, + { name: "sequence", ...getTypeByORM("int"), default: getDefaultByORM("number", 1) }, + { name: "createdAt", ...getTypeByORM("datetime", false, 6), default: getDefaultByORM("currentTimestamp", 6) }, { name: "updatedAt", - type: getTypeByORM("datetime"), - precision: 6, - isNullable: false, - default: "CURRENT_TIMESTAMP(6)", - onUpdate: "CURRENT_TIMESTAMP(6)", + ...getTypeByORM("datetime", false, 6), + default: getDefaultByORM("currentTimestamp", 6), + onUpdate: getDefaultByORM("currentTimestamp", 6), }, - { - name: "webpageId", - type: getTypeByORM("varchar"), - length: "255", - isNullable: true, - default: null, - isUnique: true, - }, - { name: "typeId", type: getTypeByORM("int"), isNullable: false }, + { name: "webpageId", ...getTypeByORM("varchar", true), default: getDefaultByORM("null"), isUnique: true }, + { name: "typeId", ...getTypeByORM("int") }, ], foreignKeys: [ new TableForeignKey({ diff --git a/src/migrations/baseSchemaTables/member.ts b/src/migrations/baseSchemaTables/member.ts index ead90b5..c2657cb 100644 --- a/src/migrations/baseSchemaTables/member.ts +++ b/src/migrations/baseSchemaTables/member.ts @@ -1,53 +1,53 @@ import { Table, TableForeignKey, View } from "typeorm"; -import { getTypeByORM } from "../ormHelper"; +import { getDefaultByORM, getTypeByORM, isIncrementPrimary, isUUIDPrimary } 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 }, + { name: "id", ...getTypeByORM("int"), ...isIncrementPrimary }, + { name: "salutation", ...getTypeByORM("varchar"), isUnique: true }, ], }); 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 }, + { name: "id", ...getTypeByORM("int"), ...isIncrementPrimary }, + { name: "award", ...getTypeByORM("varchar"), isUnique: true }, ], }); 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: "''" }, + { name: "id", ...getTypeByORM("int"), ...isIncrementPrimary }, + { name: "type", ...getTypeByORM("varchar"), isUnique: true }, + { name: "useColumns", ...getTypeByORM("varchar"), default: getDefaultByORM("string") }, ], }); 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 }, + { name: "id", ...getTypeByORM("int"), ...isIncrementPrimary }, + { name: "position", ...getTypeByORM("varchar"), isUnique: true }, ], }); 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 }, + { name: "id", ...getTypeByORM("int"), ...isIncrementPrimary }, + { name: "status", ...getTypeByORM("varchar"), isUnique: true }, ], }); 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 }, + { name: "id", ...getTypeByORM("int"), ...isIncrementPrimary }, + { name: "qualification", ...getTypeByORM("varchar"), isUnique: true }, + { name: "description", ...getTypeByORM("varchar"), isNullable: true }, ], }); @@ -55,27 +55,13 @@ export const qualification_table = new Table({ 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 }, + { name: "id", ...getTypeByORM("uuid"), ...isUUIDPrimary }, + { name: "salutationId", ...getTypeByORM("int") }, + { name: "internalId", ...getTypeByORM("varchar", true), default: getDefaultByORM("null"), isUnique: true }, + { name: "firstname", ...getTypeByORM("varchar") }, + { name: "lastname", ...getTypeByORM("varchar") }, + { name: "nameaffix", ...getTypeByORM("varchar") }, + { name: "birthdate", ...getTypeByORM("date") }, ], foreignKeys: [ new TableForeignKey({ @@ -91,12 +77,12 @@ export const member_table = new Table({ 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 }, + { name: "id", ...getTypeByORM("int"), ...isIncrementPrimary }, + { name: "start", ...getTypeByORM("date") }, + { name: "end", ...getTypeByORM("date", true) }, + { name: "terminationReason", ...getTypeByORM("varchar"), isNullable: true }, + { name: "memberId", ...getTypeByORM("uuid") }, + { name: "statusId", ...getTypeByORM("int") }, ], foreignKeys: [ new TableForeignKey({ @@ -119,13 +105,13 @@ export const membership_table = new Table({ 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 }, + { name: "id", ...getTypeByORM("int"), ...isIncrementPrimary }, + { name: "note", ...getTypeByORM("varchar"), isNullable: true }, + { name: "start", ...getTypeByORM("date") }, + { name: "end", ...getTypeByORM("date", true) }, + { name: "terminationReason", ...getTypeByORM("varchar"), isNullable: true }, + { name: "memberId", ...getTypeByORM("uuid") }, + { name: "qualificationId", ...getTypeByORM("int") }, ], foreignKeys: [ new TableForeignKey({ @@ -148,12 +134,12 @@ export const member_qualifications_table = new Table({ 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 }, + { name: "id", ...getTypeByORM("int"), ...isIncrementPrimary }, + { name: "note", ...getTypeByORM("varchar"), isNullable: true }, + { name: "start", ...getTypeByORM("date") }, + { name: "end", ...getTypeByORM("date", true) }, + { name: "memberId", ...getTypeByORM("uuid") }, + { name: "executivePositionId", ...getTypeByORM("int") }, ], foreignKeys: [ new TableForeignKey({ @@ -176,12 +162,12 @@ export const member_executive_positions_table = new Table({ 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 }, + { name: "id", ...getTypeByORM("int"), ...isIncrementPrimary }, + { name: "given", ...getTypeByORM("boolean"), default: getDefaultByORM("boolean", false) }, + { name: "note", ...getTypeByORM("varchar"), isNullable: true }, + { name: "date", ...getTypeByORM("date") }, + { name: "memberId", ...getTypeByORM("uuid") }, + { name: "awardId", ...getTypeByORM("int") }, ], foreignKeys: [ new TableForeignKey({ @@ -204,19 +190,19 @@ export const member_awards_table = new Table({ 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 }, + { name: "id", ...getTypeByORM("int"), ...isIncrementPrimary }, + { name: "preferred", ...getTypeByORM("boolean"), default: getDefaultByORM("boolean", false) }, + { name: "isSendNewsletter", ...getTypeByORM("boolean"), default: getDefaultByORM("boolean", false) }, + { name: "isSMSAlarming", ...getTypeByORM("boolean"), default: getDefaultByORM("boolean", false) }, + { name: "mobile", ...getTypeByORM("varchar"), isNullable: true }, + { name: "email", ...getTypeByORM("varchar"), isNullable: true }, + { name: "postalCode", ...getTypeByORM("varchar"), default: getDefaultByORM("null"), isNullable: true }, + { name: "city", ...getTypeByORM("varchar"), isNullable: true }, + { name: "street", ...getTypeByORM("varchar"), isNullable: true }, + { name: "streetNumber", ...getTypeByORM("int", true) }, + { name: "streetNumberAddition", ...getTypeByORM("varchar"), isNullable: true }, + { name: "memberId", ...getTypeByORM("uuid") }, + { name: "typeId", ...getTypeByORM("int") }, ], foreignKeys: [ new TableForeignKey({ @@ -247,13 +233,32 @@ export const member_view = new View({ \`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\`, + 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_view_postgres = 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", + DATE_PART('year', AGE(CURRENT_DATE, member.birthdate)) AS "todayAge", + EXTRACT(YEAR FROM CURRENT_DATE) - EXTRACT(YEAR FROM member.birthdate) AS "ageThisYear", + AGE(CURRENT_DATE, member.birthdate) 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: ` @@ -266,13 +271,35 @@ export const member_executive_positions_view = new View({ \`member\`.\`nameaffix\` AS \`memberNameaffix\`, \`member\`.\`birthdate\` AS \`memberBirthdate\`, \`salutation\`.\`salutation\` AS \`memberSalutation\`, - SUM(TIMESTAMPDIFF(DAY, \`memberExecutivePositions\`.\`start\`, - COALESCE(\`memberExecutivePositions\`.\`end\`, CURRENT_DATE))) AS \`durationInDays\` + SUM(TIMESTAMPDIFF(DAY, \`memberExecutivePositions\`.\`start\`, COALESCE(\`memberExecutivePositions\`.\`end\`, CURRENT_DATE))) AS \`durationInDays\`, + CONCAT('_', FROM_DAYS(SUM(TIMESTAMPDIFF(DAY, \`memberExecutivePositions\`.\`start\`, COALESCE(\`memberExecutivePositions\`.\`end\`, CURRENT_DATE))))) AS \`durationInYears\` 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\` + GROUP BY \`executivePosition\`.\`id\`, \`member\`.\`id\`, \`salutation\`.\`id\` + `, +}); + +export const member_executive_positions_view_postgres = 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(COALESCE("memberExecutivePositions"."end", CURRENT_DATE) - "memberExecutivePositions"."start") AS "durationInDays", + SUM(AGE(COALESCE("memberExecutivePositions"."end", CURRENT_DATE), "memberExecutivePositions"."start")) AS "durationInYears" + 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", "salutation"."id" `, }); @@ -288,13 +315,35 @@ export const member_qualifications_view = new View({ \`member\`.\`nameaffix\` AS \`memberNameaffix\`, \`member\`.\`birthdate\` AS \`memberBirthdate\`, \`salutation\`.\`salutation\` AS \`memberSalutation\`, - SUM(TIMESTAMPDIFF(DAY, \`memberQualifications\`.\`start\`, - COALESCE(\`memberQualifications\`.\`end\`, CURRENT_DATE))) AS \`durationInDays\` + SUM(TIMESTAMPDIFF(DAY, \`memberQualifications\`.\`start\`, COALESCE(\`memberQualifications\`.\`end\`, CURRENT_DATE))) AS \`durationInDays\`, + CONCAT('_', FROM_DAYS(SUM(TIMESTAMPDIFF(DAY, \`memberQualifications\`.\`start\`, COALESCE(\`memberQualifications\`.\`end\`, CURRENT_DATE))))) AS \`durationInYears\` 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\` + GROUP BY \`qualification\`.\`id\`, \`member\`.\`id\`, \`salutation\`.\`id\` + `, +}); + +export const member_qualifications_view_postgres = 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(COALESCE("memberQualifications"."end", CURRENT_DATE) - "memberQualifications"."start") AS "durationInDays", + SUM(AGE(COALESCE("memberQualifications"."end", CURRENT_DATE), "memberQualifications"."start")) AS "durationInYears" + 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", "salutation"."id" `, }); @@ -310,13 +359,34 @@ export const membership_view = new View({ \`member\`.\`nameaffix\` AS \`memberNameaffix\`, \`member\`.\`birthdate\` AS \`memberBirthdate\`, \`salutation\`.\`salutation\` AS \`memberSalutation\`, - SUM(TIMESTAMPDIFF(DAY, \`membership\`.\`start\`, - COALESCE(\`membership\`.\`end\`, CURRENT_DATE))) AS \`durationInDays\`, + 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\` + GROUP BY \`status\`.\`id\`, \`member\`.\`id\`, \`salutation\`.\`id\` + `, +}); + +export const membership_view_postgres = 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(COALESCE("membership"."end", CURRENT_DATE) - "membership"."start") AS "durationInDays", + SUM(AGE(COALESCE("membership"."end", CURRENT_DATE), "membership"."start")) 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", "salutation"."id" `, }); diff --git a/src/migrations/baseSchemaTables/newsletter.ts b/src/migrations/baseSchemaTables/newsletter.ts index 5e39fcc..537428f 100644 --- a/src/migrations/baseSchemaTables/newsletter.ts +++ b/src/migrations/baseSchemaTables/newsletter.ts @@ -1,17 +1,17 @@ import { Table, TableForeignKey } from "typeorm"; -import { getTypeByORM } from "../ormHelper"; +import { getDefaultByORM, getTypeByORM, isIncrementPrimary } 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 }, + { name: "id", ...getTypeByORM("int"), ...isIncrementPrimary }, + { name: "title", ...getTypeByORM("varchar") }, + { name: "description", ...getTypeByORM("varchar"), default: getDefaultByORM("string") }, + { name: "newsletterTitle", ...getTypeByORM("varchar"), default: getDefaultByORM("string") }, + { name: "newsletterText", ...getTypeByORM("text"), default: getDefaultByORM("string") }, + { name: "newsletterSignatur", ...getTypeByORM("varchar"), default: getDefaultByORM("string") }, + { name: "isSent", ...getTypeByORM("boolean"), default: getDefaultByORM("boolean", false) }, + { name: "recipientsByQueryId", ...getTypeByORM("int", true) }, ], foreignKeys: [ new TableForeignKey({ @@ -27,10 +27,10 @@ export const newsletter_table = new Table({ 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 }, + { name: "newsletterId", ...getTypeByORM("int"), isPrimary: true }, + { name: "calendarId", ...getTypeByORM("uuid"), isPrimary: true }, + { name: "diffTitle", ...getTypeByORM("varchar"), isNullable: true }, + { name: "diffDescription", ...getTypeByORM("text", true) }, ], foreignKeys: [ new TableForeignKey({ @@ -53,8 +53,8 @@ export const newsletter_dates_table = new Table({ 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 }, + { name: "newsletterId", ...getTypeByORM("int"), isPrimary: true }, + { name: "memberId", ...getTypeByORM("uuid"), isPrimary: true }, ], foreignKeys: [ new TableForeignKey({ @@ -77,8 +77,8 @@ export const newsletter_recipients_table = new Table({ 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 }, + { name: "comTypeId", ...getTypeByORM("int"), isPrimary: true }, + { name: "config", ...getTypeByORM("varchar") }, ], foreignKeys: [ new TableForeignKey({ diff --git a/src/migrations/baseSchemaTables/protocol.ts b/src/migrations/baseSchemaTables/protocol.ts index 2dee098..fd1cc89 100644 --- a/src/migrations/baseSchemaTables/protocol.ts +++ b/src/migrations/baseSchemaTables/protocol.ts @@ -1,25 +1,25 @@ import { Table, TableForeignKey } from "typeorm"; -import { getTypeByORM } from "../ormHelper"; +import { getDefaultByORM, getTypeByORM, isIncrementPrimary } 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 }, + { name: "id", ...getTypeByORM("int"), ...isIncrementPrimary }, + { name: "title", ...getTypeByORM("varchar") }, + { name: "date", ...getTypeByORM("date") }, + { name: "starttime", ...getTypeByORM("time", true) }, + { name: "endtime", ...getTypeByORM("time", true) }, + { name: "summary", ...getTypeByORM("text", 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 }, + { name: "id", ...getTypeByORM("int"), ...isIncrementPrimary }, + { name: "topic", ...getTypeByORM("varchar") }, + { name: "context", ...getTypeByORM("text"), default: getDefaultByORM("string") }, + { name: "protocolId", ...getTypeByORM("int") }, ], foreignKeys: [ new TableForeignKey({ @@ -35,10 +35,10 @@ export const protocol_agenda_table = new Table({ 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 }, + { name: "id", ...getTypeByORM("int"), ...isIncrementPrimary }, + { name: "topic", ...getTypeByORM("varchar") }, + { name: "context", ...getTypeByORM("text"), default: getDefaultByORM("string") }, + { name: "protocolId", ...getTypeByORM("int") }, ], foreignKeys: [ new TableForeignKey({ @@ -54,10 +54,10 @@ export const protocol_decision_table = new Table({ 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 }, + { name: "memberId", ...getTypeByORM("uuid"), isPrimary: true }, + { name: "protocolId", ...getTypeByORM("int"), isPrimary: true }, + { name: "absent", ...getTypeByORM("boolean"), default: getDefaultByORM("boolean", false) }, + { name: "excused", ...getTypeByORM("boolean"), default: getDefaultByORM("boolean", true) }, ], foreignKeys: [ new TableForeignKey({ @@ -80,13 +80,13 @@ export const protocol_presence_table = new Table({ 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 }, + { name: "id", ...getTypeByORM("int"), ...isIncrementPrimary }, + { name: "topic", ...getTypeByORM("varchar") }, + { name: "context", ...getTypeByORM("text"), default: getDefaultByORM("string") }, + { name: "favour", ...getTypeByORM("int"), default: getDefaultByORM("number", 0) }, + { name: "abstain", ...getTypeByORM("int"), default: getDefaultByORM("number", 0) }, + { name: "against", ...getTypeByORM("int"), default: getDefaultByORM("number", 0) }, + { name: "protocolId", ...getTypeByORM("int") }, ], foreignKeys: [ new TableForeignKey({ @@ -102,12 +102,12 @@ export const protocol_voting_table = new Table({ 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 }, + { name: "id", ...getTypeByORM("int"), ...isIncrementPrimary }, + { name: "title", ...getTypeByORM("varchar") }, + { name: "iteration", ...getTypeByORM("int"), default: getDefaultByORM("number", 1) }, + { name: "filename", ...getTypeByORM("varchar") }, + { name: "createdAt", ...getTypeByORM("datetime", false, 6), default: getDefaultByORM("currentTimestamp", 6) }, + { name: "protocolId", ...getTypeByORM("int") }, ], foreignKeys: [ new TableForeignKey({ diff --git a/src/migrations/baseSchemaTables/query_template.ts b/src/migrations/baseSchemaTables/query_template.ts index 399b34b..da4eb44 100644 --- a/src/migrations/baseSchemaTables/query_template.ts +++ b/src/migrations/baseSchemaTables/query_template.ts @@ -1,35 +1,35 @@ import { Table, TableForeignKey } from "typeorm"; -import { getTypeByORM } from "../ormHelper"; +import { getDefaultByORM, getTypeByORM, isIncrementPrimary } 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: "''" }, + { name: "id", ...getTypeByORM("int"), ...isIncrementPrimary }, + { name: "title", ...getTypeByORM("varchar"), isUnique: true }, + { name: "query", ...getTypeByORM("text"), default: getDefaultByORM("string") }, ], }); 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: "''" }, + { name: "id", ...getTypeByORM("int"), ...isIncrementPrimary }, + { name: "template", ...getTypeByORM("varchar"), isUnique: true }, + { name: "description", ...getTypeByORM("varchar", true) }, + { name: "design", ...getTypeByORM("text"), default: getDefaultByORM("string", "{}") }, + { name: "html", ...getTypeByORM("text"), default: getDefaultByORM("string") }, ], }); 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 }, + { name: "scope", ...getTypeByORM("varchar"), isPrimary: true }, + { name: "headerId", ...getTypeByORM("int", true), default: getDefaultByORM("null") }, + { name: "bodyId", ...getTypeByORM("int", true), default: getDefaultByORM("null") }, + { name: "footerId", ...getTypeByORM("int", true), default: getDefaultByORM("null") }, + { name: "headerHeight", ...getTypeByORM("int", true), default: getDefaultByORM("null") }, + { name: "footerHeight", ...getTypeByORM("int", true), default: getDefaultByORM("null") }, ], foreignKeys: [ new TableForeignKey({ diff --git a/src/migrations/ormHelper.ts b/src/migrations/ormHelper.ts index d4c7f43..20a57ad 100644 --- a/src/migrations/ormHelper.ts +++ b/src/migrations/ormHelper.ts @@ -1,6 +1,18 @@ -export type ORMType = "int" | "bigint" | "boolean" | "date" | "datetime" | "time" | "text" | "varchar"; +export type ORMType = "int" | "bigint" | "boolean" | "date" | "datetime" | "time" | "text" | "varchar" | "uuid"; +export type ORMDefault = "currentTimestamp" | "string" | "boolean" | "number" | "null"; +export type ColumnConfig = { + type: string; + length?: string; + precision?: number; + isNullable: boolean; +}; +export type Primary = { + isPrimary: boolean; + isGenerated: boolean; + generationStrategy: "increment" | "uuid" | "rowid" | "identity"; +}; -export function getTypeByORM(type: ORMType): string { +export function getTypeByORM(type: ORMType, nullable: boolean = false, length: number = 255): ColumnConfig { const dbType = process.env.DB_TYPE; const typeMap: Record> = { @@ -13,6 +25,7 @@ export function getTypeByORM(type: ORMType): string { time: "time", text: "text", varchar: "varchar", + uuid: "varchar", }, postgres: { int: "integer", @@ -22,19 +35,71 @@ export function getTypeByORM(type: ORMType): string { datetime: "timestamp", time: "time", text: "text", - varchar: "varchar", + varchar: "character varying", + uuid: "uuid", }, sqlite: { int: "integer", bigint: "integer", boolean: "integer", - date: "text", - datetime: "text", + date: "date", + datetime: "datetime", time: "text", text: "text", - varchar: "text", + varchar: "varchar", + uuid: "varchar", }, }; - return typeMap[dbType]?.[type] || type; + let obj: ColumnConfig = { + type: typeMap[dbType]?.[type] || type, + isNullable: nullable, + }; + if (type == "datetime") obj.precision = length; + else if (dbType != "sqlite" && (obj.type == "varchar" || type == "varchar")) obj.length = `${length}`; + else if (dbType != "postgres" && type == "uuid") obj.length = "36"; + + return obj; } + +export function getDefaultByORM(type: ORMDefault, data?: string | number | boolean): T { + const dbType = process.env.DB_TYPE; + + const typeMap: Record> = { + mysql: { + currentTimestamp: `CURRENT_TIMESTAMP(${data ?? 6})`, + string: `'${data ?? ""}'`, + boolean: Boolean(data).toString(), + number: Number(data).toString(), + null: null, + }, + postgres: { + currentTimestamp: `now()`, + string: `'${data ?? ""}'`, + boolean: Boolean(data) == true ? "true" : "false", + number: Number(data).toString(), + null: null, + }, + sqlite: { + currentTimestamp: `datetime('now')`, + string: `'${data ?? ""}'`, + boolean: Boolean(data) == true ? "1" : "0", + number: Number(data).toString(), + null: null, + }, + }; + + return (typeMap[dbType]?.[type] || type) as T; +} + +export const isIncrementPrimary: Primary = { + isPrimary: true, + isGenerated: true, + generationStrategy: "increment", +}; + +export const isUUIDPrimary: Primary = { + isPrimary: true, + isGenerated: true, + generationStrategy: "uuid", +}; diff --git a/src/views/memberExecutivePositionView.ts b/src/views/memberExecutivePositionView.ts index 6bac173..610fe34 100644 --- a/src/views/memberExecutivePositionView.ts +++ b/src/views/memberExecutivePositionView.ts @@ -1,5 +1,6 @@ import { DataSource, ViewColumn, ViewEntity } from "typeorm"; import { memberExecutivePositions } from "../entity/club/member/memberExecutivePositions"; +import { DB_TYPE } from "../env.defaults"; @ViewEntity({ expression: (datasource: DataSource) => @@ -15,19 +16,31 @@ import { memberExecutivePositions } from "../entity/club/member/memberExecutiveP .addSelect("member.birthdate", "memberBirthdate") .addSelect("salutation.salutation", "memberSalutation") .addSelect( - "SUM(TIMESTAMPDIFF(DAY, memberExecutivePositions.start, COALESCE(memberExecutivePositions.end, CURRENT_DATE)))", + DB_TYPE == "postgres" + ? `SUM(COALESCE("memberExecutivePositions"."end", CURRENT_DATE) - "memberExecutivePositions"."start")` + : "SUM(TIMESTAMPDIFF(DAY, memberExecutivePositions.start, COALESCE(memberExecutivePositions.end, CURRENT_DATE)))", "durationInDays" ) + .addSelect( + DB_TYPE == "postgres" + ? `SUM(AGE(COALESCE("memberExecutivePositions"."end", CURRENT_DATE), "memberExecutivePositions"."start"))` + : "CONCAT('_', FROM_DAYS(SUM(TIMESTAMPDIFF(DAY, memberExecutivePositions.start, COALESCE(memberExecutivePositions.end, CURRENT_DATE)))))", + "durationInYears" + ) .leftJoin("memberExecutivePositions.executivePosition", "executivePosition") .leftJoin("memberExecutivePositions.member", "member") .leftJoin("member.salutation", "salutation") .groupBy("executivePosition.id") - .addGroupBy("member.id"), + .addGroupBy("member.id") + .addGroupBy("salutation.id"), }) export class memberExecutivePositionsView { @ViewColumn() durationInDays: number; + @ViewColumn() + durationInYears: string; + @ViewColumn() position: string; diff --git a/src/views/memberQualificationsView.ts b/src/views/memberQualificationsView.ts index 9db700c..1e6dd55 100644 --- a/src/views/memberQualificationsView.ts +++ b/src/views/memberQualificationsView.ts @@ -1,5 +1,6 @@ import { DataSource, ViewColumn, ViewEntity } from "typeorm"; import { memberQualifications } from "../entity/club/member/memberQualifications"; +import { DB_TYPE } from "../env.defaults"; @ViewEntity({ expression: (datasource: DataSource) => @@ -15,19 +16,31 @@ import { memberQualifications } from "../entity/club/member/memberQualifications .addSelect("member.birthdate", "memberBirthdate") .addSelect("salutation.salutation", "memberSalutation") .addSelect( - "SUM(TIMESTAMPDIFF(DAY, memberQualifications.start, COALESCE(memberQualifications.end, CURRENT_DATE)))", + DB_TYPE == "postgres" + ? `SUM(COALESCE("memberQualifications"."end", CURRENT_DATE) - "memberQualifications"."start")` + : "SUM(TIMESTAMPDIFF(DAY, memberQualifications.start, COALESCE(memberQualifications.end, CURRENT_DATE)))", "durationInDays" ) + .addSelect( + DB_TYPE == "postgres" + ? `SUM(AGE(COALESCE("memberQualifications"."end", CURRENT_DATE), "memberQualifications"."start"))` + : "CONCAT('_', FROM_DAYS(SUM(TIMESTAMPDIFF(DAY, memberQualifications.start, COALESCE(memberQualifications.end, CURRENT_DATE)))))", + "durationInYears" + ) .leftJoin("memberQualifications.qualification", "qualification") .leftJoin("memberQualifications.member", "member") .leftJoin("member.salutation", "salutation") .groupBy("qualification.id") - .addGroupBy("member.id"), + .addGroupBy("member.id") + .addGroupBy("salutation.id"), }) export class memberQualificationsView { @ViewColumn() durationInDays: number; + @ViewColumn() + durationInYears: string; + @ViewColumn() qualification: string; diff --git a/src/views/memberView.ts b/src/views/memberView.ts index 74be622..11de1e8 100644 --- a/src/views/memberView.ts +++ b/src/views/memberView.ts @@ -1,5 +1,6 @@ import { DataSource, ViewColumn, ViewEntity } from "typeorm"; import { member } from "../entity/club/member/member"; +import { DB_TYPE } from "../env.defaults"; @ViewEntity({ expression: (datasource: DataSource) => @@ -12,9 +13,24 @@ import { member } from "../entity/club/member/member"; .addSelect("member.nameaffix", "nameaffix") .addSelect("member.birthdate", "birthdate") .addSelect("salutation.salutation", "salutation") - .addSelect("TIMESTAMPDIFF(YEAR, member.birthdate, CURDATE())", "todayAge") - .addSelect("YEAR(CURDATE()) - YEAR(member.birthdate)", "ageThisYear") - .addSelect("CONCAT('_', FROM_DAYS(TIMESTAMPDIFF(DAY, member.birthdate, CURDATE())))", "exactAge") + .addSelect( + DB_TYPE == "postgres" + ? `DATE_PART('year', AGE(CURRENT_DATE, member.birthdate))` + : "TIMESTAMPDIFF(YEAR, member.birthdate, CURDATE())", + "todayAge" + ) + .addSelect( + DB_TYPE == "postgres" + ? `EXTRACT(YEAR FROM CURRENT_DATE) - EXTRACT(YEAR FROM member.birthdate)` + : "YEAR(CURDATE()) - YEAR(member.birthdate)", + "ageThisYear" + ) + .addSelect( + DB_TYPE == "postgres" + ? `AGE(CURRENT_DATE, member.birthdate)` + : "CONCAT('_', FROM_DAYS(TIMESTAMPDIFF(DAY, member.birthdate, CURDATE())))", + "exactAge" + ) .leftJoin("member.salutation", "salutation"), }) export class memberView { diff --git a/src/views/membershipsView.ts b/src/views/membershipsView.ts index 52ebcc7..5a614de 100644 --- a/src/views/membershipsView.ts +++ b/src/views/membershipsView.ts @@ -1,5 +1,6 @@ import { DataSource, ViewColumn, ViewEntity } from "typeorm"; import { membership } from "../entity/club/member/membership"; +import { DB_TYPE } from "../env.defaults"; @ViewEntity({ expression: (datasource: DataSource) => @@ -14,16 +15,24 @@ import { membership } from "../entity/club/member/membership"; .addSelect("member.nameaffix", "memberNameaffix") .addSelect("member.birthdate", "memberBirthdate") .addSelect("salutation.salutation", "memberSalutation") - .addSelect("SUM(TIMESTAMPDIFF(DAY, membership.start, COALESCE(membership.end, CURRENT_DATE)))", "durationInDays") .addSelect( - "CONCAT('_', FROM_DAYS(SUM(TIMESTAMPDIFF(DAY, membership.start, COALESCE(membership.end, CURRENT_DATE)))))", + DB_TYPE == "postgres" + ? `SUM(COALESCE("membership"."end", CURRENT_DATE) - "membership"."start") ` + : "SUM(TIMESTAMPDIFF(DAY, membership.start, COALESCE(membership.end, CURRENT_DATE)))", + "durationInDays" + ) + .addSelect( + DB_TYPE == "postgres" + ? `SUM(AGE(COALESCE("membership"."end", CURRENT_DATE), "membership"."start"))` + : "CONCAT('_', FROM_DAYS(SUM(TIMESTAMPDIFF(DAY, membership.start, COALESCE(membership.end, CURRENT_DATE)))))", "durationInYears" ) .leftJoin("membership.status", "status") .leftJoin("membership.member", "member") .leftJoin("member.salutation", "salutation") .groupBy("status.id") - .addGroupBy("member.id"), + .addGroupBy("member.id") + .addGroupBy("salutation.id"), }) export class membershipView { @ViewColumn() -- 2.45.3 From fc01263c4e634835f8094f707859f38a22850564 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Sat, 1 Feb 2025 18:09:58 +0100 Subject: [PATCH 19/22] enhance views to display exact year, month, day values sqlite displays negative days and months --- README.md | 2 +- src/factory/admin/club/member/member.ts | 1 + src/factory/admin/club/member/membership.ts | 1 + src/migrations/1738166167472-CreateSchema.ts | 24 ++- src/migrations/baseSchemaTables/member.ts | 165 ++++++++++++++++-- .../admin/club/member/member.models.ts | 1 + .../admin/club/member/membership.models.ts | 3 +- src/views/memberExecutivePositionView.ts | 50 ++++-- src/views/memberQualificationsView.ts | 50 ++++-- src/views/memberView.ts | 58 ++++-- src/views/membershipsView.ts | 50 ++++-- 11 files changed, 324 insertions(+), 81 deletions(-) diff --git a/README.md b/README.md index 432cc37..c697570 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ networks: ff_internal: ``` -Die Verwendung von postgres wird aufgrund des Verhaltens bei Struktur-Update-Fehlern und genauerer Datum-Berechnungen empfohlen. +Die Verwendung von postgres wird aufgrund des Verhaltens bei Datenbank-Update-Fehlern empfohlen. Die Verwendung von SQLite wird nur für die Entwicklung oder lokale Tests empfohlen. diff --git a/src/factory/admin/club/member/member.ts b/src/factory/admin/club/member/member.ts index e934025..95a46cb 100644 --- a/src/factory/admin/club/member/member.ts +++ b/src/factory/admin/club/member/member.ts @@ -51,6 +51,7 @@ export default abstract class MemberFactory { public static mapToMemberStatistic(record: memberView): MemberStatisticsViewModel { return { id: record.id, + internalId: record.internalId, salutation: record.salutation, firstname: record.firstname, lastname: record.lastname, diff --git a/src/factory/admin/club/member/membership.ts b/src/factory/admin/club/member/membership.ts index 42980ab..cbc171a 100644 --- a/src/factory/admin/club/member/membership.ts +++ b/src/factory/admin/club/member/membership.ts @@ -40,6 +40,7 @@ export default abstract class MembershipFactory { return { durationInDays: record.durationInDays, durationInYears: record.durationInYears, + exactDuration: record.exactDuration, status: record.status, statusId: record.statusId, memberId: record.memberId, diff --git a/src/migrations/1738166167472-CreateSchema.ts b/src/migrations/1738166167472-CreateSchema.ts index f5995b7..70a6c97 100644 --- a/src/migrations/1738166167472-CreateSchema.ts +++ b/src/migrations/1738166167472-CreateSchema.ts @@ -19,18 +19,22 @@ import { member_awards_table, member_communication_table, member_executive_positions_table, - member_executive_positions_view, + member_executive_positions_view_mysql, member_executive_positions_view_postgres, + member_executive_positions_view_sqlite, member_qualifications_table, - member_qualifications_view, + member_qualifications_view_mysql, member_qualifications_view_postgres, + member_qualifications_view_sqlite, member_table, - member_view, + member_view_mysql, member_view_postgres, + member_view_sqlite, membership_status_table, membership_table, - membership_view, + membership_view_mysql, membership_view_postgres, + membership_view_sqlite, qualification_table, salutation_table, } from "./baseSchemaTables/member"; @@ -81,13 +85,17 @@ export class CreateSchema1738166167472 implements MigrationInterface { await queryRunner.createTable(member_qualifications_table, true, true, true); if (DB_TYPE == "postgres") await queryRunner.createView(member_view_postgres, true); - else await queryRunner.createView(member_view, true); + else if (DB_TYPE == "mysql") await queryRunner.createView(member_view_mysql, true); + else if (DB_TYPE == "sqlite") await queryRunner.createView(member_view_sqlite, true); if (DB_TYPE == "postgres") await queryRunner.createView(membership_view_postgres, true); - else await queryRunner.createView(membership_view, true); + else if (DB_TYPE == "mysql") await queryRunner.createView(membership_view_mysql, true); + else if (DB_TYPE == "sqlite") await queryRunner.createView(membership_view_sqlite, true); if (DB_TYPE == "postgres") await queryRunner.createView(member_qualifications_view_postgres, true); - else await queryRunner.createView(member_qualifications_view, true); + else if (DB_TYPE == "mysql") await queryRunner.createView(member_qualifications_view_mysql, true); + else if (DB_TYPE == "sqlite") await queryRunner.createView(member_qualifications_view_sqlite, true); if (DB_TYPE == "postgres") await queryRunner.createView(member_executive_positions_view_postgres, true); - else await queryRunner.createView(member_executive_positions_view, true); + else if (DB_TYPE == "mysql") await queryRunner.createView(member_executive_positions_view_mysql, true); + else if (DB_TYPE == "sqlite") await queryRunner.createView(member_executive_positions_view_sqlite, true); await queryRunner.createTable(query_table, true, true, true); await queryRunner.createTable(template_table, true, true, true); diff --git a/src/migrations/baseSchemaTables/member.ts b/src/migrations/baseSchemaTables/member.ts index c2657cb..422f3cd 100644 --- a/src/migrations/baseSchemaTables/member.ts +++ b/src/migrations/baseSchemaTables/member.ts @@ -223,11 +223,12 @@ export const member_communication_table = new Table({ }); /** views */ -export const member_view = new View({ +export const member_view_mysql = new View({ name: "member_view", expression: ` SELECT \`member\`.\`id\` AS \`id\`, + \`member\`.\`internalId\` AS \`internalId\`, \`member\`.\`firstname\` AS \`firstname\`, \`member\`.\`lastname\` AS \`lastname\`, \`member\`.\`nameaffix\` AS \`nameaffix\`, @@ -235,7 +236,17 @@ export const member_view = new View({ \`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\` + CONCAT( + TIMESTAMPDIFF(YEAR, \`member\`.\`birthdate\`, CURDATE()), ' years ', + TIMESTAMPDIFF(MONTH, \`member\`.\`birthdate\`, CURDATE()) % 12, ' months ', + TIMESTAMPDIFF(DAY, + DATE_ADD( + \`member\`.\`birthdate\`, + INTERVAL TIMESTAMPDIFF(MONTH, \`member\`.\`birthdate\`, CURDATE()) MONTH + ), + CURDATE() + ), ' days' + ) AS \`exactAge\` FROM \`member\` \`member\` LEFT JOIN \`salutation\` \`salutation\` ON \`salutation\`.\`id\`=\`member\`.\`salutationId\` `, @@ -246,6 +257,7 @@ export const member_view_postgres = new View({ expression: ` SELECT "member"."id" AS "id", + "member"."internalId" AS "internalId", "member"."firstname" AS "firstname", "member"."lastname" AS "lastname", "member"."nameaffix" AS "nameaffix", @@ -259,7 +271,29 @@ export const member_view_postgres = new View({ `, }); -export const member_executive_positions_view = new View({ +export const member_view_sqlite = new View({ + name: "member_view", + expression: ` + SELECT + member.id AS id, + member.internalId AS internalId, + member.firstname AS firstname, + member.lastname AS lastname, + member.nameaffix AS nameaffix, + member.birthdate AS birthdate, + salutation.salutation AS salutation, + (strftime('%Y', 'now') - strftime('%Y', member.birthdate) - (strftime('%m-%d', 'now') < strftime('%m-%d', member.birthdate))) AS todayAge, + FLOOR(strftime('%Y', 'now') - strftime('%Y', member.birthdate)) AS ageThisYear, + (strftime('%Y', 'now') - strftime('%Y', member.birthdate)) || ' years ' || + (strftime('%m', 'now') - strftime('%m', member.birthdate)) || ' months ' || + (strftime('%d', 'now') - strftime('%d', member.birthdate)) || ' days' + AS exactAge + FROM member member + LEFT JOIN salutation salutation ON salutation.id=member.salutationId + `, +}); + +export const member_executive_positions_view_mysql = new View({ name: "member_executive_positions_view", expression: ` SELECT @@ -271,8 +305,16 @@ export const member_executive_positions_view = new View({ \`member\`.\`nameaffix\` AS \`memberNameaffix\`, \`member\`.\`birthdate\` AS \`memberBirthdate\`, \`salutation\`.\`salutation\` AS \`memberSalutation\`, - SUM(TIMESTAMPDIFF(DAY, \`memberExecutivePositions\`.\`start\`, COALESCE(\`memberExecutivePositions\`.\`end\`, CURRENT_DATE))) AS \`durationInDays\`, - CONCAT('_', FROM_DAYS(SUM(TIMESTAMPDIFF(DAY, \`memberExecutivePositions\`.\`start\`, COALESCE(\`memberExecutivePositions\`.\`end\`, CURRENT_DATE))))) AS \`durationInYears\` + SUM(DATEDIFF(COALESCE(\`memberExecutivePositions\`.\`end\`, CURDATE()), \`memberExecutivePositions\`.\`start\`)) AS \`durationInDays\`, + SUM(TIMESTAMPDIFF(YEAR, \`memberExecutivePositions\`.\`start\`, COALESCE(\`memberExecutivePositions\`.\`end\`, CURDATE()))) AS \`durationInYears\`, + CONCAT( + SUM(FLOOR(TIMESTAMPDIFF(DAY, \`memberExecutivePositions\`.\`start\`, COALESCE(\`memberExecutivePositions\`.\`end\`, CURDATE())) / 365.25)), + ' years ', + SUM(FLOOR(MOD(TIMESTAMPDIFF(MONTH, \`memberExecutivePositions\`.\`start\`, COALESCE(\`memberExecutivePositions\`.\`end\`, CURDATE())), 12))), + ' months ', + SUM(FLOOR(MOD(TIMESTAMPDIFF(DAY, \`memberExecutivePositions\`.\`start\`, COALESCE(\`memberExecutivePositions\`.\`end\`, CURDATE())), 30))), + ' days' + ) AS \`exactDuration\` FROM \`member_executive_positions\` \`memberExecutivePositions\` LEFT JOIN \`executive_position\` \`executivePosition\` ON \`executivePosition\`.\`id\`=\`memberExecutivePositions\`.\`executivePositionId\` LEFT JOIN \`member\` \`member\` ON \`member\`.\`id\`=\`memberExecutivePositions\`.\`memberId\` @@ -294,7 +336,8 @@ export const member_executive_positions_view_postgres = new View({ "member"."birthdate" AS "memberBirthdate", "salutation"."salutation" AS "memberSalutation", SUM(COALESCE("memberExecutivePositions"."end", CURRENT_DATE) - "memberExecutivePositions"."start") AS "durationInDays", - SUM(AGE(COALESCE("memberExecutivePositions"."end", CURRENT_DATE), "memberExecutivePositions"."start")) AS "durationInYears" + SUM(EXTRACT(YEAR FROM CURRENT_DATE) - EXTRACT(YEAR FROM member.birthdate)) AS "durationInYears", + SUM(AGE(COALESCE("memberExecutivePositions"."end", CURRENT_DATE), "memberExecutivePositions"."start")) AS "exactDuration" FROM "member_executive_positions" "memberExecutivePositions" LEFT JOIN "executive_position" "executivePosition" ON "executivePosition"."id"="memberExecutivePositions"."executivePositionId" LEFT JOIN "member" "member" ON "member"."id"="memberExecutivePositions"."memberId" @@ -303,7 +346,33 @@ export const member_executive_positions_view_postgres = new View({ `, }); -export const member_qualifications_view = new View({ +export const member_executive_positions_view_sqlite = 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(JULIANDAY(COALESCE(memberExecutivePositions.end, DATE('now'))) - JULIANDAY(memberExecutivePositions.start)) AS durationInDays, + SUM(FLOOR((JULIANDAY(COALESCE(memberExecutivePositions.end, DATE('now'))) - JULIANDAY(memberExecutivePositions.start)) / 365.25)) AS durationInYears, + SUM((strftime('%Y', COALESCE(memberExecutivePositions.end, DATE('now'))) - strftime('%Y', memberExecutivePositions.start))) || ' years ' || + SUM((strftime('%m', COALESCE(memberExecutivePositions.end, DATE('now'))) - strftime('%m', memberExecutivePositions.start))) || ' months ' || + SUM((strftime('%d', COALESCE(memberExecutivePositions.end, DATE('now'))) - strftime('%d', memberExecutivePositions.start))) || ' days' + AS exactDuration + 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, salutation.id + `, +}); + +export const member_qualifications_view_mysql = new View({ name: "member_qualifications_view", expression: ` SELECT @@ -314,9 +383,17 @@ export const member_qualifications_view = new View({ \`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\`, - CONCAT('_', FROM_DAYS(SUM(TIMESTAMPDIFF(DAY, \`memberQualifications\`.\`start\`, COALESCE(\`memberQualifications\`.\`end\`, CURRENT_DATE))))) AS \`durationInYears\` + \`salutation\`.\`salutation\` AS \`memberSalutation\`, + SUM(DATEDIFF(COALESCE(\`memberQualifications\`.\`end\`, CURDATE()), \`memberQualifications\`.\`start\`)) AS \`durationInDays\`, + SUM(TIMESTAMPDIFF(YEAR, \`memberQualifications\`.\`start\`, COALESCE(\`memberQualifications\`.\`end\`, CURDATE()))) AS \`durationInYears\`, + CONCAT( + SUM(FLOOR(TIMESTAMPDIFF(DAY, \`memberQualifications\`.\`start\`, COALESCE(\`memberQualifications\`.\`end\`, CURDATE())) / 365.25)), + ' years ', + SUM(FLOOR(MOD(TIMESTAMPDIFF(MONTH, \`memberQualifications\`.\`start\`, COALESCE(\`memberQualifications\`.\`end\`, CURDATE())), 12))), + ' months ', + SUM(FLOOR(MOD(TIMESTAMPDIFF(DAY, \`memberQualifications\`.\`start\`, COALESCE(\`memberQualifications\`.\`end\`, CURDATE())), 30))), + ' days' + ) AS \`exactDuration\` FROM \`member_qualifications\` \`memberQualifications\` LEFT JOIN \`qualification\` \`qualification\` ON \`qualification\`.\`id\`=\`memberQualifications\`.\`qualificationId\` LEFT JOIN \`member\` \`member\` ON \`member\`.\`id\`=\`memberQualifications\`.\`memberId\` @@ -347,7 +424,33 @@ export const member_qualifications_view_postgres = new View({ `, }); -export const membership_view = new View({ +export const member_qualifications_view_sqlite = 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(JULIANDAY(COALESCE(memberQualifications.end, DATE('now'))) - JULIANDAY(memberQualifications.start)) AS durationInDays, + SUM(FLOOR((JULIANDAY(COALESCE(memberQualifications.end, DATE('now'))) - JULIANDAY(memberQualifications.start)) / 365.25)) AS durationInYears, + SUM((strftime('%Y', COALESCE(memberQualifications.end, DATE('now'))) - strftime('%Y', memberQualifications.start))) || ' years ' || + SUM((strftime('%m', COALESCE(memberQualifications.end, DATE('now'))) - strftime('%m', memberQualifications.start))) || ' months ' || + SUM((strftime('%d', COALESCE(memberQualifications.end, DATE('now'))) - strftime('%d', memberQualifications.start))) || ' days' + AS exactDuration + 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, salutation.id + `, +}); + +export const membership_view_mysql = new View({ name: "membership_view", expression: ` SELECT @@ -358,9 +461,17 @@ export const membership_view = new View({ \`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\` + \`salutation\`.\`salutation\` AS \`memberSalutation\`, + SUM(DATEDIFF(COALESCE(\`membership\`.\`end\`, CURDATE()), \`membership\`.\`start\`)) AS \`durationInDays\`, + SUM(TIMESTAMPDIFF(YEAR, \`membership\`.\`start\`, COALESCE(\`membership\`.\`end\`, CURDATE()))) AS \`durationInYears\`, + CONCAT( + SUM(FLOOR(TIMESTAMPDIFF(DAY, \`membership\`.\`start\`, COALESCE(\`membership\`.\`end\`, CURDATE())) / 365.25)), + ' years ', + SUM(FLOOR(MOD(TIMESTAMPDIFF(MONTH, \`membership\`.\`start\`, COALESCE(\`membership\`.\`end\`, CURDATE())), 12))), + ' months ', + SUM(FLOOR(MOD(TIMESTAMPDIFF(DAY, \`membership\`.\`start\`, COALESCE(\`membership\`.\`end\`, CURDATE())), 30))), + ' days' + ) AS \`exactDuration\` FROM \`membership\` \`membership\` LEFT JOIN \`membership_status\` \`status\` ON \`status\`.\`id\`=\`membership\`.\`statusId\` LEFT JOIN \`member\` \`member\` ON \`member\`.\`id\`=\`membership\`.\`memberId\` @@ -390,3 +501,29 @@ export const membership_view_postgres = new View({ GROUP BY "status"."id","member"."id", "salutation"."id" `, }); + +export const membership_view_sqlite = 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(JULIANDAY(COALESCE(membership.end, DATE('now'))) - JULIANDAY(membership.start)) AS durationInDays, + SUM(FLOOR((JULIANDAY(COALESCE(membership.end, DATE('now'))) - JULIANDAY(membership.start)) / 365.25)) AS durationInYears, + SUM((strftime('%Y', COALESCE(membership.end, DATE('now'))) - strftime('%Y', membership.start))) || ' years ' || + SUM((strftime('%m', COALESCE(membership.end, DATE('now'))) - strftime('%m', membership.start))) || ' months ' || + SUM((strftime('%d', COALESCE(membership.end, DATE('now'))) - strftime('%d', membership.start))) || ' days' + AS exactDuration + 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, salutation.id + `, +}); diff --git a/src/viewmodel/admin/club/member/member.models.ts b/src/viewmodel/admin/club/member/member.models.ts index 466a6f7..69f1bdf 100644 --- a/src/viewmodel/admin/club/member/member.models.ts +++ b/src/viewmodel/admin/club/member/member.models.ts @@ -19,6 +19,7 @@ export interface MemberViewModel { export interface MemberStatisticsViewModel { id: string; + internalId: string; salutation: string; firstname: string; lastname: string; diff --git a/src/viewmodel/admin/club/member/membership.models.ts b/src/viewmodel/admin/club/member/membership.models.ts index 6b53086..ace02ea 100644 --- a/src/viewmodel/admin/club/member/membership.models.ts +++ b/src/viewmodel/admin/club/member/membership.models.ts @@ -9,7 +9,8 @@ export interface MembershipViewModel { export interface MembershipStatisticsViewModel { durationInDays: number; - durationInYears: string; + durationInYears: number; + exactDuration: string; status: string; statusId: number; memberId: string; diff --git a/src/views/memberExecutivePositionView.ts b/src/views/memberExecutivePositionView.ts index 610fe34..f82e35c 100644 --- a/src/views/memberExecutivePositionView.ts +++ b/src/views/memberExecutivePositionView.ts @@ -2,6 +2,36 @@ import { DataSource, ViewColumn, ViewEntity } from "typeorm"; import { memberExecutivePositions } from "../entity/club/member/memberExecutivePositions"; import { DB_TYPE } from "../env.defaults"; +let durationInDays: string; +let durationInYears: string; +let exactDuration: string; +if (DB_TYPE == "postgres") { + durationInDays = `SUM(COALESCE("memberExecutivePositions"."end", CURRENT_DATE) - "memberExecutivePositions"."start")`; + durationInYears = `SUM(EXTRACT(YEAR FROM CURRENT_DATE) - EXTRACT(YEAR FROM member.birthdate))`; + exactDuration = `SUM(AGE(COALESCE("memberExecutivePositions"."end", CURRENT_DATE), "memberExecutivePositions"."start"))`; +} else if (DB_TYPE == "mysql") { + durationInDays = `SUM(DATEDIFF(COALESCE(memberExecutivePositions.end, CURDATE()), memberExecutivePositions.start))`; + durationInYears = `SUM(TIMESTAMPDIFF(YEAR, memberExecutivePositions.start, COALESCE(memberExecutivePositions.end, CURDATE())))`; + exactDuration = ` + CONCAT( + SUM(FLOOR(TIMESTAMPDIFF(DAY, memberExecutivePositions.start, COALESCE(memberExecutivePositions.end, CURDATE())) / 365.25)), + ' years ', + SUM(FLOOR(MOD(TIMESTAMPDIFF(MONTH, memberExecutivePositions.start, COALESCE(memberExecutivePositions.end, CURDATE())), 12))), + ' months ', + SUM(FLOOR(MOD(TIMESTAMPDIFF(DAY, memberExecutivePositions.start, COALESCE(memberExecutivePositions.end, CURDATE())), 30))), + ' days' + ) + `; +} else if (DB_TYPE == "sqlite") { + durationInDays = `SUM(JULIANDAY(COALESCE(memberExecutivePositions.end, DATE('now'))) - JULIANDAY(memberExecutivePositions.start))`; + durationInYears = `SUM(FLOOR((JULIANDAY(COALESCE(memberExecutivePositions.end, DATE('now'))) - JULIANDAY(memberExecutivePositions.start)) / 365.25))`; + exactDuration = ` + SUM((strftime('%Y', COALESCE(memberExecutivePositions.end, DATE('now'))) - strftime('%Y', memberExecutivePositions.start))) || ' years ' || + SUM((strftime('%m', COALESCE(memberExecutivePositions.end, DATE('now'))) - strftime('%m', memberExecutivePositions.start))) || ' months ' || + SUM((strftime('%d', COALESCE(memberExecutivePositions.end, DATE('now'))) - strftime('%d', memberExecutivePositions.start))) || ' days' + `; +} + @ViewEntity({ expression: (datasource: DataSource) => datasource @@ -15,18 +45,9 @@ import { DB_TYPE } from "../env.defaults"; .addSelect("member.nameaffix", "memberNameaffix") .addSelect("member.birthdate", "memberBirthdate") .addSelect("salutation.salutation", "memberSalutation") - .addSelect( - DB_TYPE == "postgres" - ? `SUM(COALESCE("memberExecutivePositions"."end", CURRENT_DATE) - "memberExecutivePositions"."start")` - : "SUM(TIMESTAMPDIFF(DAY, memberExecutivePositions.start, COALESCE(memberExecutivePositions.end, CURRENT_DATE)))", - "durationInDays" - ) - .addSelect( - DB_TYPE == "postgres" - ? `SUM(AGE(COALESCE("memberExecutivePositions"."end", CURRENT_DATE), "memberExecutivePositions"."start"))` - : "CONCAT('_', FROM_DAYS(SUM(TIMESTAMPDIFF(DAY, memberExecutivePositions.start, COALESCE(memberExecutivePositions.end, CURRENT_DATE)))))", - "durationInYears" - ) + .addSelect(durationInDays, "durationInDays") + .addSelect(durationInYears, "durationInYears") + .addSelect(exactDuration, "exactDuration") .leftJoin("memberExecutivePositions.executivePosition", "executivePosition") .leftJoin("memberExecutivePositions.member", "member") .leftJoin("member.salutation", "salutation") @@ -39,7 +60,10 @@ export class memberExecutivePositionsView { durationInDays: number; @ViewColumn() - durationInYears: string; + durationInYears: number; + + @ViewColumn() + exactDuration: string; @ViewColumn() position: string; diff --git a/src/views/memberQualificationsView.ts b/src/views/memberQualificationsView.ts index 1e6dd55..45d6089 100644 --- a/src/views/memberQualificationsView.ts +++ b/src/views/memberQualificationsView.ts @@ -2,6 +2,36 @@ import { DataSource, ViewColumn, ViewEntity } from "typeorm"; import { memberQualifications } from "../entity/club/member/memberQualifications"; import { DB_TYPE } from "../env.defaults"; +let durationInDays: string; +let durationInYears: string; +let exactDuration: string; +if (DB_TYPE == "postgres") { + durationInDays = `SUM(COALESCE("memberQualifications"."end", CURRENT_DATE) - "memberQualifications"."start") `; + durationInYears = `SUM(EXTRACT(YEAR FROM CURRENT_DATE) - EXTRACT(YEAR FROM member.birthdate))`; + exactDuration = `SUM(AGE(COALESCE("memberQualifications"."end", CURRENT_DATE), "memberQualifications"."start"))`; +} else if (DB_TYPE == "mysql") { + durationInDays = `SUM(DATEDIFF(COALESCE(memberQualifications.end, CURDATE()), memberQualifications.start))`; + durationInYears = `SUM(TIMESTAMPDIFF(YEAR, memberQualifications.start, COALESCE(memberQualifications.end, CURDATE())))`; + exactDuration = ` + CONCAT( + SUM(FLOOR(TIMESTAMPDIFF(DAY, memberQualifications.start, COALESCE(memberQualifications.end, CURDATE())) / 365.25)), + ' years ', + SUM(FLOOR(MOD(TIMESTAMPDIFF(MONTH, memberQualifications.start, COALESCE(memberQualifications.end, CURDATE())), 12))), + ' months ', + SUM(FLOOR(MOD(TIMESTAMPDIFF(DAY, memberQualifications.start, COALESCE(memberQualifications.end, CURDATE())), 30))), + ' days' + ) + `; +} else if (DB_TYPE == "sqlite") { + durationInDays = `SUM(JULIANDAY(COALESCE(memberQualifications.end, DATE('now'))) - JULIANDAY(memberQualifications.start))`; + durationInYears = `SUM(FLOOR((JULIANDAY(COALESCE(memberQualifications.end, DATE('now'))) - JULIANDAY(memberQualifications.start)) / 365.25))`; + exactDuration = ` + SUM((strftime('%Y', COALESCE(memberQualifications.end, DATE('now'))) - strftime('%Y', memberQualifications.start))) || ' years ' || + SUM((strftime('%m', COALESCE(memberQualifications.end, DATE('now'))) - strftime('%m', memberQualifications.start))) || ' months ' || + SUM((strftime('%d', COALESCE(memberQualifications.end, DATE('now'))) - strftime('%d', memberQualifications.start))) || ' days' + `; +} + @ViewEntity({ expression: (datasource: DataSource) => datasource @@ -15,18 +45,9 @@ import { DB_TYPE } from "../env.defaults"; .addSelect("member.nameaffix", "memberNameaffix") .addSelect("member.birthdate", "memberBirthdate") .addSelect("salutation.salutation", "memberSalutation") - .addSelect( - DB_TYPE == "postgres" - ? `SUM(COALESCE("memberQualifications"."end", CURRENT_DATE) - "memberQualifications"."start")` - : "SUM(TIMESTAMPDIFF(DAY, memberQualifications.start, COALESCE(memberQualifications.end, CURRENT_DATE)))", - "durationInDays" - ) - .addSelect( - DB_TYPE == "postgres" - ? `SUM(AGE(COALESCE("memberQualifications"."end", CURRENT_DATE), "memberQualifications"."start"))` - : "CONCAT('_', FROM_DAYS(SUM(TIMESTAMPDIFF(DAY, memberQualifications.start, COALESCE(memberQualifications.end, CURRENT_DATE)))))", - "durationInYears" - ) + .addSelect(durationInDays, "durationInDays") + .addSelect(durationInYears, "durationInYears") + .addSelect(exactDuration, "exactDuration") .leftJoin("memberQualifications.qualification", "qualification") .leftJoin("memberQualifications.member", "member") .leftJoin("member.salutation", "salutation") @@ -39,7 +60,10 @@ export class memberQualificationsView { durationInDays: number; @ViewColumn() - durationInYears: string; + durationInYears: number; + + @ViewColumn() + exactDuration: string; @ViewColumn() qualification: string; diff --git a/src/views/memberView.ts b/src/views/memberView.ts index 11de1e8..194bade 100644 --- a/src/views/memberView.ts +++ b/src/views/memberView.ts @@ -2,35 +2,54 @@ import { DataSource, ViewColumn, ViewEntity } from "typeorm"; import { member } from "../entity/club/member/member"; import { DB_TYPE } from "../env.defaults"; +let todayAge: string; +let ageThisYear: string; +let exactAge: string; +if (DB_TYPE == "postgres") { + todayAge = `DATE_PART('year', AGE(CURRENT_DATE, member.birthdate))`; + ageThisYear = `EXTRACT(YEAR FROM CURRENT_DATE) - EXTRACT(YEAR FROM member.birthdate)`; + exactAge = `AGE(CURRENT_DATE, member.birthdate)`; +} else if (DB_TYPE == "mysql") { + todayAge = `TIMESTAMPDIFF(YEAR, member.birthdate, CURDATE()) AS todayAge`; + ageThisYear = `YEAR(CURDATE()) - YEAR(member.birthdate) AS ageThisYear`; + exactAge = ` + CONCAT( + TIMESTAMPDIFF(YEAR, member.birthdate, CURDATE()), ' years ', + TIMESTAMPDIFF(MONTH, member.birthdate, CURDATE()) % 12, ' months ', + TIMESTAMPDIFF(DAY, + DATE_ADD( + member.birthdate, + INTERVAL TIMESTAMPDIFF(MONTH, member.birthdate, CURDATE()) MONTH + ), + CURDATE() + ), ' days' + ) + `; +} else if (DB_TYPE == "sqlite") { + todayAge = `(strftime('%Y', 'now') - strftime('%Y', member.birthdate) - (strftime('%m-%d', 'now') < strftime('%m-%d', member.birthdate)))`; + ageThisYear = `strftime('%Y', 'now') - strftime('%Y', member.birthdate)`; + exactAge = ` + (strftime('%Y', 'now') - strftime('%Y', member.birthdate)) || ' years ' || + (strftime('%m', 'now') - strftime('%m', member.birthdate)) || ' months ' || + (strftime('%d', 'now') - strftime('%d', member.birthdate)) || ' days' + `; +} + @ViewEntity({ expression: (datasource: DataSource) => datasource .getRepository(member) .createQueryBuilder("member") .select("member.id", "id") + .select("member.internalId", "internalId") .addSelect("member.firstname", "firstname") .addSelect("member.lastname", "lastname") .addSelect("member.nameaffix", "nameaffix") .addSelect("member.birthdate", "birthdate") .addSelect("salutation.salutation", "salutation") - .addSelect( - DB_TYPE == "postgres" - ? `DATE_PART('year', AGE(CURRENT_DATE, member.birthdate))` - : "TIMESTAMPDIFF(YEAR, member.birthdate, CURDATE())", - "todayAge" - ) - .addSelect( - DB_TYPE == "postgres" - ? `EXTRACT(YEAR FROM CURRENT_DATE) - EXTRACT(YEAR FROM member.birthdate)` - : "YEAR(CURDATE()) - YEAR(member.birthdate)", - "ageThisYear" - ) - .addSelect( - DB_TYPE == "postgres" - ? `AGE(CURRENT_DATE, member.birthdate)` - : "CONCAT('_', FROM_DAYS(TIMESTAMPDIFF(DAY, member.birthdate, CURDATE())))", - "exactAge" - ) + .addSelect(todayAge, "todayAge") + .addSelect(ageThisYear, "ageThisYear") + .addSelect(exactAge, "exactAge") .leftJoin("member.salutation", "salutation"), }) export class memberView { @@ -40,6 +59,9 @@ export class memberView { @ViewColumn() salutation: string; + @ViewColumn() + internalId: string; + @ViewColumn() firstname: string; diff --git a/src/views/membershipsView.ts b/src/views/membershipsView.ts index 5a614de..68d1927 100644 --- a/src/views/membershipsView.ts +++ b/src/views/membershipsView.ts @@ -2,6 +2,36 @@ import { DataSource, ViewColumn, ViewEntity } from "typeorm"; import { membership } from "../entity/club/member/membership"; import { DB_TYPE } from "../env.defaults"; +let durationInDays: string; +let durationInYears: string; +let exactDuration: string; +if (DB_TYPE == "postgres") { + durationInDays = `SUM(COALESCE("membership"."end", CURRENT_DATE) - "membership"."start") `; + durationInYears = `SUM(EXTRACT(YEAR FROM CURRENT_DATE) - EXTRACT(YEAR FROM member.birthdate))`; + exactDuration = `SUM(AGE(COALESCE("membership"."end", CURRENT_DATE), "membership"."start"))`; +} else if (DB_TYPE == "mysql") { + durationInDays = `SUM(DATEDIFF(COALESCE(membership.end, CURDATE()), membership.start))`; + durationInYears = `SUM(TIMESTAMPDIFF(YEAR, membership.start, COALESCE(membership.end, CURDATE())))`; + exactDuration = ` + CONCAT( + SUM(FLOOR(TIMESTAMPDIFF(DAY, membership.start, COALESCE(membership.end, CURDATE())) / 365.25)), + ' years ', + SUM(FLOOR(MOD(TIMESTAMPDIFF(MONTH, membership.start, COALESCE(membership.end, CURDATE())), 12))), + ' months ', + SUM(FLOOR(MOD(TIMESTAMPDIFF(DAY, membership.start, COALESCE(membership.end, CURDATE())), 30))), + ' days' + ) + `; +} else if (DB_TYPE == "sqlite") { + durationInDays = `SUM(JULIANDAY(COALESCE(membership.end, DATE('now'))) - JULIANDAY(membership.start))`; + durationInYears = `SUM(FLOOR((JULIANDAY(COALESCE(membership.end, DATE('now'))) - JULIANDAY(membership.start)) / 365.25))`; + exactDuration = ` + SUM((strftime('%Y', COALESCE(membership.end, DATE('now'))) - strftime('%Y', membership.start))) || ' years ' || + SUM((strftime('%m', COALESCE(membership.end, DATE('now'))) - strftime('%m', membership.start))) || ' months ' || + SUM((strftime('%d', COALESCE(membership.end, DATE('now'))) - strftime('%d', membership.start))) || ' days' + `; +} + @ViewEntity({ expression: (datasource: DataSource) => datasource @@ -15,18 +45,9 @@ import { DB_TYPE } from "../env.defaults"; .addSelect("member.nameaffix", "memberNameaffix") .addSelect("member.birthdate", "memberBirthdate") .addSelect("salutation.salutation", "memberSalutation") - .addSelect( - DB_TYPE == "postgres" - ? `SUM(COALESCE("membership"."end", CURRENT_DATE) - "membership"."start") ` - : "SUM(TIMESTAMPDIFF(DAY, membership.start, COALESCE(membership.end, CURRENT_DATE)))", - "durationInDays" - ) - .addSelect( - DB_TYPE == "postgres" - ? `SUM(AGE(COALESCE("membership"."end", CURRENT_DATE), "membership"."start"))` - : "CONCAT('_', FROM_DAYS(SUM(TIMESTAMPDIFF(DAY, membership.start, COALESCE(membership.end, CURRENT_DATE)))))", - "durationInYears" - ) + .addSelect(durationInDays, "durationInDays") + .addSelect(durationInYears, "durationInYears") + .addSelect(exactDuration, "exactDuration") .leftJoin("membership.status", "status") .leftJoin("membership.member", "member") .leftJoin("member.salutation", "salutation") @@ -39,7 +60,10 @@ export class membershipView { durationInDays: number; @ViewColumn() - durationInYears: string; + durationInYears: number; + + @ViewColumn() + exactDuration: string; @ViewColumn() status: string; -- 2.45.3 From 6330ebd01dbcc966001fa5057e6751e603c91cf1 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Sun, 2 Feb 2025 16:23:44 +0100 Subject: [PATCH 20/22] backup serving, storing and restoring --- package-lock.json | 103 ++++++++++++++++++ package.json | 2 + src/controller/admin/club/backupController.ts | 75 +++++++++++++ src/helpers/backupHelper.ts | 6 + src/index.ts | 1 + src/routes/admin/index.ts | 4 +- src/routes/admin/user/backup.ts | 67 ++++++++++++ src/type/permissionTypes.ts | 6 +- 8 files changed, 261 insertions(+), 3 deletions(-) create mode 100644 src/controller/admin/club/backupController.ts create mode 100644 src/routes/admin/user/backup.ts diff --git a/package-lock.json b/package-lock.json index 75ebbc6..6ae2a2d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "lodash.uniqby": "^4.7.0", "moment": "^2.30.1", "ms": "^2.1.3", + "multer": "^1.4.5-lts.1", "mysql": "^2.18.1", "node-schedule": "^2.1.1", "nodemailer": "^6.9.14", @@ -38,6 +39,7 @@ "@types/jsonwebtoken": "^9.0.6", "@types/lodash.uniqby": "^4.7.9", "@types/ms": "^0.7.34", + "@types/multer": "^1.4.12", "@types/mysql": "^2.15.21", "@types/node": "^16.18.41", "@types/node-schedule": "^2.1.6", @@ -567,6 +569,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/multer": { + "version": "1.4.12", + "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.12.tgz", + "integrity": "sha512-pQ2hoqvXiJt2FP9WQVLPRO+AmiIm/ZYkavPlIQnx282u4ZrVdztx0pkh3jjpQt0Kz+YI0YhSG264y08UJKoUQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express": "*" + } + }, "node_modules/@types/mysql": { "version": "2.15.26", "resolved": "https://registry.npmjs.org/@types/mysql/-/mysql-2.15.26.tgz", @@ -781,6 +793,12 @@ "node": ">= 6.0.0" } }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", + "license": "MIT" + }, "node_modules/aproba": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", @@ -1101,6 +1119,23 @@ "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", "license": "BSD-3-Clause" }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -1434,6 +1469,21 @@ "license": "MIT", "optional": true }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "engines": [ + "node >= 0.8" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, "node_modules/console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", @@ -3203,6 +3253,36 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, + "node_modules/multer": { + "version": "1.4.5-lts.1", + "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz", + "integrity": "sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==", + "license": "MIT", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^1.0.0", + "concat-stream": "^1.5.2", + "mkdirp": "^0.5.4", + "object-assign": "^4.1.1", + "type-is": "^1.6.4", + "xtend": "^4.0.0" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/multer/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, "node_modules/mysql": { "version": "2.18.1", "resolved": "https://registry.npmjs.org/mysql/-/mysql-2.18.1.tgz", @@ -4606,6 +4686,14 @@ "node": ">= 0.8" } }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/streamx": { "version": "2.21.1", "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.21.1.tgz", @@ -4931,6 +5019,12 @@ "integrity": "sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg==", "license": "MIT" }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "license": "MIT" + }, "node_modules/typeorm": { "version": "0.3.20", "resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.3.20.tgz", @@ -5370,6 +5464,15 @@ "node": ">=4.0" } }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, "node_modules/y18n": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", diff --git a/package.json b/package.json index 7c289f7..23d7fa7 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "lodash.uniqby": "^4.7.0", "moment": "^2.30.1", "ms": "^2.1.3", + "multer": "^1.4.5-lts.1", "mysql": "^2.18.1", "node-schedule": "^2.1.1", "nodemailer": "^6.9.14", @@ -53,6 +54,7 @@ "@types/jsonwebtoken": "^9.0.6", "@types/lodash.uniqby": "^4.7.9", "@types/ms": "^0.7.34", + "@types/multer": "^1.4.12", "@types/mysql": "^2.15.21", "@types/node": "^16.18.41", "@types/node-schedule": "^2.1.6", diff --git a/src/controller/admin/club/backupController.ts b/src/controller/admin/club/backupController.ts new file mode 100644 index 0000000..5d36791 --- /dev/null +++ b/src/controller/admin/club/backupController.ts @@ -0,0 +1,75 @@ +import { Request, Response } from "express"; +import { FileSystemHelper } from "../../../helpers/fileSystemHelper"; +import BackupHelper from "../../../helpers/backupHelper"; +import InternalException from "../../../exceptions/internalException"; + +/** + * @description get generated backups + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function getGeneratedBackups(req: Request, res: Response): Promise { + let filesInFolder = FileSystemHelper.getFilesInDirectory(`backup`); + + res.json(filesInFolder); +} + +/** + * @description download backup file + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function downloadBackupFile(req: Request, res: Response): Promise { + let filename = req.params.filename; + + let filepath = FileSystemHelper.formatPath("backup", filename); + + res.sendFile(filepath, { + headers: { + "Content-Type": "application/json", + }, + }); +} + +/** + * @description create backup manually + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function createManualBackup(req: Request, res: Response): Promise { + await BackupHelper.createBackup({}); + + res.sendStatus(204); +} + +/** + * @description restore backup by selected + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function restoreBackupByLocalFile(req: Request, res: Response): Promise { + let filename = req.body.filename; + let partial = req.body.partial; + let include = req.body.includes; + + await BackupHelper.loadBackup({ filename, include, partial }); + + res.sendStatus(204); +} + +/** + * @description upload backup + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function uploadBackupFile(req: Request, res: Response): Promise { + if (!req.file) { + throw new InternalException("File upload failed"); + } + res.sendStatus(204); +} diff --git a/src/helpers/backupHelper.ts b/src/helpers/backupHelper.ts index 1471974..f183993 100644 --- a/src/helpers/backupHelper.ts +++ b/src/helpers/backupHelper.ts @@ -96,6 +96,8 @@ export default abstract class BackupHelper { } FileSystemHelper.writeFile(path, filename + ".json", JSON.stringify(json, null, 2)); + + // TODO: delete older backups by copies env } static async loadBackup({ @@ -114,6 +116,10 @@ export default abstract class BackupHelper { let file = FileSystemHelper.readFile(`${path}/${filename}`); let backup: BackupFileContent = JSON.parse(file); + if ((partial && include.length == 0) || (!partial && include.length != 0)) { + throw new InternalException("partial and include have to be set correctly for restoring backup."); + } + dataSource.manager .transaction(async (transaction) => { this.transactionManager = transaction; diff --git a/src/index.ts b/src/index.ts index a189059..fe2fc80 100644 --- a/src/index.ts +++ b/src/index.ts @@ -40,4 +40,5 @@ import RefreshCommandHandler from "./command/refreshCommandHandler"; const job = schedule.scheduleJob("0 0 * * *", async () => { console.log(`${new Date().toISOString()}: running Cron`); await RefreshCommandHandler.deleteExpired(); + // TODO: create backup by interval env }); diff --git a/src/routes/admin/index.ts b/src/routes/admin/index.ts index c291c15..59d5990 100644 --- a/src/routes/admin/index.ts +++ b/src/routes/admin/index.ts @@ -1,5 +1,6 @@ import express from "express"; import PermissionHelper from "../../helpers/permissionHelper"; +import preventWebapiAccess from "../../middleware/preventWebApiAccess"; import award from "./settings/award"; import communicationType from "./settings/communicationType"; @@ -23,7 +24,7 @@ import role from "./user/role"; import user from "./user/user"; import invite from "./user/invite"; import api from "./user/webapi"; -import preventWebapiAccess from "../../middleware/preventWebApiAccess"; +import backup from "./user/backup"; var router = express.Router({ mergeParams: true }); @@ -143,5 +144,6 @@ router.use( ); router.use("/invite", PermissionHelper.passCheckMiddleware("read", "user", "user"), invite); router.use("/webapi", preventWebapiAccess, PermissionHelper.passCheckMiddleware("read", "user", "webapi"), api); +router.use("/backup", preventWebapiAccess, PermissionHelper.passCheckMiddleware("read", "user", "backup"), backup); export default router; diff --git a/src/routes/admin/user/backup.ts b/src/routes/admin/user/backup.ts new file mode 100644 index 0000000..e537655 --- /dev/null +++ b/src/routes/admin/user/backup.ts @@ -0,0 +1,67 @@ +import express, { Request, Response } from "express"; +import PermissionHelper from "../../../helpers/permissionHelper"; +import multer from "multer"; +import { + createManualBackup, + downloadBackupFile, + getGeneratedBackups, + restoreBackupByLocalFile, + uploadBackupFile, +} from "../../../controller/admin/club/backupController"; + +const storage = multer.diskStorage({ + destination: (req, file, cb) => { + cb(null, "files/backup/"); + }, + filename: (req, file, cb) => { + cb(null, `${new Date().toISOString().split("T")[0]}-uploaded-${file.originalname}`); + }, +}); + +const upload = multer({ + storage, + fileFilter: (req: Request, file, cb) => { + if (file.mimetype === "application/json") { + cb(null, true); + } else { + cb(new Error("Only JSON files are allowed!")); + } + }, +}); + +var router = express.Router({ mergeParams: true }); + +router.get("/", async (req: Request, res: Response) => { + await getGeneratedBackups(req, res); +}); + +router.get("/:filename", async (req: Request, res: Response) => { + await downloadBackupFile(req, res); +}); + +router.post( + "/", + PermissionHelper.passCheckMiddleware("create", "user", "backup"), + async (req: Request, res: Response) => { + await createManualBackup(req, res); + } +); + +router.post( + "/restore", + PermissionHelper.passCheckMiddleware("admin", "user", "backup"), + async (req: Request, res: Response) => { + await restoreBackupByLocalFile(req, res); + } +); + +router.post( + "/upload", + PermissionHelper.passCheckMiddleware("create", "user", "backup"), + upload.single("file"), + async (req: Request, res: Response) => { + await uploadBackupFile(req, res); + } +); + +export default router; diff --git a/src/type/permissionTypes.ts b/src/type/permissionTypes.ts index cd37e49..e3d3105 100644 --- a/src/type/permissionTypes.ts +++ b/src/type/permissionTypes.ts @@ -19,7 +19,8 @@ export type PermissionModule = | "query" | "query_store" | "template" - | "template_usage"; + | "template_usage" + | "backup"; export type PermissionType = "read" | "create" | "update" | "delete"; @@ -63,6 +64,7 @@ export const permissionModules: Array = [ "query_store", "template", "template_usage", + "backup", ]; export const permissionTypes: Array = ["read", "create", "update", "delete"]; export const sectionsAndModules: SectionsAndModulesObject = { @@ -80,5 +82,5 @@ export const sectionsAndModules: SectionsAndModulesObject = { "template_usage", "newsletter_config", ], - user: ["user", "role", "webapi"], + user: ["user", "role", "webapi", "backup"], }; -- 2.45.3 From 542a77fbeff19e6f34e0b8d03c58fd2148ca4da1 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Sun, 2 Feb 2025 16:37:55 +0100 Subject: [PATCH 21/22] fix backup await --- src/controller/admin/{club => user}/backupController.ts | 2 +- src/helpers/backupHelper.ts | 2 +- src/routes/admin/user/backup.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename src/controller/admin/{club => user}/backupController.ts (98%) diff --git a/src/controller/admin/club/backupController.ts b/src/controller/admin/user/backupController.ts similarity index 98% rename from src/controller/admin/club/backupController.ts rename to src/controller/admin/user/backupController.ts index 5d36791..069ab4d 100644 --- a/src/controller/admin/club/backupController.ts +++ b/src/controller/admin/user/backupController.ts @@ -54,7 +54,7 @@ export async function createManualBackup(req: Request, res: Response): Promise { let filename = req.body.filename; let partial = req.body.partial; - let include = req.body.includes; + let include = req.body.include; await BackupHelper.loadBackup({ filename, include, partial }); diff --git a/src/helpers/backupHelper.ts b/src/helpers/backupHelper.ts index f183993..ee649e7 100644 --- a/src/helpers/backupHelper.ts +++ b/src/helpers/backupHelper.ts @@ -120,7 +120,7 @@ export default abstract class BackupHelper { throw new InternalException("partial and include have to be set correctly for restoring backup."); } - dataSource.manager + await dataSource.manager .transaction(async (transaction) => { this.transactionManager = transaction; diff --git a/src/routes/admin/user/backup.ts b/src/routes/admin/user/backup.ts index e537655..d700636 100644 --- a/src/routes/admin/user/backup.ts +++ b/src/routes/admin/user/backup.ts @@ -7,7 +7,7 @@ import { getGeneratedBackups, restoreBackupByLocalFile, uploadBackupFile, -} from "../../../controller/admin/club/backupController"; +} from "../../../controller/admin/user/backupController"; const storage = multer.diskStorage({ destination: (req, file, cb) => { -- 2.45.3 From 0d6103170abdc778fc8a75413464a69228b58541 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Mon, 3 Feb 2025 11:03:31 +0100 Subject: [PATCH 22/22] split uploaded and generated backups --- src/controller/admin/user/backupController.ts | 55 ++++++++++++++++++- src/factory/admin/club/member/membership.ts | 2 +- src/helpers/backupHelper.ts | 37 +++++++++++-- src/helpers/fileSystemHelper.ts | 10 ++++ src/index.ts | 2 +- src/routes/admin/user/backup.ts | 31 +++++++++-- 6 files changed, 122 insertions(+), 15 deletions(-) diff --git a/src/controller/admin/user/backupController.ts b/src/controller/admin/user/backupController.ts index 069ab4d..25ef6dd 100644 --- a/src/controller/admin/user/backupController.ts +++ b/src/controller/admin/user/backupController.ts @@ -12,7 +12,9 @@ import InternalException from "../../../exceptions/internalException"; export async function getGeneratedBackups(req: Request, res: Response): Promise { let filesInFolder = FileSystemHelper.getFilesInDirectory(`backup`); - res.json(filesInFolder); + let sorted = filesInFolder.sort((a, b) => new Date(b.split(".")[0]).getTime() - new Date(a.split(".")[0]).getTime()); + + res.json(sorted); } /** @@ -33,6 +35,38 @@ export async function downloadBackupFile(req: Request, res: Response): Promise} + */ +export async function getUploadedBackups(req: Request, res: Response): Promise { + let filesInFolder = FileSystemHelper.getFilesInDirectory("uploaded-backup"); + + let sorted = filesInFolder.sort((a, b) => new Date(b.split("_")[0]).getTime() - new Date(a.split("_")[0]).getTime()); + + res.json(sorted); +} + +/** + * @description download uploaded backup file + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function downloadUploadedBackupFile(req: Request, res: Response): Promise { + let filename = req.params.filename; + + let filepath = FileSystemHelper.formatPath("uploaded-backup", filename); + + res.sendFile(filepath, { + headers: { + "Content-Type": "application/json", + }, + }); +} + /** * @description create backup manually * @param req {Request} Express req object @@ -55,8 +89,25 @@ export async function restoreBackupByLocalFile(req: Request, res: Response): Pro let filename = req.body.filename; let partial = req.body.partial; let include = req.body.include; + let overwrite = req.body.overwrite; - await BackupHelper.loadBackup({ filename, include, partial }); + await BackupHelper.loadBackup({ filename, include, partial, overwrite }); + + res.sendStatus(204); +} + +/** + * @description restore uploaded backup by selected + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function restoreBackupByUploadedFile(req: Request, res: Response): Promise { + let filename = req.body.filename; + let partial = req.body.partial; + let include = req.body.include; + + await BackupHelper.loadBackup({ filename, path: "uploaded-backup", include, partial }); res.sendStatus(204); } diff --git a/src/factory/admin/club/member/membership.ts b/src/factory/admin/club/member/membership.ts index cbc171a..72089ae 100644 --- a/src/factory/admin/club/member/membership.ts +++ b/src/factory/admin/club/member/membership.ts @@ -40,7 +40,7 @@ export default abstract class MembershipFactory { return { durationInDays: record.durationInDays, durationInYears: record.durationInYears, - exactDuration: record.exactDuration, + exactDuration: record.exactDuration.toString(), status: record.status, statusId: record.statusId, memberId: record.memberId, diff --git a/src/helpers/backupHelper.ts b/src/helpers/backupHelper.ts index ee649e7..ff69e71 100644 --- a/src/helpers/backupHelper.ts +++ b/src/helpers/backupHelper.ts @@ -4,6 +4,8 @@ import { EntityManager } from "typeorm"; import uniqBy from "lodash.uniqby"; import InternalException from "../exceptions/internalException"; import UserService from "../service/user/userService"; +import { BACKUP_COPIES, BACKUP_INTERVAL } from "../env.defaults"; +import DatabaseActionException from "../exceptions/databaseActionException"; export type BackupSection = | "member" @@ -97,7 +99,26 @@ export default abstract class BackupHelper { FileSystemHelper.writeFile(path, filename + ".json", JSON.stringify(json, null, 2)); - // TODO: delete older backups by copies env + let files = FileSystemHelper.getFilesInDirectory("backup", ".json"); + let sorted = files.sort((a, b) => new Date(b.split(".")[0]).getTime() - new Date(a.split(".")[0]).getTime()); + + const filesToDelete = sorted.slice(BACKUP_COPIES); + for (const file of filesToDelete) { + FileSystemHelper.deleteFile("backup", file); + } + } + + static async createBackupOnInterval() { + let files = FileSystemHelper.getFilesInDirectory("backup", ".json"); + let newestFile = files.sort((a, b) => new Date(b.split(".")[0]).getTime() - new Date(a.split(".")[0]).getTime())[0]; + + let lastBackup = new Date(newestFile.split(".")[0]); + let diffInMs = new Date().getTime() - lastBackup.getTime(); + let diffInDays = diffInMs / (1000 * 60 * 60 * 24); + + if (diffInDays >= BACKUP_INTERVAL) { + await this.createBackup({}); + } } static async loadBackup({ @@ -105,11 +126,13 @@ export default abstract class BackupHelper { path = "/backup", include = [], partial = false, + overwrite = false, }: { filename: string; path?: string; partial?: boolean; include?: Array; + overwrite?: boolean; }): Promise { this.transactionManager = undefined; @@ -127,10 +150,12 @@ export default abstract class BackupHelper { const sections = this.backupSection .filter((bs) => (partial ? include.includes(bs.type) : true)) .sort((a, b) => a.orderOnClear - b.orderOnClear); - for (const section of sections.filter((s) => Object.keys(backup).includes(s.type))) { - let refered = this.backupSectionRefered[section.type]; - for (const ref of refered) { - await this.transactionManager.getRepository(ref).delete({}); + if (!overwrite) { + for (const section of sections.filter((s) => Object.keys(backup).includes(s.type))) { + let refered = this.backupSectionRefered[section.type]; + for (const ref of refered) { + await this.transactionManager.getRepository(ref).delete({}); + } } } @@ -144,7 +169,7 @@ export default abstract class BackupHelper { }) .catch((err) => { this.transactionManager = undefined; - throw new InternalException("failed to restore backup - rolling back actions", err); + throw new DatabaseActionException("BACKUP RESTORE", include.join(", "), err); }); } diff --git a/src/helpers/fileSystemHelper.ts b/src/helpers/fileSystemHelper.ts index db6b95e..f77bef2 100644 --- a/src/helpers/fileSystemHelper.ts +++ b/src/helpers/fileSystemHelper.ts @@ -11,14 +11,17 @@ export abstract class FileSystemHelper { } static readFile(...filePath: string[]) { + this.createFolder(...filePath); return readFileSync(this.formatPath(...filePath), "utf8"); } static readFileasBase64(...filePath: string[]) { + this.createFolder(...filePath); return readFileSync(this.formatPath(...filePath), "base64"); } static readTemplateFile(filePath: string) { + this.createFolder(filePath); return readFileSync(process.cwd() + filePath, "utf8"); } @@ -28,6 +31,13 @@ export abstract class FileSystemHelper { writeFileSync(path, file); } + static deleteFile(...filePath: string[]) { + const path = this.formatPath(...filePath); + if (existsSync(path)) { + unlinkSync(path); + } + } + static formatPath(...args: string[]) { return join(process.cwd(), "files", ...args); } diff --git a/src/index.ts b/src/index.ts index fe2fc80..5aa758a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -40,5 +40,5 @@ import RefreshCommandHandler from "./command/refreshCommandHandler"; const job = schedule.scheduleJob("0 0 * * *", async () => { console.log(`${new Date().toISOString()}: running Cron`); await RefreshCommandHandler.deleteExpired(); - // TODO: create backup by interval env + await BackupHelper.createBackupOnInterval(); }); diff --git a/src/routes/admin/user/backup.ts b/src/routes/admin/user/backup.ts index d700636..b486a49 100644 --- a/src/routes/admin/user/backup.ts +++ b/src/routes/admin/user/backup.ts @@ -4,17 +4,22 @@ import multer from "multer"; import { createManualBackup, downloadBackupFile, + downloadUploadedBackupFile, getGeneratedBackups, + getUploadedBackups, restoreBackupByLocalFile, + restoreBackupByUploadedFile, uploadBackupFile, } from "../../../controller/admin/user/backupController"; +import { FileSystemHelper } from "../../../helpers/fileSystemHelper"; const storage = multer.diskStorage({ destination: (req, file, cb) => { - cb(null, "files/backup/"); + FileSystemHelper.createFolder("uploaded-backup"); + cb(null, "files/uploaded-backup/"); }, filename: (req, file, cb) => { - cb(null, `${new Date().toISOString().split("T")[0]}-uploaded-${file.originalname}`); + cb(null, `${new Date().toISOString().split("T")[0]}_${file.originalname}`); }, }); @@ -31,14 +36,22 @@ const upload = multer({ var router = express.Router({ mergeParams: true }); -router.get("/", async (req: Request, res: Response) => { +router.get("/generated", async (req: Request, res: Response) => { await getGeneratedBackups(req, res); }); -router.get("/:filename", async (req: Request, res: Response) => { +router.get("/generated/:filename", async (req: Request, res: Response) => { await downloadBackupFile(req, res); }); +router.get("/uploaded", async (req: Request, res: Response) => { + await getUploadedBackups(req, res); +}); + +router.get("/uploaded/:filename", async (req: Request, res: Response) => { + await downloadUploadedBackupFile(req, res); +}); + router.post( "/", PermissionHelper.passCheckMiddleware("create", "user", "backup"), @@ -48,13 +61,21 @@ router.post( ); router.post( - "/restore", + "/generated/restore", PermissionHelper.passCheckMiddleware("admin", "user", "backup"), async (req: Request, res: Response) => { await restoreBackupByLocalFile(req, res); } ); +router.post( + "/uploaded/restore", + PermissionHelper.passCheckMiddleware("admin", "user", "backup"), + async (req: Request, res: Response) => { + await restoreBackupByUploadedFile(req, res); + } +); + router.post( "/upload", PermissionHelper.passCheckMiddleware("create", "user", "backup"), -- 2.45.3