Compare commits
33 commits
Author | SHA1 | Date | |
---|---|---|---|
3e256dc2f7 | |||
bf5aec351c | |||
0bbe22e9ae | |||
9734149848 | |||
28e05c70a9 | |||
1116b50773 | |||
8b59f3b7a8 | |||
2d8e8a2e42 | |||
eb8f3fef3e | |||
b5509ba162 | |||
4dd6fa6d8a | |||
a4b26013a7 | |||
c803c2a583 | |||
ba03b6f097 | |||
e738be4e13 | |||
c9b8c3090d | |||
a2cc408e85 | |||
d206d7a77c | |||
03036c8b77 | |||
1c6c123142 | |||
e1f6b36336 | |||
916a6da4a0 | |||
b1e949dce2 | |||
69134b210a | |||
0f7516bd3d | |||
632a8290ac | |||
c173f917e3 | |||
55d4a952c3 | |||
03cc0f02b7 | |||
a968c6af25 | |||
8ba12b752c | |||
ad5f624d5d | |||
b547c2bbca |
64 changed files with 321 additions and 222 deletions
|
@ -10,6 +10,7 @@ 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)
|
||||
|
||||
MAIL_USERNAME = mail_username
|
||||
MAIL_PASSWORD = mail_password
|
||||
|
|
33
README.md
33
README.md
|
@ -1,15 +1,17 @@
|
|||
# member-administration-server
|
||||
# ff-admin-server
|
||||
|
||||
Mitgliederverwaltung für Feuerwehren und Vereine (Backend).
|
||||
Administration für Feuerwehren und Vereine (Backend).
|
||||
|
||||
## Einleitung
|
||||
|
||||
Dieses Projekt, `member-administration-server`, ist das Backend zur Verwaltung von Mitgliederdaten. Die zugehörige Webapp ist im Repository [member-administration-ui](https://forgejo.jk-effects.cloud/Ehrenamt/member-administration-ui) zu finden.
|
||||
Dieses Projekt, `ff-admin-server`, ist das Backend zur Verwaltung von Mitgliederdaten. Die zugehörige Webapp ist im Repository [ff-admin-ui](https://forgejo.jk-effects.cloud/Ehrenamt/ff-admin) zu finden.
|
||||
|
||||
Eine Demo zusammen mit der `member-administration-ui` finden Sie unter [ff-admin-demo.jk-effects.cloud](ff-admin-demo.jk-effects.cloud).
|
||||
Eine Demo zusammen mit der `ff-admin` finden Sie unter [https://admin-demo.ff-admin.de](https://admin-demo.ff-admin.de).
|
||||
|
||||
## Installation
|
||||
|
||||
Das Image exposed nur den Port 5000. Die Env-Variable SERVER_PORT kann nur im lokal ausführenden dev-Kontext verwendet werden.
|
||||
|
||||
### Docker Compose Setup
|
||||
|
||||
Um den Container hochzufahren, erstellen Sie eine `docker-compose.yml` Datei mit folgendem Inhalt:
|
||||
|
@ -18,25 +20,26 @@ Um den Container hochzufahren, erstellen Sie eine `docker-compose.yml` Datei mit
|
|||
version: "3"
|
||||
|
||||
services:
|
||||
ff-member-administration-server:
|
||||
image: docker.registry.jk-effects.cloud/ehrenamt/member-administration/server:latest
|
||||
ff-ff-admin-server:
|
||||
image: docker.registry.jk-effects.cloud/ehrenamt/ff-admin/server:latest
|
||||
container_name: ff_member_administration_server
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- DB_TYPE=mysql
|
||||
- DB_TYPE=<mysql|sqlite> # default ist auf mysql gesetzt
|
||||
- DB_HOST=ffm-db
|
||||
- DB_PORT=number # fallback ist auf 3306 gesetzt
|
||||
- DB_NAME=administration
|
||||
- DB_PORT=<number> # default ist auf 3306 gesetzt
|
||||
- DB_NAME=ffadmin
|
||||
- DB_USERNAME=administration_backend
|
||||
- DB_PASSWORD=<dbuserpasswd>
|
||||
- JWT_SECRET=<tobemodified>
|
||||
- JWT_EXPIRATION=<number[m|d] - bsp.:15m>
|
||||
- REFRESH_EXPIRATION=<number[m|d] - bsp.:1d>
|
||||
- JWT_EXPIRATION=<number[m|d] - bsp.:15m> # default ist auf 15m gesetzt
|
||||
- REFRESH_EXPIRATION=<number[m|d] - bsp.:1d> # default ist auf 1d gesetzt
|
||||
- PWA_REFRESH_EXPIRATION=<number[m|d] - bsp.:5d> # default ist auf 5d gesetzt
|
||||
- MAIL_USERNAME=<mailadress|username>
|
||||
- MAIL_PASSWORD=<password>
|
||||
- MAIL_HOST=<url>
|
||||
- MAIL_PORT=<port>
|
||||
- MAIL_SECURE=<boolean>
|
||||
- MAIL_PORT=<port> # default ist auf 578 gesetzt
|
||||
- MAIL_SECURE=<boolean> # default ist auf false gesetzt
|
||||
- CLUB_NAME=<tobemodified>
|
||||
- CLUB_WEBSITE=<tobemodified>
|
||||
volumes:
|
||||
|
@ -75,8 +78,8 @@ docker-compose up -d
|
|||
Klonen Sie dieses Repository und installieren Sie die Abhängigkeiten:
|
||||
|
||||
```sh
|
||||
git clone https://forgejo.jk-effects.cloud/Ehrenamt/member-administration-server.git
|
||||
cd member-administration-server
|
||||
git clone https://forgejo.jk-effects.cloud/Ehrenamt/ff-admin-server.git
|
||||
cd ff-admin-server
|
||||
npm install
|
||||
npm run build
|
||||
npm run start
|
||||
|
|
8
package-lock.json
generated
8
package-lock.json
generated
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "member-administration-server",
|
||||
"version": "1.0.0",
|
||||
"name": "ff-admin-server",
|
||||
"version": "1.1.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "member-administration-server",
|
||||
"version": "1.0.0",
|
||||
"name": "ff-admin-server",
|
||||
"version": "1.1.1",
|
||||
"license": "GPL-3.0-only",
|
||||
"dependencies": {
|
||||
"cors": "^2.8.5",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "member-administration-server",
|
||||
"version": "1.0.0",
|
||||
"name": "ff-admin-server",
|
||||
"version": "1.1.1",
|
||||
"description": "Feuerwehr/Verein Mitgliederverwaltung Server",
|
||||
"main": "dist/index.js",
|
||||
"scripts": {
|
||||
|
@ -16,7 +16,7 @@
|
|||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://forgejo.jk-effects.cloud/Ehrenamt/member-administration-server.git"
|
||||
"url": "https://forgejo.jk-effects.cloud/Ehrenamt/ff-admin-server.git"
|
||||
},
|
||||
"keywords": [
|
||||
"Feuerwehr"
|
||||
|
|
|
@ -43,7 +43,6 @@ export default abstract class MemberCommandHandler {
|
|||
* @returns {Promise<void>}
|
||||
*/
|
||||
static async update(updateMember: UpdateMemberCommand): Promise<void> {
|
||||
console.log(updateMember);
|
||||
return await dataSource
|
||||
.createQueryBuilder()
|
||||
.update(member)
|
||||
|
|
|
@ -31,7 +31,7 @@ export default abstract class MembershipCommandHandler {
|
|||
.createQueryBuilder()
|
||||
.update(membership)
|
||||
.set({
|
||||
end: new Date(createMembership.start).setDate(new Date(createMembership.start).getDate() - 1),
|
||||
end: new Date(new Date(createMembership.start).setDate(new Date(createMembership.start).getDate() - 1)),
|
||||
terminationReason: "beendet durch neuen Eintrag.",
|
||||
})
|
||||
.where("end IS NULL")
|
||||
|
|
|
@ -6,4 +6,5 @@ export interface SynchronizeProtocolPresenceCommand {
|
|||
export interface ProtocolPresenceCommand {
|
||||
memberId: number;
|
||||
absent: boolean;
|
||||
excused: boolean;
|
||||
}
|
||||
|
|
|
@ -74,6 +74,7 @@ export default abstract class ProtocolPresenceCommandHandler {
|
|||
.update(protocolPresence)
|
||||
.set({
|
||||
absent: member.absent,
|
||||
excused: member.excused,
|
||||
})
|
||||
.where("memberId = :memberId", { memberId: member.memberId })
|
||||
.andWhere("protocolId = :protocolId", { protocolId })
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
export interface CreateRefreshCommand {
|
||||
userId: number;
|
||||
isFromPwa?: boolean;
|
||||
}
|
||||
|
||||
export interface DeleteRefreshCommand {
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
import { dataSource } from "../data-source";
|
||||
import { refresh } from "../entity/refresh";
|
||||
import { REFRESH_EXPIRATION } from "../env.defaults";
|
||||
import { PWA_REFRESH_EXPIRATION, REFRESH_EXPIRATION } from "../env.defaults";
|
||||
import InternalException from "../exceptions/internalException";
|
||||
import { JWTHelper } from "../helpers/jwtHelper";
|
||||
import { StringHelper } from "../helpers/stringHelper";
|
||||
import UserService from "../service/user/userService";
|
||||
import { JWTRefresh } from "../type/jwtTypes";
|
||||
import { CreateRefreshCommand, DeleteRefreshCommand } from "./refreshCommand";
|
||||
import ms from "ms";
|
||||
|
||||
|
@ -16,10 +14,6 @@ export default abstract class RefreshCommandHandler {
|
|||
* @returns {Promise<string>}
|
||||
*/
|
||||
static async create(createRefresh: CreateRefreshCommand): Promise<string> {
|
||||
// let createRefreshToken: JWTRefresh = {
|
||||
// userId: createRefresh.userId,
|
||||
// };
|
||||
// const refreshToken = await JWTHelper.create(createRefreshToken);
|
||||
const refreshToken = StringHelper.random(32);
|
||||
|
||||
return await dataSource
|
||||
|
@ -29,7 +23,9 @@ export default abstract class RefreshCommandHandler {
|
|||
.values({
|
||||
token: refreshToken,
|
||||
user: await UserService.getById(createRefresh.userId),
|
||||
expiry: new Date(Date.now() + ms(REFRESH_EXPIRATION)),
|
||||
expiry: createRefresh.isFromPwa
|
||||
? new Date(Date.now() + ms(PWA_REFRESH_EXPIRATION))
|
||||
: new Date(Date.now() + ms(REFRESH_EXPIRATION)),
|
||||
})
|
||||
.execute()
|
||||
.then((result) => {
|
||||
|
|
|
@ -60,7 +60,10 @@ export default abstract class AwardCommandHandler {
|
|||
.execute()
|
||||
.then(() => {})
|
||||
.catch((err) => {
|
||||
throw new InternalException("Failed deleting award", err);
|
||||
throw new InternalException(
|
||||
`Failed deleting award ${err.code.includes("ER_ROW_IS_REFERENCED") ? " due to referenced data" : ""}`,
|
||||
err
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,7 +66,10 @@ export default abstract class CalendarTypeCommandHandler {
|
|||
.execute()
|
||||
.then(() => {})
|
||||
.catch((err) => {
|
||||
throw new InternalException("Failed deleting calendarType", err);
|
||||
throw new InternalException(
|
||||
`Failed deleting calendarType${err.code.includes("ER_ROW_IS_REFERENCED") ? " due to referenced data" : ""}`,
|
||||
err
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,7 +66,12 @@ export default abstract class CommunicationTypeCommandHandler {
|
|||
.execute()
|
||||
.then(() => {})
|
||||
.catch((err) => {
|
||||
throw new InternalException("Failed deleting communicationType", err);
|
||||
throw new InternalException(
|
||||
`Failed deleting communicationType${
|
||||
err.code.includes("ER_ROW_IS_REFERENCED") ? " due to referenced data" : ""
|
||||
}`,
|
||||
err
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,7 +64,12 @@ export default abstract class ExecutivePositionCommandHandler {
|
|||
.execute()
|
||||
.then(() => {})
|
||||
.catch((err) => {
|
||||
throw new InternalException("Failed deleting executivePosition", err);
|
||||
throw new InternalException(
|
||||
`Failed deleting executivePosition${
|
||||
err.code.includes("ER_ROW_IS_REFERENCED") ? " due to referenced data" : ""
|
||||
}`,
|
||||
err
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,7 +64,12 @@ export default abstract class MembershipStatusCommandHandler {
|
|||
.execute()
|
||||
.then(() => {})
|
||||
.catch((err) => {
|
||||
throw new InternalException("Failed deleting membershipStatus", err);
|
||||
throw new InternalException(
|
||||
`Failed deleting membershipStatus${
|
||||
err.code.includes("ER_ROW_IS_REFERENCED") ? " due to referenced data" : ""
|
||||
}`,
|
||||
err
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,3 @@ export interface SetNewsletterConfigCommand {
|
|||
comTypeId: number;
|
||||
config: NewsletterConfigType;
|
||||
}
|
||||
|
||||
export interface DeleteNewsletterConfigCommand {
|
||||
comTypeId: number;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { dataSource } from "../../../data-source";
|
||||
import { newsletterConfig } from "../../../entity/settings/newsletterConfig";
|
||||
import InternalException from "../../../exceptions/internalException";
|
||||
import { DeleteNewsletterConfigCommand, SetNewsletterConfigCommand } from "./newsletterConfigCommand";
|
||||
import { SetNewsletterConfigCommand } from "./newsletterConfigCommand";
|
||||
|
||||
export default abstract class NewsletterConfigCommandHandler {
|
||||
/**
|
||||
|
@ -27,21 +27,4 @@ export default abstract class NewsletterConfigCommandHandler {
|
|||
throw new InternalException("Failed setting newsletterConfig", err);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @description delete newsletterConfig
|
||||
* @param {DeleteNewsletterConfigCommand} deleteNewsletterConfig
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
static async delete(deleteNewsletterConfig: DeleteNewsletterConfigCommand): Promise<void> {
|
||||
return await dataSource
|
||||
.createQueryBuilder()
|
||||
.delete()
|
||||
.from(newsletterConfig)
|
||||
.where("comTypeId = :comTypeId", { comTypeId: deleteNewsletterConfig.comTypeId })
|
||||
.execute()
|
||||
.then(() => {})
|
||||
.catch((err) => {
|
||||
throw new InternalException("Failed setting newsletterConfig", err);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,7 +66,10 @@ export default abstract class QualificationCommandHandler {
|
|||
.execute()
|
||||
.then(() => {})
|
||||
.catch((err) => {
|
||||
throw new InternalException("Failed deleting qualification", err);
|
||||
throw new InternalException(
|
||||
`Failed deleting qualification${err.code.includes("ER_ROW_IS_REFERENCED") ? " due to referenced data" : ""}`,
|
||||
err
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,7 +63,10 @@ export default abstract class QueryStoreCommandHandler {
|
|||
.execute()
|
||||
.then(() => {})
|
||||
.catch((err) => {
|
||||
throw new InternalException("Failed deleting queryStore", err);
|
||||
throw new InternalException(
|
||||
`Failed deleting queryStore${err.code.includes("ER_ROW_IS_REFERENCED") ? " due to referenced data" : ""}`,
|
||||
err
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,7 +64,10 @@ export default abstract class TemplateCommandHandler {
|
|||
.execute()
|
||||
.then(() => {})
|
||||
.catch((err) => {
|
||||
throw new InternalException("Failed deleting template", err);
|
||||
throw new InternalException(
|
||||
`Failed deleting template${err.code.includes("ER_ROW_IS_REFERENCED") ? " due to referenced data" : ""}`,
|
||||
err
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,8 +48,8 @@ import {
|
|||
UpdateCommunicationCommand,
|
||||
} from "../../../command/club/member/communicationCommand";
|
||||
import CommunicationCommandHandler from "../../../command/club/member/communicationCommandHandler";
|
||||
import {PdfExport} from "../../../helpers/pdfExport";
|
||||
import {PermissionModule} from "../../../type/permissionTypes";
|
||||
import { PdfExport } from "../../../helpers/pdfExport";
|
||||
import { PermissionModule } from "../../../type/permissionTypes";
|
||||
|
||||
/**
|
||||
* @description get all members
|
||||
|
@ -61,8 +61,13 @@ export async function getAllMembers(req: Request, res: Response): Promise<any> {
|
|||
let offset = parseInt((req.query.offset as string) ?? "0");
|
||||
let count = parseInt((req.query.count as string) ?? "25");
|
||||
let search = (req.query.search as string) ?? "";
|
||||
let noLimit = req.query.noLimit === "true";
|
||||
let ids = ((req.query.ids ?? "") as string)
|
||||
.split(",")
|
||||
.filter((i) => i)
|
||||
.map((i) => parseInt(i));
|
||||
|
||||
let [members, total] = await MemberService.getAll(offset, count, search);
|
||||
let [members, total] = await MemberService.getAll({ offset, count, search, noLimit, ids });
|
||||
|
||||
res.json({
|
||||
members: MemberFactory.mapToBase(members),
|
||||
|
@ -227,14 +232,14 @@ export async function getCommunicationByMemberAndRecord(req: Request, res: Respo
|
|||
* @returns {Promise<*>}
|
||||
*/
|
||||
export async function createMemberPrintoutList(req: Request, res: Response): Promise<any> {
|
||||
let members = await MemberService.getByRunningMembership()
|
||||
let members = await MemberService.getByRunningMembership();
|
||||
|
||||
let pdf = await PdfExport.renderFile({
|
||||
title: "Mitgliederliste",
|
||||
template: "member.list",
|
||||
saveToDisk: false,
|
||||
data: {
|
||||
member: members
|
||||
member: members,
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -596,7 +601,7 @@ export async function updateCommunicationOfMember(req: Request, res: Response):
|
|||
communicationId: recordId,
|
||||
};
|
||||
await MemberCommandHandler.updateNewsletter(updateNewsletter);
|
||||
} else if (currentUserNewsletterMain.sendNewsletter.id == recordId) {
|
||||
} else if (currentUserNewsletterMain.sendNewsletter?.id == recordId) {
|
||||
await MemberCommandHandler.unsetNewsletter(memberId);
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,10 @@ import NewsletterDatesFactory from "../../../factory/admin/club/newsletter/newsl
|
|||
import NewsletterRecipientsService from "../../../service/club/newsletter/newsletterRecipientsService";
|
||||
import NewsletterRecipientsFactory from "../../../factory/admin/club/newsletter/newsletterRecipients";
|
||||
import { FileSystemHelper } from "../../../helpers/fileSystemHelper";
|
||||
import { CreateNewsletterCommand, SynchronizeNewsletterCommand } from "../../../command/club/newsletter/newsletterCommand";
|
||||
import {
|
||||
CreateNewsletterCommand,
|
||||
SynchronizeNewsletterCommand,
|
||||
} from "../../../command/club/newsletter/newsletterCommand";
|
||||
import NewsletterCommandHandler from "../../../command/club/newsletter/newsletterCommandHandler";
|
||||
import { SynchronizeNewsletterDatesCommand } from "../../../command/club/newsletter/newsletterDatesCommand";
|
||||
import NewsletterDatesCommandHandler from "../../../command/club/newsletter/newsletterDatesCommandHandler";
|
||||
|
@ -188,15 +191,14 @@ export async function createNewsletter(req: Request, res: Response): Promise<any
|
|||
export async function getNewsletterPrintoutProgressById(req: Request, res: Response): Promise<any> {
|
||||
let newsletterId = parseInt(req.params.newsletterId);
|
||||
|
||||
res.setHeader("Content-Type", "text/event-stream");
|
||||
res.setHeader("Cache-Control", "no-cache");
|
||||
res.setHeader("Connection", "keep-alive");
|
||||
|
||||
res.flushHeaders();
|
||||
res.writeHead(200, {
|
||||
"Content-Type": "text/plain",
|
||||
"Transfer-Encoding": "chunked",
|
||||
});
|
||||
|
||||
const progressHandler = (data: NewsletterEventType) => {
|
||||
if (data.newsletterId == newsletterId && data.kind == "pdf") {
|
||||
res.write(JSON.stringify(data));
|
||||
res.write(JSON.stringify(data) + "//");
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -287,15 +289,14 @@ export async function sendNewsletterById(req: Request, res: Response): Promise<a
|
|||
export async function getNewsletterSendingProgressById(req: Request, res: Response): Promise<any> {
|
||||
let newsletterId = parseInt(req.params.newsletterId);
|
||||
|
||||
res.setHeader("Content-Type", "text/event-stream");
|
||||
res.setHeader("Cache-Control", "no-cache");
|
||||
res.setHeader("Connection", "keep-alive");
|
||||
|
||||
res.flushHeaders();
|
||||
res.writeHead(200, {
|
||||
"Content-Type": "text/plain",
|
||||
"Transfer-Encoding": "chunked",
|
||||
});
|
||||
|
||||
const progressHandler = (data: NewsletterEventType) => {
|
||||
if (data.newsletterId == newsletterId && data.kind == "mail") {
|
||||
res.write(JSON.stringify(data));
|
||||
res.write(JSON.stringify(data) + "//");
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -260,7 +260,9 @@ export async function createProtocolPrintoutById(req: Request, res: Response): P
|
|||
agenda,
|
||||
decisions,
|
||||
presence: presence.filter((p) => !p.absent).map((p) => p.member),
|
||||
absent: presence.filter((p) => p.absent).map((p) => p.member),
|
||||
absent: presence.filter((p) => p.absent).map((p) => ({ ...p.member, excused: p.excused })),
|
||||
excused_absent: presence.filter((p) => p.absent && p.excused).map((p) => p.member),
|
||||
unexcused_absent: presence.filter((p) => p.absent && !p.excused).map((p) => p.member),
|
||||
votings,
|
||||
},
|
||||
});
|
||||
|
@ -389,6 +391,7 @@ export async function synchronizeProtocolPrecenseById(req: Request, res: Respons
|
|||
members: presence.map((p) => ({
|
||||
memberId: p.memberId,
|
||||
absent: p.absent,
|
||||
excused: p.excused,
|
||||
})),
|
||||
protocolId,
|
||||
};
|
||||
|
|
|
@ -2,7 +2,7 @@ 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 { DeleteNewsletterConfigCommand, SetNewsletterConfigCommand } from "../../../command/settings/newsletterConfig/newsletterConfigCommand";
|
||||
import { SetNewsletterConfigCommand } from "../../../command/settings/newsletterConfig/newsletterConfigCommand";
|
||||
|
||||
/**
|
||||
* @description get all newsletterConfigs
|
||||
|
@ -47,20 +47,3 @@ export async function setNewsletterConfig(req: Request, res: Response): Promise<
|
|||
|
||||
res.send(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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<any> {
|
||||
const comTypeId = parseInt(req.params.comTypeId);
|
||||
|
||||
let deleteNewsletterConfig: DeleteNewsletterConfigCommand = {
|
||||
comTypeId: comTypeId,
|
||||
};
|
||||
await NewsletterConfigCommandHandler.delete(deleteNewsletterConfig);
|
||||
|
||||
res.sendStatus(204);
|
||||
}
|
||||
|
|
|
@ -8,9 +8,6 @@ import UserService from "../service/user/userService";
|
|||
import speakeasy from "speakeasy";
|
||||
import UnauthorizedRequestException from "../exceptions/unauthorizedRequestException";
|
||||
import RefreshService from "../service/refreshService";
|
||||
import UserPermissionService from "../service/user/userPermissionService";
|
||||
import PermissionHelper from "../helpers/permissionHelper";
|
||||
import RolePermissionService from "../service/user/rolePermissionService";
|
||||
|
||||
/**
|
||||
* @description Check authentication status by token
|
||||
|
@ -39,6 +36,7 @@ export async function login(req: Request, res: Response): Promise<any> {
|
|||
|
||||
let refreshCommand: CreateRefreshCommand = {
|
||||
userId: id,
|
||||
isFromPwa: req.isPWA,
|
||||
};
|
||||
let refreshToken = await RefreshCommandHandler.create(refreshCommand);
|
||||
|
||||
|
@ -83,6 +81,7 @@ export async function refresh(req: Request, res: Response): Promise<any> {
|
|||
|
||||
let refreshCommand: CreateRefreshCommand = {
|
||||
userId: tokenUserId,
|
||||
isFromPwa: req.isPWA,
|
||||
};
|
||||
let refreshToken = await RefreshCommandHandler.create(refreshCommand);
|
||||
|
||||
|
|
|
@ -66,6 +66,8 @@ import { PostalCode1735927918979 } from "./migrations/1735927918979-postalCode";
|
|||
import { ProtocolAbsent1736072179716 } from "./migrations/1736072179716-protocolAbsent";
|
||||
import { Memberlist1736079005086 } from "./migrations/1736079005086-memberlist";
|
||||
import { ExtendViewValues1736084198860 } from "./migrations/1736084198860-extendViewValues";
|
||||
import { FinishInternalIdTransfer1736505324488 } from "./migrations/1736505324488-finishInternalIdTransfer";
|
||||
import { ProtocolPresenceExcuse1737287798828 } from "./migrations/1737287798828-protocolPresenceExcuse";
|
||||
|
||||
const dataSource = new DataSource({
|
||||
type: DB_TYPE as any,
|
||||
|
@ -142,6 +144,8 @@ const dataSource = new DataSource({
|
|||
ProtocolAbsent1736072179716,
|
||||
Memberlist1736079005086,
|
||||
ExtendViewValues1736084198860,
|
||||
FinishInternalIdTransfer1736505324488,
|
||||
ProtocolPresenceExcuse1737287798828,
|
||||
],
|
||||
migrationsRun: true,
|
||||
migrationsTransactionMode: "each",
|
||||
|
|
|
@ -40,17 +40,17 @@ export class communication {
|
|||
@Column()
|
||||
typeId: number;
|
||||
|
||||
@ManyToOne(() => communicationType, (communicationType) => communicationType.communications, {
|
||||
nullable: false,
|
||||
onDelete: "RESTRICT",
|
||||
onUpdate: "RESTRICT",
|
||||
})
|
||||
type: communicationType;
|
||||
|
||||
@ManyToOne(() => member, (member) => member.awards, {
|
||||
nullable: false,
|
||||
onDelete: "CASCADE",
|
||||
onUpdate: "RESTRICT",
|
||||
})
|
||||
member: member;
|
||||
|
||||
@ManyToOne(() => communicationType, (communicationType) => communicationType.communications, {
|
||||
nullable: false,
|
||||
onDelete: "RESTRICT",
|
||||
onUpdate: "RESTRICT",
|
||||
})
|
||||
type: communicationType;
|
||||
}
|
||||
|
|
|
@ -7,12 +7,6 @@ export class membership {
|
|||
@PrimaryColumn({ generated: "increment", type: "int" })
|
||||
id: number;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
@Column({ type: "varchar", length: 255, unique: true, nullable: true })
|
||||
internalId?: string;
|
||||
|
||||
@Column({ type: "date" })
|
||||
start: Date;
|
||||
|
||||
|
|
|
@ -13,6 +13,9 @@ export class protocolPresence {
|
|||
@Column({ type: "boolean", default: false })
|
||||
absent: boolean;
|
||||
|
||||
@Column({ type: "boolean", default: true })
|
||||
excused: boolean;
|
||||
|
||||
@ManyToOne(() => member, {
|
||||
nullable: false,
|
||||
onDelete: "CASCADE",
|
||||
|
|
|
@ -9,7 +9,7 @@ export class protocolPrintout {
|
|||
@Column({ type: "varchar", length: 255 })
|
||||
title: string;
|
||||
|
||||
@Column({ type: "int" })
|
||||
@Column({ type: "int", default: "1" })
|
||||
iteration: number;
|
||||
|
||||
@Column({ type: "varchar", length: 255 })
|
||||
|
|
|
@ -13,6 +13,7 @@ export const SERVER_PORT = Number(process.env.SERVER_PORT ?? 5000);
|
|||
export const JWT_SECRET = process.env.JWT_SECRET ?? "my_jwt_secret_string_ilughfnadiuhgq§$IUZGFVRweiouarbt1oub3h5q4a";
|
||||
export const JWT_EXPIRATION = process.env.JWT_EXPIRATION ?? "15m";
|
||||
export const REFRESH_EXPIRATION = process.env.REFRESH_EXPIRATION ?? "1d";
|
||||
export const PWA_REFRESH_EXPIRATION = process.env.PWA_REFRESH_EXPIRATION ?? "5d";
|
||||
|
||||
export const MAIL_USERNAME = process.env.MAIL_USERNAME ?? "";
|
||||
export const MAIL_PASSWORD = process.env.MAIL_PASSWORD ?? "";
|
||||
|
@ -35,6 +36,7 @@ export function configCheck() {
|
|||
if (JWT_SECRET == "" || typeof JWT_SECRET != "string") throw new Error("set valid value to JWT_SECRET");
|
||||
checkMS(JWT_EXPIRATION, "JWT_EXPIRATION");
|
||||
checkMS(REFRESH_EXPIRATION, "REFRESH_EXPIRATION");
|
||||
checkMS(PWA_REFRESH_EXPIRATION, "PWA_REFRESH_EXPIRATION");
|
||||
|
||||
if (MAIL_USERNAME == "" || typeof MAIL_USERNAME != "string") throw new Error("set valid value to MAIL_USERNAME");
|
||||
if (MAIL_PASSWORD == "" || typeof MAIL_PASSWORD != "string") throw new Error("set valid value to MAIL_PASSWORD");
|
||||
|
@ -42,7 +44,6 @@ export function configCheck() {
|
|||
if (typeof MAIL_PORT != "number") throw new Error("set valid numeric value to MAIL_PORT");
|
||||
if (MAIL_SECURE != "true" && MAIL_SECURE != "false") throw new Error("set 'true' or 'false' to MAIL_SECURE");
|
||||
|
||||
console.log(CLUB_WEBSITE);
|
||||
if (
|
||||
CLUB_WEBSITE != "" &&
|
||||
!/^(http(s):\/\/.)[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)$/.test(CLUB_WEBSITE)
|
||||
|
|
|
@ -12,6 +12,7 @@ export default abstract class ProtocolPresenceFactory {
|
|||
return {
|
||||
memberId: record.member.id,
|
||||
absent: record.absent,
|
||||
excused: record.excused,
|
||||
protocolId: record.protocolId,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -33,12 +33,21 @@ export abstract class NewsletterHelper {
|
|||
private static formatJobEmit(
|
||||
event: "progress" | "complete",
|
||||
kind: "pdf" | "mail",
|
||||
factor: "success" | "failed" | "info",
|
||||
newsletterId: number,
|
||||
total: number,
|
||||
iteration: number,
|
||||
msg: string
|
||||
) {
|
||||
this.jobStatus.emit<NewsletterEventType>(event, { kind, newsletterId, total, iteration, msg, date: new Date() });
|
||||
this.jobStatus.emit<NewsletterEventType>(event, {
|
||||
kind,
|
||||
newsletterId,
|
||||
factor,
|
||||
total,
|
||||
iteration,
|
||||
msg,
|
||||
date: new Date(),
|
||||
});
|
||||
}
|
||||
|
||||
public static buildData(
|
||||
|
@ -132,9 +141,7 @@ export abstract class NewsletterHelper {
|
|||
}
|
||||
}
|
||||
|
||||
console.log(queryMemberIds);
|
||||
|
||||
let members = await MemberService.getAll(0, 1000);
|
||||
let members = await MemberService.getAll({ noLimit: true });
|
||||
|
||||
return members[0].filter((m) => queryMemberIds.includes(m.id));
|
||||
}
|
||||
|
@ -171,18 +178,9 @@ export abstract class NewsletterHelper {
|
|||
allowedForMail.includes(m.sendNewsletter?.type?.id)
|
||||
);
|
||||
|
||||
this.formatJobEmit("progress", "mail", newsletterId, mailRecipients.length, 0, "starting sending");
|
||||
this.formatJobEmit("progress", "mail", "info", newsletterId, mailRecipients.length, 0, "starting sending");
|
||||
|
||||
for (const [index, rec] of mailRecipients.entries()) {
|
||||
this.formatJobEmit(
|
||||
"progress",
|
||||
"mail",
|
||||
newsletterId,
|
||||
mailRecipients.length,
|
||||
index,
|
||||
`start sending to ${rec.sendNewsletter.email}`
|
||||
);
|
||||
|
||||
let data = this.buildData(newsletter, dates, rec);
|
||||
|
||||
const { body } = await TemplateHelper.renderFileForModule({
|
||||
|
@ -197,9 +195,10 @@ export abstract class NewsletterHelper {
|
|||
this.formatJobEmit(
|
||||
"progress",
|
||||
"mail",
|
||||
"success",
|
||||
newsletterId,
|
||||
mailRecipients.length,
|
||||
index,
|
||||
index + 1,
|
||||
`successfully sent to ${rec.sendNewsletter.email}`
|
||||
);
|
||||
})
|
||||
|
@ -207,18 +206,19 @@ export abstract class NewsletterHelper {
|
|||
this.formatJobEmit(
|
||||
"progress",
|
||||
"mail",
|
||||
"failed",
|
||||
newsletterId,
|
||||
mailRecipients.length,
|
||||
index,
|
||||
index + 1,
|
||||
`failed to send to ${rec.sendNewsletter.email}`
|
||||
);
|
||||
console.log("mail send", err);
|
||||
});
|
||||
}
|
||||
|
||||
this.formatJobEmit(
|
||||
"complete",
|
||||
"mail",
|
||||
"info",
|
||||
newsletterId,
|
||||
mailRecipients.length,
|
||||
mailRecipients.length,
|
||||
|
@ -249,21 +249,12 @@ export abstract class NewsletterHelper {
|
|||
(m) => !notAllowedForPdf.includes(m.sendNewsletter?.type?.id) || m.sendNewsletter == null
|
||||
);
|
||||
|
||||
this.formatJobEmit("progress", "pdf", newsletterId, pdfRecipients.length, 0, "starting sending");
|
||||
this.formatJobEmit("progress", "pdf", "info", newsletterId, pdfRecipients.length + 1, 0, "starting sending");
|
||||
|
||||
for (const [index, rec] of [
|
||||
...pdfRecipients,
|
||||
{ id: 0, firstname: "Alle Mitglieder", lastname: CLUB_NAME } as member,
|
||||
].entries()) {
|
||||
this.formatJobEmit(
|
||||
"progress",
|
||||
"pdf",
|
||||
newsletterId,
|
||||
pdfRecipients.length,
|
||||
index,
|
||||
`start print for ${rec.lastname}, ${rec.firstname}`
|
||||
);
|
||||
|
||||
let data = this.buildData(newsletter, dates, rec, printWithAdress.includes(rec.sendNewsletter?.type?.id));
|
||||
|
||||
await PdfExport.renderFile({
|
||||
|
@ -277,9 +268,10 @@ export abstract class NewsletterHelper {
|
|||
this.formatJobEmit(
|
||||
"progress",
|
||||
"pdf",
|
||||
"success",
|
||||
newsletterId,
|
||||
pdfRecipients.length,
|
||||
index,
|
||||
pdfRecipients.length + 1,
|
||||
index + 1,
|
||||
`successfully printed for ${rec.lastname}, ${rec.firstname}`
|
||||
);
|
||||
})
|
||||
|
@ -287,24 +279,15 @@ export abstract class NewsletterHelper {
|
|||
this.formatJobEmit(
|
||||
"progress",
|
||||
"pdf",
|
||||
"failed",
|
||||
newsletterId,
|
||||
pdfRecipients.length,
|
||||
index,
|
||||
pdfRecipients.length + 1,
|
||||
index + 1,
|
||||
`failed print for ${rec.lastname}, ${rec.firstname}`
|
||||
);
|
||||
console.log("pdf print", err);
|
||||
});
|
||||
}
|
||||
|
||||
this.formatJobEmit(
|
||||
"progress",
|
||||
"pdf",
|
||||
newsletterId,
|
||||
pdfRecipients.length,
|
||||
pdfRecipients.length,
|
||||
"starting pdf combine"
|
||||
);
|
||||
|
||||
await PdfExport.sqashToSingleFile(
|
||||
`newsletter/${newsletter.id}_${newsletter.title.replace(" ", "")}`,
|
||||
"allPdfsTogether",
|
||||
|
@ -314,9 +297,10 @@ export abstract class NewsletterHelper {
|
|||
this.formatJobEmit(
|
||||
"progress",
|
||||
"pdf",
|
||||
"success",
|
||||
newsletterId,
|
||||
pdfRecipients.length,
|
||||
pdfRecipients.length,
|
||||
pdfRecipients.length + 1,
|
||||
pdfRecipients.length + 1,
|
||||
"sucessfully combined pdf"
|
||||
);
|
||||
})
|
||||
|
@ -324,20 +308,21 @@ export abstract class NewsletterHelper {
|
|||
this.formatJobEmit(
|
||||
"progress",
|
||||
"pdf",
|
||||
"failed",
|
||||
newsletterId,
|
||||
pdfRecipients.length,
|
||||
pdfRecipients.length,
|
||||
pdfRecipients.length + 1,
|
||||
pdfRecipients.length + 1,
|
||||
"failed combining pdf"
|
||||
);
|
||||
console.log("pdf squash", err);
|
||||
});
|
||||
|
||||
this.formatJobEmit(
|
||||
"complete",
|
||||
"pdf",
|
||||
"info",
|
||||
newsletterId,
|
||||
pdfRecipients.length,
|
||||
pdfRecipients.length,
|
||||
pdfRecipients.length + 1,
|
||||
pdfRecipients.length + 1,
|
||||
`completed printing process`
|
||||
);
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ declare global {
|
|||
username: string;
|
||||
isOwner: boolean;
|
||||
permissions: PermissionObject;
|
||||
isPWA: boolean;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +24,7 @@ const app = express();
|
|||
import router from "./routes/index";
|
||||
router(app);
|
||||
app.listen(process.env.NODE_ENV ? SERVER_PORT : 5000, () => {
|
||||
console.log(`listening on *:${SERVER_PORT}`);
|
||||
console.log(`listening on *:${process.env.NODE_ENV ? SERVER_PORT : 5000}`);
|
||||
});
|
||||
|
||||
import schedule from "node-schedule";
|
||||
|
|
11
src/middleware/detectPWA.ts
Normal file
11
src/middleware/detectPWA.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
import { Request, Response } from "express";
|
||||
|
||||
export default async function detectPWA(req: Request, res: Response, next: Function) {
|
||||
const userAgent = req.headers["user-agent"] || "";
|
||||
if ((userAgent.includes("Mobile") && userAgent.includes("Standalone")) || req.headers["x-pwa-client"] === "true") {
|
||||
req.isPWA = true;
|
||||
} else {
|
||||
req.isPWA = false;
|
||||
}
|
||||
next();
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
import { Request, Response } from "express";
|
||||
import { ExceptionBase } from "../exceptions/exceptionsBaseType";
|
||||
import CustomRequestException from "../exceptions/customRequestException";
|
||||
import UnauthorizedRequestException from "../exceptions/unauthorizedRequestException";
|
||||
|
||||
export default function errorHandler(err: ExceptionBase | Error, req: Request, res: Response, next: Function) {
|
||||
let status = 500;
|
||||
|
@ -11,7 +12,11 @@ export default function errorHandler(err: ExceptionBase | Error, req: Request, r
|
|||
msg = err.message;
|
||||
}
|
||||
|
||||
console.log("Handler", err);
|
||||
if (err instanceof CustomRequestException) {
|
||||
console.log("Custom Handler", status, msg);
|
||||
} else {
|
||||
console.log("Error Handler", err);
|
||||
}
|
||||
|
||||
res.status(status).send(msg);
|
||||
}
|
||||
|
|
|
@ -18,27 +18,25 @@ export class InternalId1735822722235 implements MigrationInterface {
|
|||
})
|
||||
);
|
||||
|
||||
let memberships = await queryRunner.manager.getRepository(membership).find();
|
||||
console.log(memberships);
|
||||
let internalIds = memberships.reduce<{ [key: number]: Array<string> }>((acc, cur) => {
|
||||
let memberId = cur.memberId;
|
||||
let setIds = acc[memberId] ?? [];
|
||||
if (cur?.internalId) {
|
||||
setIds.push(cur.internalId);
|
||||
}
|
||||
acc[memberId] = setIds;
|
||||
return acc;
|
||||
}, {});
|
||||
// let memberships = await queryRunner.manager.getRepository(membership).find();
|
||||
// let internalIds = memberships.reduce<{ [key: number]: Array<string> }>((acc, cur) => {
|
||||
// let memberId = cur.memberId;
|
||||
// let setIds = acc[memberId] ?? [];
|
||||
// if (cur?.internalId) {
|
||||
// setIds.push(cur.internalId);
|
||||
// }
|
||||
// acc[memberId] = setIds;
|
||||
// return acc;
|
||||
// }, {});
|
||||
|
||||
console.log(internalIds);
|
||||
for (const [id, value] of Object.entries(internalIds)) {
|
||||
const ids = value.filter((v) => v != null).join(", ");
|
||||
if (ids) {
|
||||
let m = await queryRunner.manager.getRepository(member).findOneByOrFail({ id: parseInt(id) });
|
||||
m.internalId = ids;
|
||||
await queryRunner.manager.getRepository(member).save(m);
|
||||
}
|
||||
}
|
||||
// for (const [id, value] of Object.entries(internalIds)) {
|
||||
// const ids = value.filter((v) => v != null).join(", ");
|
||||
// if (ids) {
|
||||
// let m = await queryRunner.manager.getRepository(member).findOneByOrFail({ id: parseInt(id) });
|
||||
// m.internalId = ids;
|
||||
// await queryRunner.manager.getRepository(member).save(m);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
|
|
|
@ -55,7 +55,8 @@ export class ExtendViewValues1736084198860 implements MigrationInterface {
|
|||
)
|
||||
.leftJoin("membership.status", "status")
|
||||
.leftJoin("membership.member", "member")
|
||||
.groupBy("status.id"),
|
||||
.groupBy("status.id")
|
||||
.addGroupBy("member.id"),
|
||||
}),
|
||||
true
|
||||
);
|
||||
|
|
23
src/migrations/1736505324488-finishInternalIdTransfer.ts
Normal file
23
src/migrations/1736505324488-finishInternalIdTransfer.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import { MigrationInterface, QueryRunner, TableColumn } from "typeorm";
|
||||
|
||||
export class FinishInternalIdTransfer1736505324488 implements MigrationInterface {
|
||||
name = "FinishInternalIdTransfer1736505324488";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.dropColumn("membership", "internalId");
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.addColumn(
|
||||
"membership",
|
||||
new TableColumn({
|
||||
name: "internalId",
|
||||
type: "varchar",
|
||||
length: "255",
|
||||
default: null,
|
||||
isNullable: true,
|
||||
isUnique: true,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
21
src/migrations/1737287798828-protocolPresenceExcuse.ts
Normal file
21
src/migrations/1737287798828-protocolPresenceExcuse.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
import { MigrationInterface, QueryRunner, TableColumn } from "typeorm";
|
||||
|
||||
export class ProtocolPresenceExcuse1737287798828 implements MigrationInterface {
|
||||
name = "ProtocolPresenceExcuse1737287798828";
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.addColumn(
|
||||
"protocol_presence",
|
||||
new TableColumn({
|
||||
name: "excused",
|
||||
type: "tinyint",
|
||||
default: "1",
|
||||
isNullable: false,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.dropColumn("protocol_presence", "excused");
|
||||
}
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
import express, { Request, Response } from "express";
|
||||
import {
|
||||
deleteNewsletterConfig,
|
||||
getAllNewsletterConfigs,
|
||||
getNewsletterConfigById,
|
||||
setNewsletterConfig,
|
||||
|
@ -25,12 +24,4 @@ router.put(
|
|||
}
|
||||
);
|
||||
|
||||
router.delete(
|
||||
"/:comTypeId",
|
||||
PermissionHelper.passCheckMiddleware("create", "settings", "newsletter_config"),
|
||||
async (req: Request, res: Response) => {
|
||||
await deleteNewsletterConfig(req, res);
|
||||
}
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
|
|
@ -13,6 +13,7 @@ import reset from "./reset";
|
|||
import auth from "./auth";
|
||||
import admin from "./admin/index";
|
||||
import user from "./user";
|
||||
import detectPWA from "../middleware/detectPWA";
|
||||
|
||||
export default (app: Express) => {
|
||||
app.set("query parser", "extended");
|
||||
|
@ -25,6 +26,7 @@ export default (app: Express) => {
|
|||
app.use(cors());
|
||||
app.options("*", cors());
|
||||
|
||||
app.use(detectPWA);
|
||||
app.use("/api/public", publicAvailable);
|
||||
app.use("/api/setup", allowSetup, setup);
|
||||
app.use("/api/reset", reset);
|
||||
|
|
|
@ -12,6 +12,7 @@ export default abstract class CalendarService {
|
|||
.getRepository(calendar)
|
||||
.createQueryBuilder("calendar")
|
||||
.leftJoinAndSelect("calendar.type", "type")
|
||||
.orderBy("starttime", "ASC")
|
||||
.getMany()
|
||||
.then((res) => {
|
||||
return res;
|
||||
|
|
|
@ -16,6 +16,7 @@ export default abstract class CommunicationService {
|
|||
.leftJoinAndSelect("communication.member", "member")
|
||||
.leftJoinAndSelect("member.sendNewsletter", "sendNewsletter")
|
||||
.where("communication.memberId = :memberId", { memberId: memberId })
|
||||
.orderBy("communicationType.type", "ASC")
|
||||
.getMany()
|
||||
.then((res) => {
|
||||
return res;
|
||||
|
|
|
@ -14,6 +14,7 @@ export default abstract class MemberAwardService {
|
|||
.createQueryBuilder("memberAwards")
|
||||
.leftJoinAndSelect("memberAwards.award", "award")
|
||||
.where("memberAwards.memberId = :memberId", { memberId: memberId })
|
||||
.orderBy("award.award", "ASC")
|
||||
.getMany()
|
||||
.then((res) => {
|
||||
return res;
|
||||
|
|
|
@ -14,6 +14,8 @@ export default abstract class MemberExecutivePositionService {
|
|||
.createQueryBuilder("memberExecutivePositions")
|
||||
.leftJoinAndSelect("memberExecutivePositions.executivePosition", "executivePosition")
|
||||
.where("memberExecutivePositions.memberId = :memberId", { memberId: memberId })
|
||||
.orderBy("executivePosition.position", "ASC")
|
||||
.addOrderBy("memberExecutivePositions.start", "DESC")
|
||||
.getMany()
|
||||
.then((res) => {
|
||||
return res;
|
||||
|
|
|
@ -14,6 +14,7 @@ export default abstract class MemberQualificationService {
|
|||
.createQueryBuilder("memberQualifications")
|
||||
.leftJoinAndSelect("memberQualifications.qualification", "qualification")
|
||||
.where("memberQualifications.memberId = :memberId", { memberId: memberId })
|
||||
.orderBy("qualification.qualification", "ASC")
|
||||
.getMany()
|
||||
.then((res) => {
|
||||
return res;
|
||||
|
|
|
@ -8,7 +8,19 @@ export default abstract class MemberService {
|
|||
* @description get all members
|
||||
* @returns {Promise<[Array<member>, number]>}
|
||||
*/
|
||||
static async getAll(offset: number = 0, count: number = 25, search: string = ""): Promise<[Array<member>, number]> {
|
||||
static async getAll({
|
||||
offset = 0,
|
||||
count = 25,
|
||||
search = "",
|
||||
noLimit = false,
|
||||
ids = [],
|
||||
}: {
|
||||
offset?: number;
|
||||
count?: number;
|
||||
search?: string;
|
||||
noLimit?: boolean;
|
||||
ids?: Array<number>;
|
||||
}): Promise<[Array<member>, number]> {
|
||||
let query = dataSource
|
||||
.getRepository(member)
|
||||
.createQueryBuilder("member")
|
||||
|
@ -54,9 +66,15 @@ export default abstract class MemberService {
|
|||
});
|
||||
}
|
||||
|
||||
if (ids.length != 0) {
|
||||
query = query.where({ id: ids });
|
||||
}
|
||||
|
||||
if (!noLimit) {
|
||||
query = query.offset(offset).limit(count);
|
||||
}
|
||||
|
||||
return await query
|
||||
.offset(offset)
|
||||
.limit(count)
|
||||
.orderBy("member.lastname")
|
||||
.addOrderBy("member.firstname")
|
||||
.addOrderBy("member.nameaffix")
|
||||
|
@ -124,6 +142,9 @@ export default abstract class MemberService {
|
|||
.createQueryBuilder("member")
|
||||
.leftJoinAndSelect("member.memberships", "membership")
|
||||
.where("membership.end IS NULL")
|
||||
.orderBy("member.lastname")
|
||||
.addOrderBy("member.firstname")
|
||||
.addOrderBy("member.nameaffix")
|
||||
.getMany()
|
||||
.then((res) => {
|
||||
return res;
|
||||
|
|
|
@ -12,6 +12,7 @@ export default abstract class AwardService {
|
|||
return await dataSource
|
||||
.getRepository(award)
|
||||
.createQueryBuilder("award")
|
||||
.orderBy("award", "ASC")
|
||||
.getMany()
|
||||
.then((res) => {
|
||||
return res;
|
||||
|
|
|
@ -11,6 +11,7 @@ export default abstract class CalendarTypeService {
|
|||
return await dataSource
|
||||
.getRepository(calendarType)
|
||||
.createQueryBuilder("calendarType")
|
||||
.orderBy("type", "ASC")
|
||||
.getMany()
|
||||
.then((res) => {
|
||||
return res;
|
||||
|
|
|
@ -11,6 +11,7 @@ export default abstract class CommunicationTypeService {
|
|||
return await dataSource
|
||||
.getRepository(communicationType)
|
||||
.createQueryBuilder("communicationType")
|
||||
.orderBy("type", "ASC")
|
||||
.getMany()
|
||||
.then((res) => {
|
||||
return res;
|
||||
|
|
|
@ -12,6 +12,7 @@ export default abstract class ExecutivePositionService {
|
|||
return await dataSource
|
||||
.getRepository(executivePosition)
|
||||
.createQueryBuilder("executivePosition")
|
||||
.orderBy("position", "ASC")
|
||||
.getMany()
|
||||
.then((res) => {
|
||||
return res;
|
||||
|
|
|
@ -12,6 +12,7 @@ export default abstract class MembershipStatusService {
|
|||
return await dataSource
|
||||
.getRepository(membershipStatus)
|
||||
.createQueryBuilder("membershipStatus")
|
||||
.orderBy("status", "ASC")
|
||||
.getMany()
|
||||
.then((res) => {
|
||||
return res;
|
||||
|
|
|
@ -13,6 +13,7 @@ export default abstract class NewsletterConfigService {
|
|||
.getRepository(newsletterConfig)
|
||||
.createQueryBuilder("newsletterConfig")
|
||||
.leftJoinAndSelect("newsletterConfig.comType", "comType")
|
||||
.orderBy("comType.type", "ASC")
|
||||
.getMany()
|
||||
.then((res) => {
|
||||
return res;
|
||||
|
|
|
@ -13,6 +13,7 @@ export default abstract class QualificationService {
|
|||
return await dataSource
|
||||
.getRepository(qualification)
|
||||
.createQueryBuilder("qualification")
|
||||
.orderBy("qualification", "ASC")
|
||||
.getMany()
|
||||
.then((res) => {
|
||||
return res;
|
||||
|
|
|
@ -11,6 +11,7 @@ export default abstract class QueryStoreService {
|
|||
return await dataSource
|
||||
.getRepository(query)
|
||||
.createQueryBuilder("queryStore")
|
||||
.orderBy("title", "ASC")
|
||||
.getMany()
|
||||
.then((res) => {
|
||||
return res;
|
||||
|
|
|
@ -12,6 +12,7 @@ export default abstract class TemplateService {
|
|||
return await dataSource
|
||||
.getRepository(template)
|
||||
.createQueryBuilder("template")
|
||||
.orderBy("template", "ASC")
|
||||
.getMany()
|
||||
.then((res) => {
|
||||
return res;
|
||||
|
|
|
@ -14,6 +14,7 @@ export default abstract class TemplateUsageService {
|
|||
.leftJoinAndSelect("templateUsage.header", "headerTemplate")
|
||||
.leftJoinAndSelect("templateUsage.body", "bodyTemplate")
|
||||
.leftJoinAndSelect("templateUsage.footer", "footerTemplate")
|
||||
.orderBy("scope", "ASC")
|
||||
.getMany()
|
||||
.then((res) => {
|
||||
return res;
|
||||
|
|
|
@ -12,6 +12,7 @@ export default abstract class RoleService {
|
|||
.getRepository(role)
|
||||
.createQueryBuilder("role")
|
||||
.leftJoinAndSelect("role.permissions", "role_permissions")
|
||||
.orderBy("role", "ASC")
|
||||
.getMany()
|
||||
.then((res) => {
|
||||
return res;
|
||||
|
|
|
@ -15,6 +15,8 @@ export default abstract class UserService {
|
|||
.leftJoinAndSelect("user.roles", "roles")
|
||||
.leftJoinAndSelect("user.permissions", "permissions")
|
||||
.leftJoinAndSelect("roles.permissions", "role_permissions")
|
||||
.orderBy("firstname", "ASC")
|
||||
.addOrderBy("lastname", "ASC")
|
||||
.getMany()
|
||||
.then((res) => {
|
||||
return res;
|
||||
|
|
|
@ -8,11 +8,13 @@
|
|||
<h1>Mitgliederliste</h1>
|
||||
<br />
|
||||
|
||||
<table style="width:100%">
|
||||
<table style="width: 100%">
|
||||
{{#each member}}
|
||||
<tr>
|
||||
<th style="width: 35%;padding: 10px 5px">{{this.firstname}} {{this.lastname}} {{this.nameaffix}}</th>
|
||||
<th style="width: 65%;padding: 10px 0"></th>
|
||||
<th style="width: 35%; padding: 10px 5px">
|
||||
{{this.lastname}} {{this.firstname}}{{#if this.nameaffix}} - {{this.nameaffix}}{{/if}}
|
||||
</th>
|
||||
<th style="width: 65%; padding: 10px 0"></th>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</table>
|
||||
|
@ -37,7 +39,9 @@
|
|||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
table, th, td {
|
||||
table,
|
||||
th,
|
||||
td {
|
||||
border: 1px solid black;
|
||||
border-collapse: collapse;
|
||||
text-align: start;
|
||||
|
|
|
@ -14,9 +14,19 @@
|
|||
<br />
|
||||
<br />
|
||||
<h2>Anwesenheit ({{presence.length}})</h2>
|
||||
<p>{{#each presence}} {{this.firstname}} {{this.lastname}}{{#unless @last}}, {{/unless}} {{/each}}</p>
|
||||
<p>
|
||||
{{#each presence}}{{this.firstname}} {{this.lastname}}{{#unless @last}}, {{/unless}}{{/each}}{{#unless
|
||||
presence.length}}---{{/unless}}
|
||||
</p>
|
||||
<h2>Abwesenheit ({{absent.length}})</h2>
|
||||
<p>{{#each absent}} {{this.firstname}} {{this.lastname}}{{#unless @last}}, {{/unless}} {{/each}}</p>
|
||||
<p>
|
||||
entschuldigt: {{#each excused_absent}}{{this.firstname}} {{this.lastname}}{{#unless @last}},
|
||||
{{/unless}}{{/each}}{{#unless excused_absent.length}}---{{/unless}}
|
||||
</p>
|
||||
<p>
|
||||
unentschuldigt: {{#each unexcused_absent}}{{this.firstname}} {{this.lastname}}{{#unless @last}},
|
||||
{{/unless}}{{/each}}{{#unless unexcused_absent.length}}---{{/unless}}
|
||||
</p>
|
||||
<br />
|
||||
<h2>Agenda</h2>
|
||||
{{#each agenda}}
|
||||
|
|
|
@ -3,5 +3,6 @@ import { MemberViewModel } from "../member/member.models";
|
|||
export interface ProtocolPresenceViewModel {
|
||||
memberId: number;
|
||||
absent: boolean;
|
||||
excused: boolean;
|
||||
protocolId: number;
|
||||
}
|
||||
|
|
|
@ -22,7 +22,8 @@ import { Salutation } from "../enums/salutation";
|
|||
)
|
||||
.leftJoin("membership.status", "status")
|
||||
.leftJoin("membership.member", "member")
|
||||
.groupBy("status.id"),
|
||||
.groupBy("status.id")
|
||||
.addGroupBy("member.id"),
|
||||
})
|
||||
export class membershipView {
|
||||
@ViewColumn()
|
||||
|
|
Loading…
Reference in a new issue