create and restore backups

This commit is contained in:
Julian Krauser 2025-01-29 16:49:34 +01:00
parent 6ae463a784
commit f78097b616
16 changed files with 696 additions and 21 deletions

View file

@ -47,6 +47,7 @@ export class calendar {
nullable: false,
onDelete: "RESTRICT",
onUpdate: "RESTRICT",
cascade: ["insert"],
})
type: calendarType;
}

View file

@ -54,6 +54,7 @@ export class communication {
nullable: false,
onDelete: "RESTRICT",
onUpdate: "RESTRICT",
cascade: ["insert"],
})
type: communicationType;
}

View file

@ -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;

View file

@ -33,6 +33,7 @@ export class memberAwards {
nullable: false,
onDelete: "RESTRICT",
onUpdate: "RESTRICT",
cascade: ["insert"],
})
award: award;
}

View file

@ -33,6 +33,7 @@ export class memberExecutivePositions {
nullable: false,
onDelete: "RESTRICT",
onUpdate: "RESTRICT",
cascade: ["insert"],
})
executivePosition: executivePosition;
}

View file

@ -36,6 +36,7 @@ export class memberQualifications {
nullable: false,
onDelete: "RESTRICT",
onUpdate: "RESTRICT",
cascade: ["insert"],
})
qualification: qualification;
}

View file

@ -34,6 +34,7 @@ export class membership {
nullable: false,
onDelete: "RESTRICT",
onUpdate: "RESTRICT",
cascade: ["insert"],
})
@JoinColumn()
status: membershipStatus;

View file

@ -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;
}

View file

@ -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[];
}

View file

@ -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;
}

View file

@ -17,6 +17,6 @@ export class role {
})
users: user[];
@OneToMany(() => rolePermission, (rolePermission) => rolePermission.role)
@OneToMany(() => rolePermission, (rolePermission) => rolePermission.role, { cascade: ["insert"] })
permissions: rolePermission[];
}

View file

@ -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[];
}

View file

@ -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[];
}

View file

@ -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)");

638
src/helpers/backupHelper.ts Normal file
View file

@ -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<string>;
};
export type BackupFileContent = { [key in BackupSection]?: BackupFileContentSection } & { collectIds: boolean };
export type BackupFileContentSection = Array<any> | { [key: string]: Array<any> };
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<void> {
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<BackupSection>;
}): Promise<void> {
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<Array<any> | { [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<Array<any>> {
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<any> }> {
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<Array<any>> {
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<Array<any>> {
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<Array<any>> {
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<any> }> {
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<Array<any>> {
return await dataSource.getRepository("query").find({ select: { title: true, query: true } });
}
private static async getTemplate(): Promise<{ [key: string]: Array<any> }> {
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<any> }> {
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<Array<any>> {
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<void> {
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<any>): Promise<void> {
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<any> }): Promise<void> {
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<any>, collectedIds: boolean): Promise<void> {
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<any>, collectedIds: boolean): Promise<void> {
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<any>): Promise<void> {
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<any> }): Promise<void> {
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<any>): Promise<void> {
await this.transactionManager.createQueryBuilder().insert().into("query").values(data).orIgnore().execute();
}
private static async setTemplate(data: { [key: string]: Array<any> }): Promise<void> {
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<any> }): Promise<void> {
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<any>): Promise<void> {
await this.transactionManager.getRepository("webapi").save(data);
}
}

View file

@ -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();
});