Merge pull request 'minor v1.7.0' (#116) from develop into main

Reviewed-on: #116
This commit is contained in:
Julian Krauser 2025-06-07 13:55:09 +00:00
commit 4dcfe5febc
36 changed files with 762 additions and 1701 deletions

834
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -41,44 +41,41 @@
"moment": "^2.30.1",
"morgan": "^1.10.0",
"ms": "^2.1.3",
"multer": "^1.4.5-lts.2",
"mysql": "^2.18.1",
"multer": "^2.0.1",
"node-schedule": "^2.1.1",
"nodemailer": "^7.0.2",
"nodemailer": "^7.0.3",
"pdf-lib": "^1.17.1",
"pg": "^8.15.6",
"puppeteer": "^24.8.0",
"pg": "^8.16.0",
"puppeteer": "^24.10.0",
"qrcode": "^1.5.4",
"reflect-metadata": "^0.2.2",
"rss-parser": "^3.13.0",
"sharp": "^0.34.1",
"sharp": "^0.34.2",
"sharp-ico": "^0.1.5",
"socket.io": "^4.8.1",
"speakeasy": "^2.0.0",
"sqlite3": "^5.1.7",
"typeorm": "^0.3.22",
"typeorm": "^0.3.24",
"uuid": "^11.1.0",
"validator": "^13.15.0"
"validator": "^13.15.15"
},
"devDependencies": {
"@types/cors": "^2.8.17",
"@types/express": "^5.0.1",
"@types/cors": "^2.8.19",
"@types/express": "^5.0.3",
"@types/ip": "^1.1.3",
"@types/jsonwebtoken": "^9.0.9",
"@types/lodash.clonedeep": "^4.5.9",
"@types/lodash.uniqby": "^4.7.9",
"@types/morgan": "^1.9.9",
"@types/morgan": "^1.9.10",
"@types/ms": "^2.1.0",
"@types/multer": "^1.4.12",
"@types/mysql": "^2.15.27",
"@types/node": "^22.15.12",
"@types/multer": "^1.4.13",
"@types/node": "^22.15.30",
"@types/node-schedule": "^2.1.7",
"@types/nodemailer": "^6.4.17",
"@types/pg": "~8.12.0",
"@types/pg": "~8.15.4",
"@types/qrcode": "~1.5.5",
"@types/speakeasy": "^2.0.10",
"@types/uuid": "^10.0.0",
"@types/validator": "^13.15.0",
"@types/validator": "^13.15.1",
"ts-node": "10.9.2",
"typescript": "^5.8.3"
}

View file

