diff --git a/.env.example b/.env.example index d71fb73..6520381 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,4 @@ -DB_TYPE = (mysql|sqlite|postgres) +DB_TYPE = (mysql|sqlite|postgres) # default ist mysql ## BSP für mysql DB_PORT = 3306 @@ -19,20 +19,20 @@ DB_HOST = filename.db SERVER_PORT = portnumber -JWT_SECRET = ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 -JWT_EXPIRATION = [0-9]*(y|d|h|m|s) -REFRESH_EXPIRATION = [0-9]*(y|d|h|m|s) -PWA_REFRESH_EXPIRATION = [0-9]*(y|d|h|m|s) +JWT_SECRET = ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 # besitzt default +JWT_EXPIRATION = [0-9]*(y|d|h|m|s) # default ist 15m +REFRESH_EXPIRATION = [0-9]*(y|d|h|m|s) # default ist 1d +PWA_REFRESH_EXPIRATION = [0-9]*(y|d|h|m|s) # default ist 5d MAIL_USERNAME = mail_username MAIL_PASSWORD = mail_password MAIL_HOST = mail_hoststring -MAIL_PORT = mail_portnumber -MAIL_SECURE = (true|false) // true for port 465, fals for other ports +MAIL_PORT = mail_portnumber # default ist 587 +MAIL_SECURE = (true|false) # true für port 465, false für anders gewählten port CLUB_NAME = clubname #default FF Admin -CLUB_WEBSITE = https://my-club-website-url +CLUB_WEBSITE = https://my-club-website-url #optional, muss aber mit http:// oder https:// beginnen -BACKUP_INTERVAL = number of days (min 1) -BACKUP_COPIES = number of parallel copies +BACKUP_INTERVAL = number of days (min 1) # default 1 +BACKUP_COPIES = number of parallel copies #default 7 BACKUP_AUTO_RESTORE = (true|false) # default false \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index a059687..cba8aa0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -32,7 +32,7 @@ RUN apk add --no-cache \ WORKDIR /app -RUN mkdir -p /app/export +RUN mkdir -p /app/files ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser diff --git a/README.md b/README.md index c697570..7faf8c8 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ services: - MAIL_USERNAME= - MAIL_PASSWORD= - MAIL_HOST= - - MAIL_PORT= # default ist auf 578 gesetzt + - MAIL_PORT= # default ist auf 587 gesetzt - MAIL_SECURE= # default ist auf false gesetzt - CLUB_NAME= # default ist auf FF Admin gesetzt - CLUB_WEBSITE= diff --git a/src/command/settings/newsletterConfig/newsletterConfigCommand.ts b/src/command/settings/newsletterConfig/newsletterConfigCommand.ts index be28649..f07de54 100644 --- a/src/command/settings/newsletterConfig/newsletterConfigCommand.ts +++ b/src/command/settings/newsletterConfig/newsletterConfigCommand.ts @@ -4,3 +4,7 @@ export interface SetNewsletterConfigCommand { comTypeId: number; config: NewsletterConfigType; } + +export interface DeleteNewsletterConfigCommand { + comTypeId: number; +} diff --git a/src/command/settings/newsletterConfig/newsletterConfigCommandHandler.ts b/src/command/settings/newsletterConfig/newsletterConfigCommandHandler.ts index 98d8e02..47fa295 100644 --- a/src/command/settings/newsletterConfig/newsletterConfigCommandHandler.ts +++ b/src/command/settings/newsletterConfig/newsletterConfigCommandHandler.ts @@ -2,15 +2,15 @@ import { dataSource } from "../../../data-source"; import { newsletterConfig } from "../../../entity/settings/newsletterConfig"; import DatabaseActionException from "../../../exceptions/databaseActionException"; import InternalException from "../../../exceptions/internalException"; -import { SetNewsletterConfigCommand } from "./newsletterConfigCommand"; +import { DeleteNewsletterConfigCommand, SetNewsletterConfigCommand } from "./newsletterConfigCommand"; export default abstract class NewsletterConfigCommandHandler { /** * @description set newsletterConfig * @param {SetNewsletterConfigCommand} setNewsletterConfig - * @returns {Promise} + * @returns {Promise} */ - static async set(setNewsletterConfig: SetNewsletterConfigCommand): Promise { + static async set(setNewsletterConfig: SetNewsletterConfigCommand): Promise { return await dataSource .createQueryBuilder() .insert() @@ -21,11 +21,27 @@ export default abstract class NewsletterConfigCommandHandler { }) .orUpdate(["config"], "comTypeId") .execute() - .then((result) => { - return result.identifiers[0].id; - }) + .then((result) => {}) .catch((err) => { throw new DatabaseActionException("SET", "newsletterConfig", err); }); } + + /** + * @description delete newsletterConfig + * @param {DeleteNewsletterConfigCommand} deleteNewsletterConfig + * @returns {Promise} + */ + static async delete(deleteNewsletterConfig: DeleteNewsletterConfigCommand): Promise { + return await dataSource + .createQueryBuilder() + .delete() + .from(newsletterConfig) + .where("comTypeId = :comTypeId", { comTypeId: deleteNewsletterConfig.comTypeId }) + .execute() + .then(() => {}) + .catch((err) => { + throw new InternalException("Failed setting newsletterConfig", err); + }); + } } diff --git a/src/controller/admin/settings/newsletterConfigController.ts b/src/controller/admin/settings/newsletterConfigController.ts index 7d0ff95..c476c54 100644 --- a/src/controller/admin/settings/newsletterConfigController.ts +++ b/src/controller/admin/settings/newsletterConfigController.ts @@ -2,7 +2,10 @@ import { Request, Response } from "express"; import NewsletterConfigService from "../../../service/settings/newsletterConfigService"; import NewsletterConfigFactory from "../../../factory/admin/settings/newsletterConfig"; import NewsletterConfigCommandHandler from "../../../command/settings/newsletterConfig/newsletterConfigCommandHandler"; -import { SetNewsletterConfigCommand } from "../../../command/settings/newsletterConfig/newsletterConfigCommand"; +import { + DeleteNewsletterConfigCommand, + SetNewsletterConfigCommand, +} from "../../../command/settings/newsletterConfig/newsletterConfigCommand"; /** * @description get all newsletterConfigs @@ -43,7 +46,24 @@ export async function setNewsletterConfig(req: Request, res: Response): Promise< comTypeId, config, }; - let id = await NewsletterConfigCommandHandler.set(createNewsletterConfig); + await NewsletterConfigCommandHandler.set(createNewsletterConfig); - res.send(id); + res.sendStatus(204); +} + +/** + * @description delete award + * @param req {Request} Express req object + * @param res {Response} Express res object + * @returns {Promise<*>} + */ +export async function deleteNewsletterConfig(req: Request, res: Response): Promise { + const comTypeId = parseInt(req.params.comTypeId); + + let deleteNewsletterConfig: DeleteNewsletterConfigCommand = { + comTypeId: comTypeId, + }; + await NewsletterConfigCommandHandler.delete(deleteNewsletterConfig); + + res.sendStatus(204); } diff --git a/src/env.defaults.ts b/src/env.defaults.ts index 61562b5..19024c2 100644 --- a/src/env.defaults.ts +++ b/src/env.defaults.ts @@ -24,8 +24,8 @@ 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.BACKUP_INTERVAL ?? "0"); -export const BACKUP_COPIES = Number(process.env.BACKUP_COPIES ?? "0"); +export const BACKUP_INTERVAL = Number(process.env.BACKUP_INTERVAL ?? "1"); +export const BACKUP_COPIES = Number(process.env.BACKUP_COPIES ?? "7"); export const BACKUP_AUTO_RESTORE = process.env.BACKUP_AUTO_RESTORE ?? "true"; export function configCheck() { @@ -60,6 +60,8 @@ export function configCheck() { if (BACKUP_AUTO_RESTORE != "true" && BACKUP_AUTO_RESTORE != "false") throw new Error("set 'true' or 'false' to BACKUP_AUTO_RESTORE"); + if (BACKUP_INTERVAL < 1) throw new Error("BACKUP_INTERVAL has to be at least 1"); + if (BACKUP_COPIES < 1) throw new Error("BACKUP_COPIES has to be at least 1"); } function checkMS(input: string, origin: string) { diff --git a/src/helpers/backupHelper.ts b/src/helpers/backupHelper.ts index ff69e71..1024943 100644 --- a/src/helpers/backupHelper.ts +++ b/src/helpers/backupHelper.ts @@ -168,8 +168,9 @@ export default abstract class BackupHelper { this.transactionManager = undefined; }) .catch((err) => { + console.log(err); this.transactionManager = undefined; - throw new DatabaseActionException("BACKUP RESTORE", include.join(", "), err); + throw new DatabaseActionException("BACKUP RESTORE", include.join(", ") || "FULL", err); }); } @@ -331,6 +332,7 @@ export default abstract class BackupHelper { "newsletter.isSent", ]) .addSelect(["dates.calendarId", "dates.diffTitle", "dates.diffDescription"]) + .addSelect(["recipients.memberId"]) .addSelect([ ...(collectIds ? ["member.id"] : []), "member.firstname", @@ -340,7 +342,13 @@ export default abstract class BackupHelper { "member.internalId", ]) .addSelect(["recipientsByQuery.title", "recipientsByQuery.query"]) - .getMany(); + .getMany() + .then((res: any) => + res.map((n: any) => ({ + ...n, + recipients: n.recipients.map((r: any) => ({ ...r, ...(false ? {} : { memberId: undefined }) })), + })) + ); } private static async getNewsletterConfig(): Promise> { return await dataSource @@ -630,7 +638,10 @@ export default abstract class BackupHelper { private static async setNewsletter(data: Array, collectedIds: boolean): Promise { await this.setQueryStore( uniqBy( - data.map((d) => d.recipientsByQueryId).map((d) => ({ ...d, id: undefined })), + data + .map((d) => d.recipientsByQuery) + .filter((q) => q != null) + .map((d) => ({ ...d, id: undefined })), "query" ) ); @@ -639,10 +650,14 @@ export default abstract class BackupHelper { 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, - }, + ...(d.recipientsByQuery != null + ? { + recipientsByQuery: { + ...d.recipientsByQuery, + id: queries.find((s) => s.title == d.recipientsByQuery.title)?.id ?? undefined, + }, + } + : {}), ...(!collectedIds ? { recipients: d.recipients.map((r: any) => ({ @@ -687,7 +702,7 @@ export default abstract class BackupHelper { await this.transactionManager .createQueryBuilder() .insert() - .into("award") + .into("calendar_type") .values(uniqBy([...(data?.["calendar_type"] ?? []), ...usedTypes], "type")) .orIgnore() .execute(); diff --git a/src/index.ts b/src/index.ts index 5aa758a..3517a6b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -23,7 +23,7 @@ import BackupHelper from "./helpers/backupHelper"; dataSource.initialize().then(async () => { if ((BACKUP_AUTO_RESTORE as "true" | "false") == "true" && (await dataSource.createQueryRunner().hasTable("user"))) { await BackupHelper.autoRestoreBackup().catch((err) => { - console.log(`${new Date().toISOString()}: failed auto-restoring database`); + console.log(`${new Date().toISOString()}: failed auto-restoring database`, err); }); } }); diff --git a/src/routes/admin/settings/newsletterConfig.ts b/src/routes/admin/settings/newsletterConfig.ts index 94fe205..a874cc7 100644 --- a/src/routes/admin/settings/newsletterConfig.ts +++ b/src/routes/admin/settings/newsletterConfig.ts @@ -1,5 +1,6 @@ import express, { Request, Response } from "express"; import { + deleteNewsletterConfig, getAllNewsletterConfigs, getNewsletterConfigById, setNewsletterConfig, @@ -24,4 +25,12 @@ router.put( } ); +router.delete( + "/:comTypeId", + PermissionHelper.passCheckMiddleware("create", "settings", "newsletter_config"), + async (req: Request, res: Response) => { + await deleteNewsletterConfig(req, res); + } +); + export default router; diff --git a/src/service/club/member/memberService.ts b/src/service/club/member/memberService.ts index f970a9f..d9080c1 100644 --- a/src/service/club/member/memberService.ts +++ b/src/service/club/member/memberService.ts @@ -5,6 +5,7 @@ import { membership } from "../../../entity/club/member/membership"; import DatabaseActionException from "../../../exceptions/databaseActionException"; import InternalException from "../../../exceptions/internalException"; import { memberView } from "../../../views/memberView"; +import { DB_TYPE } from "../../../env.defaults"; export default abstract class MemberService { /** @@ -158,13 +159,17 @@ export default abstract class MemberService { "member.firstMembershipEntry", "member.memberships", "membership_first", - "membership_first.memberId = member.id AND membership_first.start = (SELECT MIN(m.start) FROM membership m WHERE m.memberId = member.id)" + 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)" ) .leftJoinAndMapOne( "member.lastMembershipEntry", "member.memberships", "membership_last", - "membership_last.memberId = member.id AND membership_last.start = (SELECT MAX(m.start) FROM membership m WHERE m.memberId = member.id)" + 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)" ) .leftJoinAndSelect("membership_first.status", "status_first") .leftJoinAndSelect("membership_last.status", "status_last") @@ -172,17 +177,22 @@ export default abstract class MemberService { "member.preferredCommunication", "member.communications", "preferredCommunication", - "preferredCommunication.preferred = 1" + "preferredCommunication.preferred = true" ) .leftJoinAndSelect("preferredCommunication.type", "communicationtype_preferred") .leftJoinAndMapOne( "member.sendNewsletter", "member.communications", "sendNewsletter", - "sendNewsletter.isSendNewsletter = 1" + "sendNewsletter.isSendNewsletter = true" ) .leftJoinAndSelect("sendNewsletter.type", "communicationtype") - .leftJoinAndMapMany("member.smsAlarming", "member.communications", "smsAlarming", "smsAlarming.isSMSAlarming = 1") + .leftJoinAndMapMany( + "member.smsAlarming", + "member.communications", + "smsAlarming", + "smsAlarming.isSMSAlarming = true" + ) .leftJoinAndSelect("smsAlarming.type", "communicationtype_smsAlarming") .leftJoinAndSelect("member.salutation", "salutation"); } diff --git a/src/service/club/newsletter/newsletterRecipientsService.ts b/src/service/club/newsletter/newsletterRecipientsService.ts index ec6d846..558acdf 100644 --- a/src/service/club/newsletter/newsletterRecipientsService.ts +++ b/src/service/club/newsletter/newsletterRecipientsService.ts @@ -20,6 +20,7 @@ export default abstract class NewsletterRecipientsService { "sendNewsletter", "sendNewsletter.isSendNewsletter = 1" ) + .leftJoinAndSelect("member.salutation", "salutation") .leftJoinAndSelect("sendNewsletter.type", "communicationtype") .leftJoinAndSelect("newsletterRecipients.newsletter", "newsletter") .where("newsletterRecipients.newsletterId = :id", { id: newsletterId })