@ -26,7 +26,7 @@ export default abstract class InviteCommandHandler {
lastname: createInvite.lastname,
secret: createInvite.secret,
})
.orUpdate(["firstName", "lastName", "token", "secret"], ["mail"])
.orUpdate(["firstname", "lastname", "token", "secret"], ["mail"])
.execute()
.then((result) => {
return token;

View file

@ -1,7 +1,7 @@
import "dotenv/config";
import "reflect-metadata";
import { DataSource } from "typeorm";
import { configCheck, DB_HOST, DB_NAME, DB_PASSWORD, DB_PORT, DB_TYPE, DB_USERNAME } from "./env.defaults";
import { configCheck, DB_HOST, DB_NAME, DB_PASSWORD, DB_PORT, DB_USERNAME } from "./env.defaults";
import { user } from "./entity/management/user";
import { refresh } from "./entity/refresh";
@ -45,27 +45,16 @@ import { webapi } from "./entity/management/webapi";
import { webapiPermission } from "./entity/management/webapi_permission";
import { salutation } from "./entity/configuration/salutation";
import { setting } from "./entity/management/setting";
import { BackupAndResetDatabase1738166124200 } from "./migrations/1738166124200-BackupAndResetDatabase";
import { CreateSchema1738166167472 } from "./migrations/1738166167472-CreateSchema";
import { TemplatesAndProtocolSort1742549956787 } from "./migrations/1742549956787-templatesAndProtocolSort";
import { QueryToUUID1742922178643 } from "./migrations/1742922178643-queryToUUID";
import { NewsletterColumnType1744351418751 } from "./migrations/1744351418751-newsletterColumnType";
import { QueryUpdatedAt1744795756230 } from "./migrations/1744795756230-QueryUpdatedAt";
import { SettingsFromEnv1745059495807 } from "./migrations/1745059495807-settingsFromEnv";
import { MemberCreatedAt1746006549262 } from "./migrations/1746006549262-memberCreatedAt";
import { UserLoginRoutine1746252454922 } from "./migrations/1746252454922-UserLoginRoutine";
import { SettingsFromEnv_SET1745059495808 } from "./migrations/1745059495808-settingsFromEnv_set";
import { AddDateToNewsletter1748509435932 } from "./migrations/1748509435932-addDateToNewsletter";
import { AddMembershipTotalView1748607842929 } from "./migrations/1748607842929-addMembershipTotalView";
import { education } from "./entity/configuration/education";
import { memberEducations } from "./entity/club/member/memberEducations";
import { MemberExtendData1748953828644 } from "./migrations/1748953828644-memberExtendData";
import { BackupAndResetDatabase1749296262915 } from "./migrations/1749296262915-BackupAndResetDatabase";
import { CreateSchema1749296280721 } from "./migrations/1749296280721-CreateSchema";
configCheck();
const dataSource = new DataSource({
type: DB_TYPE as any,
type: "postgres",
host: DB_HOST,
port: DB_PORT,
username: DB_USERNAME,
@ -73,7 +62,6 @@ const dataSource = new DataSource({
database: DB_NAME,
synchronize: false,
logging: process.env.NODE_ENV ? true : ["schema", "error", "warn", "log", "migration"],
bigNumberStrings: false,
entities: [
user,
refresh,
@ -120,21 +108,7 @@ const dataSource = new DataSource({
webapiPermission,
setting,
],
migrations: [
BackupAndResetDatabase1738166124200,
CreateSchema1738166167472,
TemplatesAndProtocolSort1742549956787,
QueryToUUID1742922178643,
NewsletterColumnType1744351418751,
QueryUpdatedAt1744795756230,
SettingsFromEnv1745059495807,
SettingsFromEnv_SET1745059495808,
MemberCreatedAt1746006549262,
UserLoginRoutine1746252454922,
AddDateToNewsletter1748509435932,
AddMembershipTotalView1748607842929,
MemberExtendData1748953828644,
],
migrations: [BackupAndResetDatabase1749296262915, CreateSchema1749296280721],
migrationsRun: true,
migrationsTransactionMode: "each",
subscribers: [],

View file

@ -45,7 +45,7 @@ export class calendar {
@UpdateDateColumn()
updatedAt: Date;
@Column({ type: "varchar", nullable: true, default: null, unique: true })
@Column({ type: "varchar", length: "255", nullable: true, default: null, unique: true })
webpageId: string;
@ManyToOne(() => calendarType, (t) => t.calendar, {

View file

@ -8,6 +8,7 @@ import {
OneToMany,
OneToOne,
PrimaryColumn,
PrimaryGeneratedColumn,
} from "typeorm";
import { membership } from "./membership";
import { memberAwards } from "./memberAwards";
@ -20,7 +21,7 @@ import { memberEducations } from "./memberEducations";
@Entity()
export class member {
@PrimaryColumn({ generated: "uuid", type: "varchar" })
@PrimaryGeneratedColumn("uuid")
id: string;
@Column({ type: "varchar", length: 255 })

View file

@ -1,8 +1,8 @@
import { Column, Entity, PrimaryColumn, UpdateDateColumn } from "typeorm";
import { Column, Entity, PrimaryColumn, PrimaryGeneratedColumn, UpdateDateColumn } from "typeorm";
@Entity()
export class query {
@PrimaryColumn({ generated: "uuid", type: "varchar" })
@PrimaryGeneratedColumn("uuid")
id: string;
@Column({ type: "varchar", length: 255, unique: true })

View file

@ -1,4 +1,4 @@
import { Column, Entity, JoinTable, ManyToMany, OneToMany, PrimaryColumn } from "typeorm";
import { Column, Entity, JoinTable, ManyToMany, OneToMany, PrimaryColumn, PrimaryGeneratedColumn } from "typeorm";
import { role } from "./role";
import { userPermission } from "./user_permission";
import { LoginRoutineEnum } from "../../enums/loginRoutineEnum";
@ -7,7 +7,7 @@ import { APPLICATION_SECRET } from "../../env.defaults";
@Entity()
export class user {
@PrimaryColumn({ generated: "uuid", type: "varchar" })
@PrimaryGeneratedColumn("uuid")
id: string;
@Column({ type: "varchar", unique: true, length: 255 })

View file

@ -2,9 +2,8 @@ import "dotenv/config";
import ms from "ms";
import ip from "ip";
export const DB_TYPE = process.env.DB_TYPE ?? "mysql";
export const DB_HOST = process.env.DB_HOST ?? "";
export const DB_PORT = Number(process.env.DB_PORT ?? 3306);
export const DB_PORT = Number(process.env.DB_PORT ?? 5432);
export const DB_NAME = process.env.DB_NAME ?? "";
export const DB_USERNAME = process.env.DB_USERNAME ?? "";
export const DB_PASSWORD = process.env.DB_PASSWORD ?? "";
@ -39,7 +38,6 @@ export const TRUST_PROXY = ((): Array<string> | string | boolean | number | null
})();
export function configCheck() {
if (DB_TYPE != "mysql" && DB_TYPE != "postgres") throw new Error("set valid value to DB_TYPE (mysql|postgres)");
if (DB_HOST == "" || typeof DB_HOST != "string") 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");

View file

@ -1,14 +1,5 @@
import { DB_TYPE } from "../../../../env.defaults";
export default abstract class DateMappingHelper {
static mapDate(entry: any) {
switch (DB_TYPE) {
case "postgres":
return `${entry?.years ?? 0} years ${entry?.months ?? 0} months ${entry?.days ?? 0} days`;
case "mysql":
return entry.toString();
case "sqlite":
return entry;
}
return `${entry?.years ?? 0} years ${entry?.months ?? 0} months ${entry?.days ?? 0} days`;
}
}

View file

@ -1,177 +0,0 @@
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/configuration/templateUsage";
import {
award_table,
communication_type_table,
executive_position_table,
member_awards_table,
member_communication_table,
member_executive_positions_table,
member_executive_positions_view_mysql,
member_executive_positions_view_postgres,
member_executive_positions_view_sqlite,
member_qualifications_table,
member_qualifications_view_mysql,
member_qualifications_view_postgres,
member_qualifications_view_sqlite,
member_table,
member_view_mysql,
member_view_postgres,
member_view_sqlite,
membership_status_table,
membership_table,
membership_view_mysql,
membership_view_postgres,
membership_view_sqlite,
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";
import { DB_TYPE } from "../env.defaults";
export class CreateSchema1738166167472 implements MigrationInterface {
name = "CreateSchema1738166167472";
public async up(queryRunner: QueryRunner): Promise<void> {
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);
if (DB_TYPE == "postgres") await queryRunner.createView(member_view_postgres, 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 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 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 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);
await queryRunner.createTable(template_usage_table, true, true, true);
await queryRunner.manager
.createQueryBuilder()
.insert()
.into(templateUsage)
.values([{ scope: "newsletter" }, { scope: "protocol" }, { scope: "member.list" }])
.orIgnore()
.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<void> {
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);
}
}

View file

@ -1,55 +0,0 @@
import { MigrationInterface, QueryRunner, TableColumn } from "typeorm";
import { templateUsage } from "../entity/configuration/templateUsage";
import { getTypeByORM, getDefaultByORM } from "./ormHelper";
export class TemplatesAndProtocolSort1742549956787 implements MigrationInterface {
name = "TemplatesAndProtocolSort1742549956787";
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.manager
.createQueryBuilder()
.insert()
.into(templateUsage)
.values([{ scope: "member" }])
.orIgnore()
.execute();
await queryRunner.manager
.createQueryBuilder()
.delete()
.from(templateUsage)
.where({ scope: "member.list" })
.execute();
await queryRunner.addColumn(
"protocol_agenda",
new TableColumn({ name: "sort", ...getTypeByORM("int"), default: getDefaultByORM("number", 0) })
);
await queryRunner.addColumn(
"protocol_decision",
new TableColumn({ name: "sort", ...getTypeByORM("int"), default: getDefaultByORM("number", 0) })
);
await queryRunner.addColumn(
"protocol_voting",
new TableColumn({ name: "sort", ...getTypeByORM("int"), default: getDefaultByORM("number", 0) })
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.dropColumn("protocol_agenda", "sort");
await queryRunner.dropColumn("protocol_decision", "sort");
await queryRunner.dropColumn("protocol_voting", "sort");
await queryRunner.manager
.createQueryBuilder()
.insert()
.into(templateUsage)
.values([{ scope: "member.list" }])
.orIgnore()
.execute();
await queryRunner.manager.createQueryBuilder().delete().from(templateUsage).where({ scope: "member" }).execute();
}
}

View file

@ -1,94 +0,0 @@
import { MigrationInterface, QueryRunner, TableColumn, TableForeignKey } from "typeorm";
import { getTypeByORM, isIncrementPrimary, isUUIDPrimary } from "./ormHelper";
import { query } from "../entity/configuration/query";
export class QueryToUUID1742922178643 implements MigrationInterface {
name = "QueryToUUID1742922178643";
public async up(queryRunner: QueryRunner): Promise<void> {
const table = await queryRunner.getTable("newsletter");
const foreignKey = table.foreignKeys.find((fk) => fk.columnNames.indexOf("recipientsByQueryId") !== -1);
await queryRunner.dropForeignKey("newsletter", foreignKey);
// const entries = await queryRunner.manager.getRepository("query").find({ select: { title: true, query: true } });
// await queryRunner.clearTable("query");
await queryRunner.dropColumn("newsletter", "recipientsByQueryId");
await queryRunner.dropColumn("query", "id");
await queryRunner.addColumn(
"query",
new TableColumn({
name: "id",
...getTypeByORM("uuid"),
...isUUIDPrimary,
})
);
await queryRunner.addColumn(
"newsletter",
new TableColumn({
name: "recipientsByQueryId",
...getTypeByORM("uuid", true),
})
);
// await queryRunner.manager.getRepository("query").save(entries);
await queryRunner.createForeignKey(
"newsletter",
new TableForeignKey({
columnNames: ["recipientsByQueryId"],
referencedColumnNames: ["id"],
referencedTableName: "query",
onDelete: "CASCADE",
onUpdate: "RESTRICT",
})
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
const table = await queryRunner.getTable("newsletter");
const foreignKey = table.foreignKeys.find((fk) => fk.columnNames.indexOf("recipientsByQueryId") !== -1);
await queryRunner.dropForeignKey("newsletter", foreignKey);
const entries = await queryRunner.manager.getRepository(query).find({ select: { title: true, query: true } });
await queryRunner.clearTable("query");
await queryRunner.dropColumn("newsletter", "recipientsByQueryId");
await queryRunner.dropColumn("query", "id");
await queryRunner.addColumn(
"query",
new TableColumn({
name: "id",
...getTypeByORM("int"),
...isIncrementPrimary,
})
);
await queryRunner.addColumn(
"newsletter",
new TableColumn({
name: "recipientsByQueryId",
...getTypeByORM("int", true),
})
);
await queryRunner.manager
.createQueryBuilder()
.insert()
.into("query")
.values(entries.map((e, i) => ({ ...e, id: i + 1 })))
.execute();
await queryRunner.createForeignKey(
"newsletter",
new TableForeignKey({
columnNames: ["recipientsByQueryId"],
referencedColumnNames: ["id"],
referencedTableName: "query",
onDelete: "CASCADE",
onUpdate: "RESTRICT",
})
);
}
}

View file

@ -1,47 +0,0 @@
import { MigrationInterface, QueryRunner, TableColumn } from "typeorm";
import { getDefaultByORM, getTypeByORM } from "./ormHelper";
import { newsletter } from "../entity/club/newsletter/newsletter";
export class NewsletterColumnType1744351418751 implements MigrationInterface {
name = "NewsletterColumnType1744351418751";
public async up(queryRunner: QueryRunner): Promise<void> {
let newsletters = await queryRunner.manager
.getRepository("newsletter")
.find({ select: { id: true, newsletterTitle: true, newsletterSignatur: true } });
await queryRunner.dropColumn("newsletter", "newsletterTitle");
await queryRunner.dropColumn("newsletter", "newsletterSignatur");
await queryRunner.addColumn(
"newsletter",
new TableColumn({ name: "newsletterTitle", ...getTypeByORM("text"), default: getDefaultByORM("string") })
);
await queryRunner.addColumn(
"newsletter",
new TableColumn({ name: "newsletterSignatur", ...getTypeByORM("text"), default: getDefaultByORM("string") })
);
await queryRunner.manager.getRepository("newsletter").save(newsletters);
}
public async down(queryRunner: QueryRunner): Promise<void> {
let newsletters = await queryRunner.manager
.getRepository("newsletter")
.find({ select: { id: true, newsletterTitle: true, newsletterSignatur: true } });
await queryRunner.dropColumn("newsletter", "newsletterTitle");
await queryRunner.dropColumn("newsletter", "newsletterSignatur");
await queryRunner.addColumn(
"newsletter",
new TableColumn({ name: "newsletterTitle", ...getTypeByORM("varchar"), default: getDefaultByORM("string") })
);
await queryRunner.addColumn(
"newsletter",
new TableColumn({ name: "newsletterSignatur", ...getTypeByORM("varchar"), default: getDefaultByORM("string") })
);
await queryRunner.manager.getRepository("newsletter").save(newsletters);
}
}

View file

@ -1,22 +0,0 @@
import { MigrationInterface, QueryRunner, TableColumn } from "typeorm";
import { getTypeByORM, getDefaultByORM } from "./ormHelper";
export class QueryUpdatedAt1744795756230 implements MigrationInterface {
name = "QueryUpdatedAt1744795756230";
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.addColumn(
"query",
new TableColumn({
name: "updatedAt",
...getTypeByORM("datetime", false, 6),
default: getDefaultByORM("currentTimestamp", 6),
onUpdate: getDefaultByORM<string>("currentTimestamp", 6),
})
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.dropColumn("query", "updatedAt");
}
}

View file

@ -1,16 +0,0 @@
import { MigrationInterface, QueryRunner } from "typeorm";
import { setting_table } from "./baseSchemaTables/admin";
import SettingHelper from "../helpers/settingsHelper";
import ms from "ms";
export class SettingsFromEnv1745059495807 implements MigrationInterface {
name = "SettingsFromEnv1745059495807";
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.createTable(setting_table, true, true, true);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.dropTable(setting_table.name, true, true, true);
}
}

View file

@ -1,29 +0,0 @@
import { MigrationInterface, QueryRunner } from "typeorm";
import { setting_table } from "./baseSchemaTables/admin";
import SettingHelper from "../helpers/settingsHelper";
import ms from "ms";
export class SettingsFromEnv_SET1745059495808 implements MigrationInterface {
name = "SettingsFromEnv_SET1745059495808";
public async up(queryRunner: QueryRunner): Promise<void> {
// transfer settings of env to database
await SettingHelper.setSetting("club.name", process.env.CLUB_NAME);
await SettingHelper.setSetting("club.website", process.env.CLUB_WEBSITE);
await SettingHelper.setSetting("session.jwt_expiration", process.env.JWT_EXPIRATION as ms.StringValue);
await SettingHelper.setSetting("session.refresh_expiration", process.env.REFRESH_EXPIRATION as ms.StringValue);
await SettingHelper.setSetting(
"session.pwa_refresh_expiration",
process.env.PWA_REFRESH_EXPIRATION as ms.StringValue
);
await SettingHelper.setSetting("mail.username", process.env.MAIL_USERNAME);
await SettingHelper.setSetting("mail.password", process.env.MAIL_PASSWORD);
await SettingHelper.setSetting("mail.host", process.env.MAIL_HOST);
await SettingHelper.setSetting("mail.port", Number(process.env.MAIL_PORT ?? "578"));
await SettingHelper.setSetting("mail.secure", process.env.MAIL_SECURE == "true");
await SettingHelper.setSetting("backup.interval", Number(process.env.BACKUP_INTERVAL ?? "1"));
await SettingHelper.setSetting("backup.copies", Number(process.env.BACKUP_COPIES ?? "7"));
}
public async down(queryRunner: QueryRunner): Promise<void> {}
}

View file

@ -1,21 +0,0 @@
import { MigrationInterface, QueryRunner, TableColumn } from "typeorm";
import { getTypeByORM, getDefaultByORM } from "./ormHelper";
export class MemberCreatedAt1746006549262 implements MigrationInterface {
name = "MemberCreatedAt1746006549262";
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.addColumn(
"member",
new TableColumn({
name: "createdAt",
...getTypeByORM("datetime", false, 6),
default: getDefaultByORM("currentTimestamp", 6),
})
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.dropColumn("member", "createdAt");
}
}

View file

@ -1,39 +0,0 @@
import { MigrationInterface, QueryRunner, TableColumn } from "typeorm";
import { getDefaultByORM, getTypeByORM } from "./ormHelper";
import { LoginRoutineEnum } from "../enums/loginRoutineEnum";
import { CodingHelper } from "../helpers/codingHelper";
import { APPLICATION_SECRET } from "../env.defaults";
export class UserLoginRoutine1746252454922 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
let users = await queryRunner.manager.getRepository("user").find({ select: ["id", "secret"] });
await queryRunner.dropColumns("user", ["secret", "static"]);
await queryRunner.addColumns("user", [
new TableColumn({ name: "secret", ...getTypeByORM("text"), default: getDefaultByORM("string") }),
new TableColumn({
name: "routine",
...getTypeByORM("varchar"),
default: getDefaultByORM("string", LoginRoutineEnum.totp),
}),
]);
await queryRunner.manager.getRepository("user").save(users.map((u) => ({ id: u.id, secret: u.secret })));
}
public async down(queryRunner: QueryRunner): Promise<void> {
let users = await queryRunner.manager.getRepository("user").find({ select: ["id", "secret"] });
await queryRunner.dropColumn("user", "secret");
await queryRunner.addColumns("user", [
new TableColumn({ name: "secret", ...getTypeByORM("varchar"), default: getDefaultByORM("string") }),
new TableColumn({ name: "static", ...getTypeByORM("boolean"), default: getDefaultByORM("boolean", false) }),
]);
await queryRunner.manager.getRepository("user").save(users.map((u) => ({ id: u.id, secret: u.secret })));
await queryRunner.dropColumn("user", "routine");
}
}

View file

@ -1,21 +0,0 @@
import { MigrationInterface, QueryRunner, TableColumn } from "typeorm";
import { getTypeByORM, getDefaultByORM } from "./ormHelper";
export class AddDateToNewsletter1748509435932 implements MigrationInterface {
name = "AddDateToNewsletter1748509435932";
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.addColumn(
"newsletter",
new TableColumn({
name: "createdAt",
...getTypeByORM("datetime", false, 6),
default: getDefaultByORM("currentTimestamp", 6),
})
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.dropColumn("newsletter", "createdAt");
}
}

View file

@ -1,21 +0,0 @@
import { MigrationInterface, QueryRunner } from "typeorm";
import { DB_TYPE } from "../env.defaults";
import {
membership_total_view_mysql,
membership_total_view_postgres,
membership_total_view_sqlite,
} from "./baseSchemaTables/member";
export class AddMembershipTotalView1748607842929 implements MigrationInterface {
name = "AddMembershipTotalView1748607842929";
public async up(queryRunner: QueryRunner): Promise<void> {
if (DB_TYPE == "postgres") await queryRunner.createView(membership_total_view_postgres, true);
else if (DB_TYPE == "mysql") await queryRunner.createView(membership_total_view_mysql, true);
else if (DB_TYPE == "sqlite") await queryRunner.createView(membership_total_view_sqlite, true);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.dropView("membership_total_view");
}
}

View file

@ -1,34 +0,0 @@
import { MigrationInterface, QueryRunner, TableColumn } from "typeorm";
import {
education_table,
member_educations_table,
member_view2_mysql,
member_view2_postgres,
member_view2_sqlite,
} from "./baseSchemaTables/member";
import { getDefaultByORM, getTypeByORM } from "./ormHelper";
import { DB_TYPE } from "../env.defaults";
export class MemberExtendData1748953828644 implements MigrationInterface {
name = "MemberExtendData1748953828644";
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.createTable(education_table, true, true, true);
await queryRunner.createTable(member_educations_table, true, true, true);
await queryRunner.addColumn(
"member",
new TableColumn({ name: "note", ...getTypeByORM("varchar", true), default: getDefaultByORM("null") })
);
await queryRunner.dropView("member_view");
if (DB_TYPE == "postgres") await queryRunner.createView(member_view2_postgres, true);
else if (DB_TYPE == "mysql") await queryRunner.createView(member_view2_mysql, true);
else if (DB_TYPE == "sqlite") await queryRunner.createView(member_view2_sqlite, true);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.dropTable(member_educations_table, true, true, true);
await queryRunner.dropTable(education_table, true, true, true);
await queryRunner.dropColumn("member", "note");
}
}

View file

@ -1,21 +1,19 @@
import { MigrationInterface, QueryRunner, Table } from "typeorm";
import BackupHelper from "../helpers/backupHelper";
import { getDefaultByORM, getTypeByORM, isIncrementPrimary } from "./ormHelper";
import InternalException from "../exceptions/internalException";
import { DB_TYPE } from "../env.defaults";
import BackupHelper from "../helpers/backupHelper";
import { getTypeByORM, isIncrementPrimary, getDefaultByORM } from "./ormHelper";
export class BackupAndResetDatabase1738166124200 implements MigrationInterface {
name = "BackupAndResetDatabase1738166124200";
export class BackupAndResetDatabase1749296262915 implements MigrationInterface {
name = "BackupAndResetDatabase1749296262915";
public async up(queryRunner: QueryRunner): Promise<void> {
let query = DB_TYPE == "postgres" ? "SELECT name FROM migrations" : "SELECT `name` FROM `migrations`";
let migrations = await queryRunner.query(query);
let migrations = await queryRunner.query("SELECT name FROM migrations");
if (
(await queryRunner.hasTable("user")) &&
migrations.findIndex((m: any) => m.name == "MoveSendNewsletterFlag1737816852011") == -1
migrations.findIndex((m: any) => m.name == "MemberExtendData1748953828644") == -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."
"Cannot update due to skiped version. Update to v1.6.0 Version first to prevent data loss and get access to the newer Versions."
);
}

View file

@ -0,0 +1,176 @@
import { MigrationInterface, QueryRunner } from "typeorm";
import {
reset_table,
invite_table,
role_table,
role_permission_table,
user_table,
user_roles_table,
user_permission_table,
refresh_table,
webapi_table,
webapi_permission_table,
setting_table,
} from "./baseSchemaTables/admin";
import { calendar_type_table, calendar_table } from "./baseSchemaTables/calendar";
import {
salutation_table,
award_table,
communication_type_table,
membership_status_table,
executive_position_table,
qualification_table,
member_table,
member_awards_table,
member_communication_table,
membership_table,
member_executive_positions_table,
member_qualifications_table,
member_view,
membership_view,
member_qualifications_view,
member_executive_positions_view,
education_table,
member_educations_table,
membership_total_view,
} from "./baseSchemaTables/member";
import {
newsletter_config_table,
newsletter_table,
newsletter_dates_table,
newsletter_recipients_table,
} from "./baseSchemaTables/newsletter";
import {
protocol_table,
protocol_agenda_table,
protocol_decision_table,
protocol_presence_table,
protocol_voting_table,
protocol_printout_table,
} from "./baseSchemaTables/protocol";
import { query_table, template_table, template_usage_table } from "./baseSchemaTables/query_template";
import { availableTemplates } from "../type/templateTypes";
export class CreateSchema1749296280721 implements MigrationInterface {
name = "CreateSchema1749296280721";
public async up(queryRunner: QueryRunner): Promise<void> {
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(setting_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(education_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.createTable(member_educations_table, true, true, true);
await queryRunner.createView(member_view, true);
await queryRunner.createView(membership_view, true);
await queryRunner.createView(membership_total_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(template_usage_table.name)
.values(
availableTemplates.map((at) => ({
scope: at,
}))
)
.orIgnore()
.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<void> {
await queryRunner.dropTable(newsletter_dates_table, true, true, true);
await queryRunner.dropTable(newsletter_recipients_table, true, true, true);
await queryRunner.dropTable(newsletter_table, true, true, true);
await queryRunner.dropTable(newsletter_config_table, true, true, true);
await queryRunner.dropTable(calendar_table, true, true, true);
await queryRunner.dropTable(calendar_type_table, true, true, true);
await queryRunner.dropTable(protocol_agenda_table, true, true, true);
await queryRunner.dropTable(protocol_decision_table, true, true, true);
await queryRunner.dropTable(protocol_presence_table, true, true, true);
await queryRunner.dropTable(protocol_voting_table, true, true, true);
await queryRunner.dropTable(protocol_printout_table, true, true, true);
await queryRunner.dropTable(protocol_table, true, true, true);
await queryRunner.dropTable(template_usage_table, true, true, true);
await queryRunner.dropTable(template_table, true, true, true);
await queryRunner.dropTable(query_table, true, true, true);
await queryRunner.dropView(member_view);
await queryRunner.dropView(membership_view);
await queryRunner.dropView(membership_total_view);
await queryRunner.dropView(member_qualifications_view);
await queryRunner.dropView(member_executive_positions_view);
await queryRunner.dropTable(member_awards_table, true, true, true);
await queryRunner.dropTable(member_communication_table, true, true, true);
await queryRunner.dropTable(membership_table, true, true, true);
await queryRunner.dropTable(member_executive_positions_table, true, true, true);
await queryRunner.dropTable(member_qualifications_table, true, true, true);
await queryRunner.dropTable(member_educations_table, true, true, true);
await queryRunner.dropTable(member_table, true, true, true);
await queryRunner.dropTable(salutation_table, true, true, true);
await queryRunner.dropTable(award_table, true, true, true);
await queryRunner.dropTable(communication_type_table, true, true, true);
await queryRunner.dropTable(membership_status_table, true, true, true);
await queryRunner.dropTable(executive_position_table, true, true, true);
await queryRunner.dropTable(qualification_table, true, true, true);
await queryRunner.dropTable(education_table, true, true, true);
await queryRunner.dropTable(setting_table, true, true, true);
await queryRunner.dropTable(webapi_permission_table, true, true, true);
await queryRunner.dropTable(webapi_table, true, true, true);
await queryRunner.dropTable(refresh_table, true, true, true);
await queryRunner.dropTable(user_permission_table, true, true, true);
await queryRunner.dropTable(user_roles_table, true, true, true);
await queryRunner.dropTable(user_table, true, true, true);
await queryRunner.dropTable(role_permission_table, true, true, true);
await queryRunner.dropTable(role_table, true, true, true);
await queryRunner.dropTable(invite_table, true, true, true);
await queryRunner.dropTable(reset_table, true, true, true);
}
}

View file

@ -1,5 +1,6 @@
import { Table, TableForeignKey } from "typeorm";
import { Table, TableForeignKey, TableIndex, TableUnique } from "typeorm";
import { getDefaultByORM, getTypeByORM, isIncrementPrimary, isUUIDPrimary } from "../ormHelper";
import { LoginRoutineEnum } from "../../enums/loginRoutineEnum";
export const invite_table = new Table({
name: "invite",
@ -16,7 +17,12 @@ export const role_table = new Table({
name: "role",
columns: [
{ name: "id", ...getTypeByORM("int"), ...isIncrementPrimary },
{ name: "role", ...getTypeByORM("varchar"), isUnique: true },
{ name: "role", ...getTypeByORM("varchar") },
],
uniques: [
new TableUnique({
columnNames: ["role"],
}),
],
});
@ -45,8 +51,8 @@ export const user_table = new Table({
{ 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: "secret", ...getTypeByORM("text") },
{ name: "routine", ...getTypeByORM("varchar"), default: getDefaultByORM("string", LoginRoutineEnum.totp) },
{ name: "isOwner", ...getTypeByORM("boolean"), default: getDefaultByORM("boolean", false) },
],
});
@ -73,6 +79,14 @@ export const user_roles_table = new Table({
onUpdate: "RESTRICT",
}),
],
indices: [
new TableIndex({
columnNames: ["userId"],
}),
new TableIndex({
columnNames: ["roleId"],
}),
],
});
export const user_permission_table = new Table({
@ -114,11 +128,19 @@ export const webapi_table = new Table({
name: "webapi",
columns: [
{ name: "id", ...getTypeByORM("int"), ...isIncrementPrimary },
{ name: "token", ...getTypeByORM("varchar"), isUnique: true },
{ name: "title", ...getTypeByORM("varchar"), isUnique: true },
{ name: "token", ...getTypeByORM("text") },
{ name: "title", ...getTypeByORM("varchar") },
{ 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") },
{ name: "lastUsage", ...getTypeByORM("datetime", true, 6) },
{ name: "expiry", ...getTypeByORM("date", true) },
],
uniques: [
new TableUnique({
columnNames: ["token"],
}),
new TableUnique({
columnNames: ["title"],
}),
],
});

View file

@ -1,4 +1,4 @@
import { Table, TableForeignKey } from "typeorm";
import { Table, TableForeignKey, TableUnique } from "typeorm";
import { getDefaultByORM, getTypeByORM, isIncrementPrimary, isUUIDPrimary } from "../ormHelper";
export const calendar_type_table = new Table({
@ -32,7 +32,7 @@ export const calendar_table = new Table({
default: getDefaultByORM("currentTimestamp", 6),
onUpdate: getDefaultByORM<string>("currentTimestamp", 6),
},
{ name: "webpageId", ...getTypeByORM("varchar", true), default: getDefaultByORM("null"), isUnique: true },
{ name: "webpageId", ...getTypeByORM("varchar", true) },
{ name: "typeId", ...getTypeByORM("int") },
],
foreignKeys: [
@ -44,4 +44,9 @@ export const calendar_table = new Table({
onUpdate: "RESTRICT",
}),
],
uniques: [
new TableUnique({
columnNames: ["webpageId"],
}),
],
});

View file

@ -1,4 +1,4 @@
import { Table, TableForeignKey, View } from "typeorm";
import { Table, TableForeignKey, TableUnique, View } from "typeorm";
import { getDefaultByORM, getTypeByORM, isIncrementPrimary, isUUIDPrimary } from "../ormHelper";
export const salutation_table = new Table({
@ -66,11 +66,13 @@ export const member_table = new Table({
columns: [
{ name: "id", ...getTypeByORM("uuid"), ...isUUIDPrimary },
{ name: "salutationId", ...getTypeByORM("int") },
{ name: "internalId", ...getTypeByORM("varchar", true), default: getDefaultByORM("null"), isUnique: true },
{ name: "internalId", ...getTypeByORM("varchar", true) },
{ name: "firstname", ...getTypeByORM("varchar") },
{ name: "lastname", ...getTypeByORM("varchar") },
{ name: "nameaffix", ...getTypeByORM("varchar") },
{ name: "birthdate", ...getTypeByORM("date") },
{ name: "createdAt", ...getTypeByORM("datetime", false, 6), default: getDefaultByORM("currentTimestamp", 6) },
{ name: "note", ...getTypeByORM("varchar", true) },
],
foreignKeys: [
new TableForeignKey({
@ -81,6 +83,11 @@ export const member_table = new Table({
onUpdate: "RESTRICT",
}),
],
uniques: [
new TableUnique({
columnNames: ["internalId"],
}),
],
});
export const membership_table = new Table({
@ -172,7 +179,7 @@ export const member_awards_table = new Table({
name: "member_awards",
columns: [
{ name: "id", ...getTypeByORM("int"), ...isIncrementPrimary },
{ name: "given", ...getTypeByORM("boolean"), default: getDefaultByORM("boolean", false) },
{ name: "given", ...getTypeByORM("boolean"), default: getDefaultByORM("boolean", true) },
{ name: "note", ...getTypeByORM("varchar"), isNullable: true },
{ name: "date", ...getTypeByORM("date") },
{ name: "memberId", ...getTypeByORM("uuid") },
@ -203,13 +210,13 @@ export const member_communication_table = new Table({
{ 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: "mobile", ...getTypeByORM("varchar", true) },
{ name: "email", ...getTypeByORM("varchar", true) },
{ name: "postalCode", ...getTypeByORM("varchar", true) },
{ name: "city", ...getTypeByORM("varchar", true) },
{ name: "street", ...getTypeByORM("varchar", true) },
{ name: "streetNumber", ...getTypeByORM("int", true) },
{ name: "streetNumberAddition", ...getTypeByORM("varchar"), isNullable: true },
{ name: "streetNumberAddition", ...getTypeByORM("varchar", true) },
{ name: "memberId", ...getTypeByORM("uuid") },
{ name: "typeId", ...getTypeByORM("int") },
],
@ -261,180 +268,30 @@ export const member_educations_table = new Table({
});
/** views */
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\`,
\`member\`.\`birthdate\` AS \`birthdate\`,
\`salutation\`.\`salutation\` AS \`salutation\`,
TIMESTAMPDIFF(YEAR, \`member\`.\`birthdate\`, CURDATE()) AS \`todayAge\`,
YEAR(CURDATE()) - YEAR(\`member\`.\`birthdate\`) AS \`ageThisYear\`,
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\`
`,
});
export const member_view_postgres = new View({
export const member_view = 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",
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_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_view2_mysql = new View({
name: "member_view",
expression: `
SELECT
\`member\`.\`id\` AS \`id\`,
\`member\`.\`internalId\` AS \`internalId\`,
\`member\`.\`note\` AS \`note\`,
\`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(
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\`
`,
});
export const member_view2_postgres = new View({
name: "member_view",
expression: `
SELECT
"member"."id" AS "id",
"member"."internalId" AS "internalId",
"member"."note" AS "note",
"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"
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"
`,
`
.replace(/\s+/g, " ")
.trim(),
});
export const member_view2_sqlite = new View({
name: "member_view",
expression: `
SELECT
member.id AS id,
member.internalId AS internalId,
member.note AS note,
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
\`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(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\`
LEFT JOIN \`salutation\` \`salutation\` ON \`salutation\`.\`id\`=\`member\`.\`salutationId\`
GROUP BY \`executivePosition\`.\`id\`, \`member\`.\`id\`, \`salutation\`.\`id\`
`,
});
export const member_executive_positions_view_postgres = new View({
export const member_executive_positions_view = new View({
name: "member_executive_positions_view",
expression: `
SELECT
@ -454,66 +311,12 @@ export const member_executive_positions_view_postgres = new View({
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"
`,
`
.replace(/\s+/g, " ")
.trim(),
});
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
\`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(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\`
LEFT JOIN \`salutation\` \`salutation\` ON \`salutation\`.\`id\`=\`member\`.\`salutationId\`
GROUP BY \`qualification\`.\`id\`, \`member\`.\`id\`, \`salutation\`.\`id\`
`,
});
export const member_qualifications_view_postgres = new View({
export const member_qualifications_view = new View({
name: "member_qualifications_view",
expression: `
SELECT
@ -533,66 +336,12 @@ export const member_qualifications_view_postgres = new View({
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"
`,
`
.replace(/\s+/g, " ")
.trim(),
});
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
\`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(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\`
LEFT JOIN \`salutation\` \`salutation\` ON \`salutation\`.\`id\`=\`member\`.\`salutationId\`
GROUP BY \`status\`.\`id\`, \`member\`.\`id\`, \`salutation\`.\`id\`
`,
});
export const membership_view_postgres = new View({
export const membership_view = new View({
name: "membership_view",
expression: `
SELECT
@ -611,65 +360,13 @@ export const membership_view_postgres = new View({
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"
`,
GROUP BY "status"."id", "member"."id", "salutation"."id"
`
.replace(/\s+/g, " ")
.trim(),
});
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
`,
});
export const membership_total_view_mysql = new View({
name: "membership_total_view",
expression: `
SELECT
\`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(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\`
LEFT JOIN \`salutation\` \`salutation\` ON \`salutation\`.\`id\`=\`member\`.\`salutationId\`
GROUP BY \`member\`.\`id\`, \`salutation\`.\`id\`
`,
});
export const membership_total_view_postgres = new View({
export const membership_total_view = new View({
name: "membership_total_view",
expression: `
SELECT
@ -687,29 +384,7 @@ export const membership_total_view_postgres = new View({
LEFT JOIN "member" "member" ON "member"."id"="membership"."memberId"
LEFT JOIN "salutation" "salutation" ON "salutation"."id"="member"."salutationId"
GROUP BY "member"."id", "salutation"."id"
`,
});
export const membership_total_view_sqlite = new View({
name: "membership_total_view",
expression: `
SELECT
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 member.id, salutation.id
`,
`
.replace(/\s+/g, " ")
.trim(),
});

View file

@ -1,4 +1,4 @@
import { Table, TableForeignKey } from "typeorm";
import { Table, TableForeignKey, TableUnique } from "typeorm";
import { getDefaultByORM, getTypeByORM, isIncrementPrimary } from "../ormHelper";
export const newsletter_table = new Table({
@ -7,11 +7,12 @@ export const newsletter_table = new Table({
{ name: "id", ...getTypeByORM("int"), ...isIncrementPrimary },
{ name: "title", ...getTypeByORM("varchar") },
{ name: "description", ...getTypeByORM("varchar"), default: getDefaultByORM("string") },
{ name: "newsletterTitle", ...getTypeByORM("varchar"), default: getDefaultByORM("string") },
{ name: "newsletterTitle", ...getTypeByORM("text"), default: getDefaultByORM("string") },
{ name: "newsletterText", ...getTypeByORM("text"), default: getDefaultByORM("string") },
{ name: "newsletterSignatur", ...getTypeByORM("varchar"), default: getDefaultByORM("string") },
{ name: "newsletterSignatur", ...getTypeByORM("text"), default: getDefaultByORM("string") },
{ name: "isSent", ...getTypeByORM("boolean"), default: getDefaultByORM("boolean", false) },
{ name: "recipientsByQueryId", ...getTypeByORM("int", true) },
{ name: "recipientsByQueryId", ...getTypeByORM("uuid", true) },
{ name: "createdAt", ...getTypeByORM("datetime", false, 6), default: getDefaultByORM("currentTimestamp", 6) },
],
foreignKeys: [
new TableForeignKey({
@ -22,6 +23,11 @@ export const newsletter_table = new Table({
onUpdate: "RESTRICT",
}),
],
uniques: [
new TableUnique({
columnNames: ["title"],
}),
],
});
export const newsletter_dates_table = new Table({

View file

@ -1,11 +1,11 @@
import { Table, TableForeignKey } from "typeorm";
import { Table, TableForeignKey, TableUnique } from "typeorm";
import { getDefaultByORM, getTypeByORM, isIncrementPrimary } from "../ormHelper";
export const protocol_table = new Table({
name: "protocol",
columns: [
{ name: "id", ...getTypeByORM("int"), ...isIncrementPrimary },
{ name: "title", ...getTypeByORM("varchar") },
{ name: "title", ...getTypeByORM("varchar"), isUnique: true },
{ name: "date", ...getTypeByORM("date") },
{ name: "starttime", ...getTypeByORM("time", true) },
{ name: "endtime", ...getTypeByORM("time", true) },
@ -19,6 +19,7 @@ export const protocol_agenda_table = new Table({
{ name: "id", ...getTypeByORM("int"), ...isIncrementPrimary },
{ name: "topic", ...getTypeByORM("varchar") },
{ name: "context", ...getTypeByORM("text"), default: getDefaultByORM("string") },
{ name: "sort", ...getTypeByORM("int"), default: getDefaultByORM("number", 0) },
{ name: "protocolId", ...getTypeByORM("int") },
],
foreignKeys: [
@ -38,6 +39,7 @@ export const protocol_decision_table = new Table({
{ name: "id", ...getTypeByORM("int"), ...isIncrementPrimary },
{ name: "topic", ...getTypeByORM("varchar") },
{ name: "context", ...getTypeByORM("text"), default: getDefaultByORM("string") },
{ name: "sort", ...getTypeByORM("int"), default: getDefaultByORM("number", 0) },
{ name: "protocolId", ...getTypeByORM("int") },
],
foreignKeys: [
@ -86,6 +88,7 @@ export const protocol_voting_table = new Table({
{ 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: "sort", ...getTypeByORM("int"), default: getDefaultByORM("number", 0) },
{ name: "protocolId", ...getTypeByORM("int") },
],
foreignKeys: [

View file

@ -1,12 +1,18 @@
import { Table, TableForeignKey } from "typeorm";
import { getDefaultByORM, getTypeByORM, isIncrementPrimary } from "../ormHelper";
import { getDefaultByORM, getTypeByORM, isIncrementPrimary, isUUIDPrimary } from "../ormHelper";
export const query_table = new Table({
name: "query",
columns: [
{ name: "id", ...getTypeByORM("int"), ...isIncrementPrimary },
{ name: "id", ...getTypeByORM("uuid"), ...isUUIDPrimary },
{ name: "title", ...getTypeByORM("varchar"), isUnique: true },
{ name: "query", ...getTypeByORM("text"), default: getDefaultByORM("string") },
{
name: "updatedAt",
...getTypeByORM("datetime", false, 6),
default: getDefaultByORM("currentTimestamp", 6),
onUpdate: getDefaultByORM<string>("currentTimestamp", 6),
},
],
});

View file

@ -1,5 +1,3 @@
import { DB_TYPE } from "../env.defaults";
export type ORMType = "int" | "bigint" | "boolean" | "date" | "datetime" | "time" | "text" | "varchar" | "uuid";
export type ORMDefault = "currentTimestamp" | "string" | "boolean" | "number" | "null";
export type ColumnConfig = {
@ -15,83 +13,38 @@ export type Primary = {
};
export function getTypeByORM(type: ORMType, nullable: boolean = false, length: number = 255): ColumnConfig {
const dbType = DB_TYPE;
const typeMap: Record<string, Record<ORMType, string>> = {
mysql: {
int: "int",
bigint: "bigint",
boolean: "tinyint",
date: "date",
datetime: "datetime",
time: "time",
text: "text",
varchar: "varchar",
uuid: "varchar",
},
postgres: {
int: "integer",
bigint: "bigint",
boolean: "boolean",
date: "date",
datetime: "timestamp",
time: "time",
text: "text",
varchar: "character varying",
uuid: "uuid",
},
sqlite: {
int: "integer",
bigint: "integer",
boolean: "integer",
date: "date",
datetime: "datetime",
time: "text",
text: "text",
varchar: "varchar",
uuid: "varchar",
},
const typeMap: Record<ORMType, string> = {
int: "integer",
bigint: "bigint",
boolean: "boolean",
date: "date",
datetime: "timestamp",
time: "time",
text: "text",
varchar: "character varying",
uuid: "uuid",
};
let obj: ColumnConfig = {
type: typeMap[dbType]?.[type] || type,
type: typeMap[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";
else if (obj.type == "varchar" || type == "varchar") obj.length = `${length}`;
return obj;
}
export function getDefaultByORM<T = string | null>(type: ORMDefault, data?: string | number | boolean): T {
const dbType = DB_TYPE;
const typeMap: Record<string, Record<ORMDefault, string | null>> = {
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,
},
const typeMap: Record<ORMDefault, string | null> = {
currentTimestamp: `now()`,
string: `'${data ?? ""}'`,
boolean: Boolean(data) == true ? "true" : "false",
number: Number(data).toString(),
null: null,
};
return (typeMap[dbType]?.[type] || type) as T;
return (typeMap[type] || type) as T;
}
export const isIncrementPrimary: Primary = {

View file

@ -3,7 +3,6 @@ import { dataSource } from "../../../data-source";
import { member } from "../../../entity/club/member/member";
import DatabaseActionException from "../../../exceptions/databaseActionException";
import { memberView } from "../../../views/memberView";
import { DB_TYPE } from "../../../env.defaults";
export default abstract class MemberService {
/**
@ -194,17 +193,13 @@ export default abstract class MemberService {
"member.firstMembershipEntry",
"member.memberships",
"membership_first",
DB_TYPE == "postgres"
? 'membership_first.memberId = member.id AND membership_first.start = (SELECT MIN("m_first"."start") FROM "membership" "m_first" WHERE "m_first"."memberId" = "member"."id")'
: "membership_first.memberId = member.id AND membership_first.start = (SELECT MIN(m_first.start) FROM membership m_first WHERE m_first.memberId = member.id)"
'membership_first.memberId = member.id AND membership_first.start = (SELECT MIN("m_first"."start") FROM "membership" "m_first" WHERE "m_first"."memberId" = "member"."id")'
)
.leftJoinAndMapOne(
"member.lastMembershipEntry",
"member.memberships",
"membership_last",
DB_TYPE == "postgres"
? 'membership_last.memberId = member.id AND membership_last.start = (SELECT MAX("m_last"."start") FROM "membership" "m_last" WHERE "m_last"."memberId" = "member"."id")'
: "membership_last.memberId = member.id AND membership_last.start = (SELECT MAX(m_last.start) FROM membership m_last WHERE m_last.memberId = member.id)"
'membership_last.memberId = member.id AND membership_last.start = (SELECT MAX("m_last"."start") FROM "membership" "m_last" WHERE "m_last"."memberId" = "member"."id")'
)
.leftJoinAndSelect("membership_first.status", "status_first")
.leftJoinAndSelect("membership_last.status", "status_last")

View file

@ -1,36 +1,5 @@
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 AGE(COALESCE("memberExecutivePositions"."end", CURRENT_DATE), "memberExecutivePositions"."start")))`;
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) =>
@ -45,9 +14,18 @@ if (DB_TYPE == "postgres") {
.addSelect("member.nameaffix", "memberNameaffix")
.addSelect("member.birthdate", "memberBirthdate")
.addSelect("salutation.salutation", "memberSalutation")
.addSelect(durationInDays, "durationInDays")
.addSelect(durationInYears, "durationInYears")
.addSelect(exactDuration, "exactDuration")
.addSelect(
`SUM(COALESCE("memberExecutivePositions"."end", CURRENT_DATE) - "memberExecutivePositions"."start")`,
"durationInDays"
)
.addSelect(
`SUM(EXTRACT(YEAR FROM AGE(COALESCE("memberExecutivePositions"."end", CURRENT_DATE), "memberExecutivePositions"."start")))`,
"durationInYears"
)
.addSelect(
`SUM(AGE(COALESCE("memberExecutivePositions"."end", CURRENT_DATE), "memberExecutivePositions"."start"))`,
"exactDuration"
)
.leftJoin("memberExecutivePositions.executivePosition", "executivePosition")
.leftJoin("memberExecutivePositions.member", "member")
.leftJoin("member.salutation", "salutation")

View file

@ -1,36 +1,5 @@
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 AGE(COALESCE("memberQualifications"."end", CURRENT_DATE), "memberQualifications"."start")))`;
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) =>
@ -45,9 +14,18 @@ if (DB_TYPE == "postgres") {
.addSelect("member.nameaffix", "memberNameaffix")
.addSelect("member.birthdate", "memberBirthdate")
.addSelect("salutation.salutation", "memberSalutation")
.addSelect(durationInDays, "durationInDays")
.addSelect(durationInYears, "durationInYears")
.addSelect(exactDuration, "exactDuration")
.addSelect(
`SUM(COALESCE("memberQualifications"."end", CURRENT_DATE) - "memberQualifications"."start")`,
"durationInDays"
)
.addSelect(
`SUM(EXTRACT(YEAR FROM AGE(COALESCE("memberQualifications"."end", CURRENT_DATE), "memberQualifications"."start")))`,
"durationInYears"
)
.addSelect(
`SUM(AGE(COALESCE("memberQualifications"."end", CURRENT_DATE), "memberQualifications"."start"))`,
"exactDuration"
)
.leftJoin("memberQualifications.qualification", "qualification")
.leftJoin("memberQualifications.member", "member")
.leftJoin("member.salutation", "salutation")

View file

@ -1,39 +1,5 @@
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) =>
@ -48,9 +14,9 @@ if (DB_TYPE == "postgres") {
.addSelect("member.nameaffix", "nameaffix")
.addSelect("member.birthdate", "birthdate")
.addSelect("salutation.salutation", "salutation")
.addSelect(todayAge, "todayAge")
.addSelect(ageThisYear, "ageThisYear")
.addSelect(exactAge, "exactAge")
.addSelect(`DATE_PART('year', AGE(CURRENT_DATE, member.birthdate))`, "todayAge")
.addSelect(`EXTRACT(YEAR FROM CURRENT_DATE) - EXTRACT(YEAR FROM member.birthdate)`, "ageThisYear")
.addSelect(`AGE(CURRENT_DATE, member.birthdate)`, "exactAge")
.leftJoin("member.salutation", "salutation"),
})
export class memberView {

View file

@ -1,36 +1,5 @@
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 AGE(COALESCE("membership"."end", CURRENT_DATE), "membership"."start")))`;
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) =>
@ -45,9 +14,12 @@ if (DB_TYPE == "postgres") {
.addSelect("member.nameaffix", "memberNameaffix")
.addSelect("member.birthdate", "memberBirthdate")
.addSelect("salutation.salutation", "memberSalutation")
.addSelect(durationInDays, "durationInDays")
.addSelect(durationInYears, "durationInYears")
.addSelect(exactDuration, "exactDuration")
.addSelect(`SUM(COALESCE("membership"."end", CURRENT_DATE) - "membership"."start") `, "durationInDays")
.addSelect(
`SUM(EXTRACT(YEAR FROM AGE(COALESCE("membership"."end", CURRENT_DATE), "membership"."start")))`,
"durationInYears"
)
.addSelect(`SUM(AGE(COALESCE("membership"."end", CURRENT_DATE), "membership"."start"))`, "exactDuration")
.leftJoin("membership.status", "status")
.leftJoin("membership.member", "member")
.leftJoin("member.salutation", "salutation")
@ -101,14 +73,17 @@ export class membershipView {
.addSelect("member.nameaffix", "memberNameaffix")
.addSelect("member.birthdate", "memberBirthdate")
.addSelect("salutation.salutation", "memberSalutation")
.addSelect(durationInDays, "durationInDays")
.addSelect(durationInYears, "durationInYears")
.addSelect(exactDuration, "exactDuration")
.addSelect(`SUM(COALESCE("membership"."end", CURRENT_DATE) - "membership"."start") `, "durationInDays")
.addSelect(
`SUM(EXTRACT(YEAR FROM AGE(COALESCE("membership"."end", CURRENT_DATE), "membership"."start")))`,
"durationInYears"
)
.addSelect(`SUM(AGE(COALESCE("membership"."end", CURRENT_DATE), "membership"."start"))`, "exactDuration")
.leftJoin("membership.status", "status")
.leftJoin("membership.member", "member")
.leftJoin("member.salutation", "salutation")
.groupBy("status.id")
.addGroupBy("member.id"),
.groupBy("member.id")
.addGroupBy("salutation.id"),
})
export class membershipTotalView {
@ViewColumn()