Compare commits
45 commits
anton/impo
...
main
Author | SHA1 | Date | |
---|---|---|---|
d9132e472b | |||
c661a588c5 | |||
3beeec38f9 | |||
3d8f1da059 | |||
bbfdc69049 | |||
a165231c47 | |||
313785b4ac | |||
0b40b9d92c | |||
d3870b4c8d | |||
ba2155d7c3 | |||
07d31bfe7d | |||
4568bef10e | |||
36ecccd0dc | |||
3f0549bd44 | |||
ca6dbafaf1 | |||
26d2f288e9 | |||
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 |
94 changed files with 1470 additions and 835 deletions
|
@ -10,6 +10,7 @@ SERVER_PORT = portnumber
|
||||||
JWT_SECRET = ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890
|
JWT_SECRET = ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890
|
||||||
JWT_EXPIRATION = [0-9]*(y|d|h|m|s)
|
JWT_EXPIRATION = [0-9]*(y|d|h|m|s)
|
||||||
REFRESH_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_USERNAME = mail_username
|
||||||
MAIL_PASSWORD = mail_password
|
MAIL_PASSWORD = mail_password
|
||||||
|
@ -17,5 +18,5 @@ MAIL_HOST = mail_hoststring
|
||||||
MAIL_PORT = mail_portnumber
|
MAIL_PORT = mail_portnumber
|
||||||
MAIL_SECURE (true|false) // true for port 465, fals for other ports
|
MAIL_SECURE (true|false) // true for port 465, fals for other ports
|
||||||
|
|
||||||
CLUB_NAME = clubname
|
CLUB_NAME = clubname #default FF Admin
|
||||||
CLUB_WEBSITE = https://my-club-website-url
|
CLUB_WEBSITE = https://my-club-website-url
|
|
@ -35,13 +35,12 @@ WORKDIR /app
|
||||||
RUN mkdir -p /app/export
|
RUN mkdir -p /app/export
|
||||||
|
|
||||||
ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser
|
ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser
|
||||||
ENV SERVER_PORT=5000
|
|
||||||
|
|
||||||
COPY --from=build /app/src/templates /app/src/templates
|
COPY --from=build /app/src/templates /app/src/templates
|
||||||
COPY --from=build /app/dist /app/dist
|
COPY --from=build /app/dist /app/dist
|
||||||
COPY --from=build /app/node_modules /app/node_modules
|
COPY --from=build /app/node_modules /app/node_modules
|
||||||
COPY --from=build /app/package.json /app/package.json
|
COPY --from=build /app/package.json /app/package.json
|
||||||
|
|
||||||
EXPOSE ${SERVER_PORT}
|
EXPOSE 5000
|
||||||
|
|
||||||
CMD [ "npm", "run", "start" ]
|
CMD [ "npm", "run", "start" ]
|
17
README.md
17
README.md
|
@ -6,7 +6,7 @@ Administration für Feuerwehren und Vereine (Backend).
|
||||||
|
|
||||||
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.
|
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 `ff-admin` 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
|
## Installation
|
||||||
|
|
||||||
|
@ -25,21 +25,22 @@ services:
|
||||||
container_name: ff_member_administration_server
|
container_name: ff_member_administration_server
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
- DB_TYPE=mysql
|
- DB_TYPE=<mysql|sqlite> # default ist auf mysql gesetzt
|
||||||
- DB_HOST=ffm-db
|
- DB_HOST=ffm-db
|
||||||
- DB_PORT=<number> # fallback ist auf 3306 gesetzt
|
- DB_PORT=<number> # default ist auf 3306 gesetzt
|
||||||
- DB_NAME=ffadmin
|
- DB_NAME=ffadmin
|
||||||
- DB_USERNAME=administration_backend
|
- DB_USERNAME=administration_backend
|
||||||
- DB_PASSWORD=<dbuserpasswd>
|
- DB_PASSWORD=<dbuserpasswd>
|
||||||
- JWT_SECRET=<tobemodified>
|
- JWT_SECRET=<tobemodified>
|
||||||
- JWT_EXPIRATION=<number[m|d] - bsp.:15m>
|
- JWT_EXPIRATION=<number[m|d] - bsp.:15m> # default ist auf 15m gesetzt
|
||||||
- REFRESH_EXPIRATION=<number[m|d] - bsp.:1d>
|
- 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_USERNAME=<mailadress|username>
|
||||||
- MAIL_PASSWORD=<password>
|
- MAIL_PASSWORD=<password>
|
||||||
- MAIL_HOST=<url>
|
- MAIL_HOST=<url>
|
||||||
- MAIL_PORT=<port>
|
- MAIL_PORT=<port> # default ist auf 578 gesetzt
|
||||||
- MAIL_SECURE=<boolean>
|
- MAIL_SECURE=<boolean> # default ist auf false gesetzt
|
||||||
- CLUB_NAME=<tobemodified>
|
- CLUB_NAME=<tobemodified> # default ist auf FF Admin gesetzt
|
||||||
- CLUB_WEBSITE=<tobemodified>
|
- CLUB_WEBSITE=<tobemodified>
|
||||||
volumes:
|
volumes:
|
||||||
- <volume|local path>:/app/files
|
- <volume|local path>:/app/files
|
||||||
|
|
52
package-lock.json
generated
52
package-lock.json
generated
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "ff-admin-server",
|
"name": "ff-admin-server",
|
||||||
"version": "1.0.0",
|
"version": "1.2.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "ff-admin-server",
|
"name": "ff-admin-server",
|
||||||
"version": "1.0.0",
|
"version": "1.2.0",
|
||||||
"license": "GPL-3.0-only",
|
"license": "GPL-3.0-only",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
|
@ -24,6 +24,7 @@
|
||||||
"puppeteer": "^23.11.1",
|
"puppeteer": "^23.11.1",
|
||||||
"qrcode": "^1.5.4",
|
"qrcode": "^1.5.4",
|
||||||
"reflect-metadata": "^0.2.2",
|
"reflect-metadata": "^0.2.2",
|
||||||
|
"rss-parser": "^3.13.0",
|
||||||
"socket.io": "^4.7.5",
|
"socket.io": "^4.7.5",
|
||||||
"speakeasy": "^2.0.0",
|
"speakeasy": "^2.0.0",
|
||||||
"typeorm": "^0.3.20",
|
"typeorm": "^0.3.20",
|
||||||
|
@ -1496,6 +1497,15 @@
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
},
|
},
|
||||||
|
"node_modules/entities": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
|
||||||
|
"license": "BSD-2-Clause",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/env-paths": {
|
"node_modules/env-paths": {
|
||||||
"version": "2.2.1",
|
"version": "2.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
|
||||||
|
@ -3152,6 +3162,16 @@
|
||||||
"node": ">= 0.10"
|
"node": ">= 0.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/rss-parser": {
|
||||||
|
"version": "3.13.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/rss-parser/-/rss-parser-3.13.0.tgz",
|
||||||
|
"integrity": "sha512-7jWUBV5yGN3rqMMj7CZufl/291QAhvrrGpDNE4k/02ZchL0npisiYYqULF71jCEKoIiHvK/Q2e6IkDwPziT7+w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"entities": "^2.0.3",
|
||||||
|
"xml2js": "^0.5.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/runes2": {
|
"node_modules/runes2": {
|
||||||
"version": "1.1.4",
|
"version": "1.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/runes2/-/runes2-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/runes2/-/runes2-1.1.4.tgz",
|
||||||
|
@ -3182,6 +3202,12 @@
|
||||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||||
},
|
},
|
||||||
|
"node_modules/sax": {
|
||||||
|
"version": "1.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/sax/-/sax-1.4.1.tgz",
|
||||||
|
"integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/semver": {
|
"node_modules/semver": {
|
||||||
"version": "7.6.3",
|
"version": "7.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
|
||||||
|
@ -4253,6 +4279,28 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/xml2js": {
|
||||||
|
"version": "0.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz",
|
||||||
|
"integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"sax": ">=0.6.0",
|
||||||
|
"xmlbuilder": "~11.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/xmlbuilder": {
|
||||||
|
"version": "11.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
|
||||||
|
"integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/y18n": {
|
"node_modules/y18n": {
|
||||||
"version": "4.0.3",
|
"version": "4.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "ff-admin-server",
|
"name": "ff-admin-server",
|
||||||
"version": "1.0.0",
|
"version": "1.2.0",
|
||||||
"description": "Feuerwehr/Verein Mitgliederverwaltung Server",
|
"description": "Feuerwehr/Verein Mitgliederverwaltung Server",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -30,10 +30,8 @@
|
||||||
"handlebars": "^4.7.8",
|
"handlebars": "^4.7.8",
|
||||||
"ics": "^3.8.1",
|
"ics": "^3.8.1",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"knex": "^3.1.0",
|
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
"ms": "^2.1.3",
|
"ms": "^2.1.3",
|
||||||
"mssql": "^10.0.4",
|
|
||||||
"mysql": "^2.18.1",
|
"mysql": "^2.18.1",
|
||||||
"node-schedule": "^2.1.1",
|
"node-schedule": "^2.1.1",
|
||||||
"nodemailer": "^6.9.14",
|
"nodemailer": "^6.9.14",
|
||||||
|
@ -41,6 +39,7 @@
|
||||||
"puppeteer": "^23.11.1",
|
"puppeteer": "^23.11.1",
|
||||||
"qrcode": "^1.5.4",
|
"qrcode": "^1.5.4",
|
||||||
"reflect-metadata": "^0.2.2",
|
"reflect-metadata": "^0.2.2",
|
||||||
|
"rss-parser": "^3.13.0",
|
||||||
"socket.io": "^4.7.5",
|
"socket.io": "^4.7.5",
|
||||||
"speakeasy": "^2.0.0",
|
"speakeasy": "^2.0.0",
|
||||||
"typeorm": "^0.3.20",
|
"typeorm": "^0.3.20",
|
||||||
|
|
|
@ -33,7 +33,10 @@ export default abstract class MemberCommandHandler {
|
||||||
return result.identifiers[0].id;
|
return result.identifiers[0].id;
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
throw new InternalException("Failed creating member", err);
|
throw new InternalException(
|
||||||
|
`Failed creating member${err.code.includes("ER_DUP_ENTRY") ? " due to duplicate entry for column" : ""}`,
|
||||||
|
err
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,7 +46,6 @@ export default abstract class MemberCommandHandler {
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
static async update(updateMember: UpdateMemberCommand): Promise<void> {
|
static async update(updateMember: UpdateMemberCommand): Promise<void> {
|
||||||
console.log(updateMember);
|
|
||||||
return await dataSource
|
return await dataSource
|
||||||
.createQueryBuilder()
|
.createQueryBuilder()
|
||||||
.update(member)
|
.update(member)
|
||||||
|
@ -59,7 +61,10 @@ export default abstract class MemberCommandHandler {
|
||||||
.execute()
|
.execute()
|
||||||
.then(() => {})
|
.then(() => {})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
throw new InternalException("Failed updating member", err);
|
throw new InternalException(
|
||||||
|
`Failed updating member${err.code.includes("ER_DUP_ENTRY") ? " due to duplicate entry for column" : ""}`,
|
||||||
|
err
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,7 +89,7 @@ export default abstract class MemberCommandHandler {
|
||||||
.execute()
|
.execute()
|
||||||
.then(() => {})
|
.then(() => {})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
throw new InternalException("Failed updating member", err);
|
throw new InternalException(`Failed updating member`, err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ export default abstract class MembershipCommandHandler {
|
||||||
.createQueryBuilder()
|
.createQueryBuilder()
|
||||||
.update(membership)
|
.update(membership)
|
||||||
.set({
|
.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.",
|
terminationReason: "beendet durch neuen Eintrag.",
|
||||||
})
|
})
|
||||||
.where("end IS NULL")
|
.where("end IS NULL")
|
||||||
|
|
|
@ -6,4 +6,5 @@ export interface SynchronizeProtocolPresenceCommand {
|
||||||
export interface ProtocolPresenceCommand {
|
export interface ProtocolPresenceCommand {
|
||||||
memberId: number;
|
memberId: number;
|
||||||
absent: boolean;
|
absent: boolean;
|
||||||
|
excused: boolean;
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,6 +74,7 @@ export default abstract class ProtocolPresenceCommandHandler {
|
||||||
.update(protocolPresence)
|
.update(protocolPresence)
|
||||||
.set({
|
.set({
|
||||||
absent: member.absent,
|
absent: member.absent,
|
||||||
|
excused: member.excused,
|
||||||
})
|
})
|
||||||
.where("memberId = :memberId", { memberId: member.memberId })
|
.where("memberId = :memberId", { memberId: member.memberId })
|
||||||
.andWhere("protocolId = :protocolId", { protocolId })
|
.andWhere("protocolId = :protocolId", { protocolId })
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
export interface CreateRefreshCommand {
|
export interface CreateRefreshCommand {
|
||||||
userId: number;
|
userId: number;
|
||||||
|
isFromPwa?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DeleteRefreshCommand {
|
export interface DeleteRefreshCommand {
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
import { dataSource } from "../data-source";
|
import { dataSource } from "../data-source";
|
||||||
import { refresh } from "../entity/refresh";
|
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 InternalException from "../exceptions/internalException";
|
||||||
import { JWTHelper } from "../helpers/jwtHelper";
|
|
||||||
import { StringHelper } from "../helpers/stringHelper";
|
import { StringHelper } from "../helpers/stringHelper";
|
||||||
import UserService from "../service/user/userService";
|
import UserService from "../service/user/userService";
|
||||||
import { JWTRefresh } from "../type/jwtTypes";
|
|
||||||
import { CreateRefreshCommand, DeleteRefreshCommand } from "./refreshCommand";
|
import { CreateRefreshCommand, DeleteRefreshCommand } from "./refreshCommand";
|
||||||
import ms from "ms";
|
import ms from "ms";
|
||||||
|
|
||||||
|
@ -16,10 +14,6 @@ export default abstract class RefreshCommandHandler {
|
||||||
* @returns {Promise<string>}
|
* @returns {Promise<string>}
|
||||||
*/
|
*/
|
||||||
static async create(createRefresh: CreateRefreshCommand): 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);
|
const refreshToken = StringHelper.random(32);
|
||||||
|
|
||||||
return await dataSource
|
return await dataSource
|
||||||
|
@ -29,7 +23,9 @@ export default abstract class RefreshCommandHandler {
|
||||||
.values({
|
.values({
|
||||||
token: refreshToken,
|
token: refreshToken,
|
||||||
user: await UserService.getById(createRefresh.userId),
|
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()
|
.execute()
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
|
|
|
@ -60,7 +60,10 @@ export default abstract class AwardCommandHandler {
|
||||||
.execute()
|
.execute()
|
||||||
.then(() => {})
|
.then(() => {})
|
||||||
.catch((err) => {
|
.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()
|
.execute()
|
||||||
.then(() => {})
|
.then(() => {})
|
||||||
.catch((err) => {
|
.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()
|
.execute()
|
||||||
.then(() => {})
|
.then(() => {})
|
||||||
.catch((err) => {
|
.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()
|
.execute()
|
||||||
.then(() => {})
|
.then(() => {})
|
||||||
.catch((err) => {
|
.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()
|
.execute()
|
||||||
.then(() => {})
|
.then(() => {})
|
||||||
.catch((err) => {
|
.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;
|
comTypeId: number;
|
||||||
config: NewsletterConfigType;
|
config: NewsletterConfigType;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DeleteNewsletterConfigCommand {
|
|
||||||
comTypeId: number;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { dataSource } from "../../../data-source";
|
import { dataSource } from "../../../data-source";
|
||||||
import { newsletterConfig } from "../../../entity/settings/newsletterConfig";
|
import { newsletterConfig } from "../../../entity/settings/newsletterConfig";
|
||||||
import InternalException from "../../../exceptions/internalException";
|
import InternalException from "../../../exceptions/internalException";
|
||||||
import { DeleteNewsletterConfigCommand, SetNewsletterConfigCommand } from "./newsletterConfigCommand";
|
import { SetNewsletterConfigCommand } from "./newsletterConfigCommand";
|
||||||
|
|
||||||
export default abstract class NewsletterConfigCommandHandler {
|
export default abstract class NewsletterConfigCommandHandler {
|
||||||
/**
|
/**
|
||||||
|
@ -27,21 +27,4 @@ export default abstract class NewsletterConfigCommandHandler {
|
||||||
throw new InternalException("Failed setting newsletterConfig", err);
|
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()
|
.execute()
|
||||||
.then(() => {})
|
.then(() => {})
|
||||||
.catch((err) => {
|
.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()
|
.execute()
|
||||||
.then(() => {})
|
.then(() => {})
|
||||||
.catch((err) => {
|
.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()
|
.execute()
|
||||||
.then(() => {})
|
.then(() => {})
|
||||||
.catch((err) => {
|
.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
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
19
src/command/user/webapi/webapiCommand.ts
Normal file
19
src/command/user/webapi/webapiCommand.ts
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
export interface CreateWebapiCommand {
|
||||||
|
title: string;
|
||||||
|
token: string;
|
||||||
|
expiry?: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateWebapiCommand {
|
||||||
|
id: number;
|
||||||
|
title: string;
|
||||||
|
expiry?: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateLastUsageWebapiCommand {
|
||||||
|
id: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DeleteWebapiCommand {
|
||||||
|
id: number;
|
||||||
|
}
|
100
src/command/user/webapi/webapiCommandHandler.ts
Normal file
100
src/command/user/webapi/webapiCommandHandler.ts
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
import { dataSource } from "../../../data-source";
|
||||||
|
import { webapi } from "../../../entity/user/webapi";
|
||||||
|
import InternalException from "../../../exceptions/internalException";
|
||||||
|
import {
|
||||||
|
CreateWebapiCommand,
|
||||||
|
DeleteWebapiCommand,
|
||||||
|
UpdateLastUsageWebapiCommand,
|
||||||
|
UpdateWebapiCommand,
|
||||||
|
} from "./webapiCommand";
|
||||||
|
|
||||||
|
export default abstract class WebapiCommandHandler {
|
||||||
|
/**
|
||||||
|
* @description create api
|
||||||
|
* @param {CreateWebapiCommand} createWebapi
|
||||||
|
* @returns {Promise<number>}
|
||||||
|
*/
|
||||||
|
static async create(createWebapi: CreateWebapiCommand): Promise<number> {
|
||||||
|
return await dataSource
|
||||||
|
.createQueryBuilder()
|
||||||
|
.insert()
|
||||||
|
.into(webapi)
|
||||||
|
.values({
|
||||||
|
token: createWebapi.token,
|
||||||
|
title: createWebapi.title,
|
||||||
|
expiry: createWebapi.expiry,
|
||||||
|
})
|
||||||
|
.execute()
|
||||||
|
.then((result) => {
|
||||||
|
return result.identifiers[0].token;
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
throw new InternalException(
|
||||||
|
`Failed creating api${err.code.includes("ER_DUP_ENTRY") ? " due to duplicate entry for column" : ""}`,
|
||||||
|
err
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description update api
|
||||||
|
* @param {UpdateWebapiCommand} updateWebapi
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
static async update(updateWebapi: UpdateWebapiCommand): Promise<void> {
|
||||||
|
return await dataSource
|
||||||
|
.createQueryBuilder()
|
||||||
|
.update(webapi)
|
||||||
|
.set({
|
||||||
|
title: updateWebapi.title,
|
||||||
|
expiry: updateWebapi.expiry,
|
||||||
|
})
|
||||||
|
.where("id = :id", { id: updateWebapi.id })
|
||||||
|
.execute()
|
||||||
|
.then(() => {})
|
||||||
|
.catch((err) => {
|
||||||
|
throw new InternalException(
|
||||||
|
`Failed updating api${err.code.includes("ER_DUP_ENTRY") ? " due to duplicate entry for column" : ""}`,
|
||||||
|
err
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description update api usage
|
||||||
|
* @param {UpdateLastUsageWebapiCommand} updateWebapi
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
static async updateUsage(updateWebapi: UpdateLastUsageWebapiCommand): Promise<void> {
|
||||||
|
return await dataSource
|
||||||
|
.createQueryBuilder()
|
||||||
|
.update(webapi)
|
||||||
|
.set({
|
||||||
|
lastUsage: new Date(),
|
||||||
|
})
|
||||||
|
.where("id = :id", { id: updateWebapi.id })
|
||||||
|
.execute()
|
||||||
|
.then(() => {})
|
||||||
|
.catch((err) => {
|
||||||
|
throw new InternalException(`Failed updating api last usage`, err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description delete api
|
||||||
|
* @param {DeleteWebapiCommand} deleteWebapi
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
static async delete(deleteWebapi: DeleteWebapiCommand): Promise<void> {
|
||||||
|
return await dataSource
|
||||||
|
.createQueryBuilder()
|
||||||
|
.delete()
|
||||||
|
.from(webapi)
|
||||||
|
.where("id = :id", { id: deleteWebapi.id })
|
||||||
|
.execute()
|
||||||
|
.then(() => {})
|
||||||
|
.catch((err) => {
|
||||||
|
throw new InternalException("Failed deleting api", err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
16
src/command/user/webapi/webapiPermissionCommand.ts
Normal file
16
src/command/user/webapi/webapiPermissionCommand.ts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import { PermissionString } from "../../../type/permissionTypes";
|
||||||
|
|
||||||
|
export interface CreateWebapiPermissionCommand {
|
||||||
|
permission: PermissionString;
|
||||||
|
webapiId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DeleteWebapiPermissionCommand {
|
||||||
|
permission: PermissionString;
|
||||||
|
webapiId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateWebapiPermissionsCommand {
|
||||||
|
webapiId: number;
|
||||||
|
permissions: Array<PermissionString>;
|
||||||
|
}
|
119
src/command/user/webapi/webapiPermissionCommandHandler.ts
Normal file
119
src/command/user/webapi/webapiPermissionCommandHandler.ts
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
import { DeleteResult, EntityManager, InsertResult } from "typeorm";
|
||||||
|
import { dataSource } from "../../../data-source";
|
||||||
|
import { webapiPermission } from "../../../entity/user/webapi_permission";
|
||||||
|
import InternalException from "../../../exceptions/internalException";
|
||||||
|
import WebapiService from "../../../service/user/webapiService";
|
||||||
|
import {
|
||||||
|
CreateWebapiPermissionCommand,
|
||||||
|
DeleteWebapiPermissionCommand,
|
||||||
|
UpdateWebapiPermissionsCommand,
|
||||||
|
} from "./webapiPermissionCommand";
|
||||||
|
import PermissionHelper from "../../../helpers/permissionHelper";
|
||||||
|
import WebapiPermissionService from "../../../service/user/webapiPermissionService";
|
||||||
|
import { PermissionString } from "../../../type/permissionTypes";
|
||||||
|
|
||||||
|
export default abstract class WebapiPermissionCommandHandler {
|
||||||
|
/**
|
||||||
|
* @description update api permissions
|
||||||
|
* @param {UpdateWebapiPermissionsCommand} updateWebapiPermissions
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
static async updatePermissions(updateWebapiPermissions: UpdateWebapiPermissionsCommand): Promise<void> {
|
||||||
|
let currentPermissions = (await WebapiPermissionService.getByApi(updateWebapiPermissions.webapiId)).map(
|
||||||
|
(r) => r.permission
|
||||||
|
);
|
||||||
|
return await dataSource.manager
|
||||||
|
.transaction(async (manager) => {
|
||||||
|
let newPermissions = PermissionHelper.getWhatToAdd(currentPermissions, updateWebapiPermissions.permissions);
|
||||||
|
let removePermissions = PermissionHelper.getWhatToRemove(
|
||||||
|
currentPermissions,
|
||||||
|
updateWebapiPermissions.permissions
|
||||||
|
);
|
||||||
|
if (newPermissions.length != 0) {
|
||||||
|
await this.updatePermissionsAdd(manager, updateWebapiPermissions.webapiId, newPermissions);
|
||||||
|
}
|
||||||
|
if (removePermissions.length != 0) {
|
||||||
|
await this.updatePermissionsRemove(manager, updateWebapiPermissions.webapiId, removePermissions);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(() => {})
|
||||||
|
.catch((err) => {
|
||||||
|
throw new InternalException("Failed saving api permissions", err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async updatePermissionsAdd(
|
||||||
|
manager: EntityManager,
|
||||||
|
webapiId: number,
|
||||||
|
permissions: Array<PermissionString>
|
||||||
|
): Promise<InsertResult> {
|
||||||
|
return await manager
|
||||||
|
.createQueryBuilder()
|
||||||
|
.insert()
|
||||||
|
.into(webapiPermission)
|
||||||
|
.values(
|
||||||
|
permissions.map((p) => ({
|
||||||
|
permission: p,
|
||||||
|
webapiId: webapiId,
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
.orIgnore()
|
||||||
|
.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async updatePermissionsRemove(
|
||||||
|
manager: EntityManager,
|
||||||
|
webapiId: number,
|
||||||
|
permissions: Array<PermissionString>
|
||||||
|
): Promise<DeleteResult> {
|
||||||
|
return await manager
|
||||||
|
.createQueryBuilder()
|
||||||
|
.delete()
|
||||||
|
.from(webapiPermission)
|
||||||
|
.where("webapiId = :id", { id: webapiId })
|
||||||
|
.andWhere("permission IN (:...permission)", { permission: permissions })
|
||||||
|
.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description grant permission to user
|
||||||
|
* @param {CreateWebapiPermissionCommand} createPermission
|
||||||
|
* @returns {Promise<number>}
|
||||||
|
*/
|
||||||
|
static async create(createPermission: CreateWebapiPermissionCommand): Promise<number> {
|
||||||
|
return await dataSource
|
||||||
|
.createQueryBuilder()
|
||||||
|
.insert()
|
||||||
|
.into(webapiPermission)
|
||||||
|
.values({
|
||||||
|
permission: createPermission.permission,
|
||||||
|
webapiId: createPermission.webapiId,
|
||||||
|
})
|
||||||
|
.execute()
|
||||||
|
.then((result) => {
|
||||||
|
return result.identifiers[0].id;
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
throw new InternalException("Failed saving api permission", err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description remove permission from api
|
||||||
|
* @param {DeleteWebapiPermissionCommand} deletePermission
|
||||||
|
* @returns {Promise<any>}
|
||||||
|
*/
|
||||||
|
static async delete(deletePermission: DeleteWebapiPermissionCommand): Promise<any> {
|
||||||
|
return await dataSource
|
||||||
|
.createQueryBuilder()
|
||||||
|
.delete()
|
||||||
|
.from(webapiPermission)
|
||||||
|
.where("webapiId = :id", { id: deletePermission.webapiId })
|
||||||
|
.andWhere("permission = :permission", { permission: deletePermission.permission })
|
||||||
|
.execute()
|
||||||
|
.then(() => {})
|
||||||
|
.catch((err) => {
|
||||||
|
throw new InternalException("failed api permission removal", err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -48,8 +48,8 @@ import {
|
||||||
UpdateCommunicationCommand,
|
UpdateCommunicationCommand,
|
||||||
} from "../../../command/club/member/communicationCommand";
|
} from "../../../command/club/member/communicationCommand";
|
||||||
import CommunicationCommandHandler from "../../../command/club/member/communicationCommandHandler";
|
import CommunicationCommandHandler from "../../../command/club/member/communicationCommandHandler";
|
||||||
import {PdfExport} from "../../../helpers/pdfExport";
|
import { PdfExport } from "../../../helpers/pdfExport";
|
||||||
import {PermissionModule} from "../../../type/permissionTypes";
|
import { PermissionModule } from "../../../type/permissionTypes";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description get all members
|
* @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 offset = parseInt((req.query.offset as string) ?? "0");
|
||||||
let count = parseInt((req.query.count as string) ?? "25");
|
let count = parseInt((req.query.count as string) ?? "25");
|
||||||
let search = (req.query.search as string) ?? "";
|
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({
|
res.json({
|
||||||
members: MemberFactory.mapToBase(members),
|
members: MemberFactory.mapToBase(members),
|
||||||
|
@ -85,6 +90,19 @@ export async function getMemberById(req: Request, res: Response): Promise<any> {
|
||||||
res.json(MemberFactory.mapToSingle(member));
|
res.json(MemberFactory.mapToSingle(member));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description get member statistics by id
|
||||||
|
* @param req {Request} Express req object
|
||||||
|
* @param res {Response} Express res object
|
||||||
|
* @returns {Promise<*>}
|
||||||
|
*/
|
||||||
|
export async function getMemberStatisticsById(req: Request, res: Response): Promise<any> {
|
||||||
|
const memberId = parseInt(req.params.id);
|
||||||
|
let member = await MemberService.getStatisticsById(memberId);
|
||||||
|
|
||||||
|
res.json(MemberFactory.mapToMemberStatistic(member));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description get memberships by member
|
* @description get memberships by member
|
||||||
* @param req {Request} Express req object
|
* @param req {Request} Express req object
|
||||||
|
@ -98,6 +116,19 @@ export async function getMembershipsByMember(req: Request, res: Response): Promi
|
||||||
res.json(MembershipFactory.mapToBase(memberships));
|
res.json(MembershipFactory.mapToBase(memberships));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description get member statistics by id
|
||||||
|
* @param req {Request} Express req object
|
||||||
|
* @param res {Response} Express res object
|
||||||
|
* @returns {Promise<*>}
|
||||||
|
*/
|
||||||
|
export async function getMembershipStatisticsById(req: Request, res: Response): Promise<any> {
|
||||||
|
const memberId = parseInt(req.params.memberId);
|
||||||
|
let member = await MembershipService.getStatisticsById(memberId);
|
||||||
|
|
||||||
|
res.json(MembershipFactory.mapToBaseStatistics(member));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description get membership by member and record
|
* @description get membership by member and record
|
||||||
* @param req {Request} Express req object
|
* @param req {Request} Express req object
|
||||||
|
@ -227,14 +258,14 @@ export async function getCommunicationByMemberAndRecord(req: Request, res: Respo
|
||||||
* @returns {Promise<*>}
|
* @returns {Promise<*>}
|
||||||
*/
|
*/
|
||||||
export async function createMemberPrintoutList(req: Request, res: Response): Promise<any> {
|
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({
|
let pdf = await PdfExport.renderFile({
|
||||||
title: "Mitgliederliste",
|
title: "Mitgliederliste",
|
||||||
template: "member.list",
|
template: "member.list",
|
||||||
saveToDisk: false,
|
saveToDisk: false,
|
||||||
data: {
|
data: {
|
||||||
member: members
|
member: members,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -596,7 +627,7 @@ export async function updateCommunicationOfMember(req: Request, res: Response):
|
||||||
communicationId: recordId,
|
communicationId: recordId,
|
||||||
};
|
};
|
||||||
await MemberCommandHandler.updateNewsletter(updateNewsletter);
|
await MemberCommandHandler.updateNewsletter(updateNewsletter);
|
||||||
} else if (currentUserNewsletterMain.sendNewsletter.id == recordId) {
|
} else if (currentUserNewsletterMain.sendNewsletter?.id == recordId) {
|
||||||
await MemberCommandHandler.unsetNewsletter(memberId);
|
await MemberCommandHandler.unsetNewsletter(memberId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,10 @@ import NewsletterDatesFactory from "../../../factory/admin/club/newsletter/newsl
|
||||||
import NewsletterRecipientsService from "../../../service/club/newsletter/newsletterRecipientsService";
|
import NewsletterRecipientsService from "../../../service/club/newsletter/newsletterRecipientsService";
|
||||||
import NewsletterRecipientsFactory from "../../../factory/admin/club/newsletter/newsletterRecipients";
|
import NewsletterRecipientsFactory from "../../../factory/admin/club/newsletter/newsletterRecipients";
|
||||||
import { FileSystemHelper } from "../../../helpers/fileSystemHelper";
|
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 NewsletterCommandHandler from "../../../command/club/newsletter/newsletterCommandHandler";
|
||||||
import { SynchronizeNewsletterDatesCommand } from "../../../command/club/newsletter/newsletterDatesCommand";
|
import { SynchronizeNewsletterDatesCommand } from "../../../command/club/newsletter/newsletterDatesCommand";
|
||||||
import NewsletterDatesCommandHandler from "../../../command/club/newsletter/newsletterDatesCommandHandler";
|
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> {
|
export async function getNewsletterPrintoutProgressById(req: Request, res: Response): Promise<any> {
|
||||||
let newsletterId = parseInt(req.params.newsletterId);
|
let newsletterId = parseInt(req.params.newsletterId);
|
||||||
|
|
||||||
res.setHeader("Content-Type", "text/event-stream");
|
res.writeHead(200, {
|
||||||
res.setHeader("Cache-Control", "no-cache");
|
"Content-Type": "text/plain",
|
||||||
res.setHeader("Connection", "keep-alive");
|
"Transfer-Encoding": "chunked",
|
||||||
|
});
|
||||||
res.flushHeaders();
|
|
||||||
|
|
||||||
const progressHandler = (data: NewsletterEventType) => {
|
const progressHandler = (data: NewsletterEventType) => {
|
||||||
if (data.newsletterId == newsletterId && data.kind == "pdf") {
|
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> {
|
export async function getNewsletterSendingProgressById(req: Request, res: Response): Promise<any> {
|
||||||
let newsletterId = parseInt(req.params.newsletterId);
|
let newsletterId = parseInt(req.params.newsletterId);
|
||||||
|
|
||||||
res.setHeader("Content-Type", "text/event-stream");
|
res.writeHead(200, {
|
||||||
res.setHeader("Cache-Control", "no-cache");
|
"Content-Type": "text/plain",
|
||||||
res.setHeader("Connection", "keep-alive");
|
"Transfer-Encoding": "chunked",
|
||||||
|
});
|
||||||
res.flushHeaders();
|
|
||||||
|
|
||||||
const progressHandler = (data: NewsletterEventType) => {
|
const progressHandler = (data: NewsletterEventType) => {
|
||||||
if (data.newsletterId == newsletterId && data.kind == "mail") {
|
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,
|
agenda,
|
||||||
decisions,
|
decisions,
|
||||||
presence: presence.filter((p) => !p.absent).map((p) => p.member),
|
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,
|
votings,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -389,6 +391,7 @@ export async function synchronizeProtocolPrecenseById(req: Request, res: Respons
|
||||||
members: presence.map((p) => ({
|
members: presence.map((p) => ({
|
||||||
memberId: p.memberId,
|
memberId: p.memberId,
|
||||||
absent: p.absent,
|
absent: p.absent,
|
||||||
|
excused: p.excused,
|
||||||
})),
|
})),
|
||||||
protocolId,
|
protocolId,
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { Request, Response } from "express";
|
||||||
import NewsletterConfigService from "../../../service/settings/newsletterConfigService";
|
import NewsletterConfigService from "../../../service/settings/newsletterConfigService";
|
||||||
import NewsletterConfigFactory from "../../../factory/admin/settings/newsletterConfig";
|
import NewsletterConfigFactory from "../../../factory/admin/settings/newsletterConfig";
|
||||||
import NewsletterConfigCommandHandler from "../../../command/settings/newsletterConfig/newsletterConfigCommandHandler";
|
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
|
* @description get all newsletterConfigs
|
||||||
|
@ -47,20 +47,3 @@ export async function setNewsletterConfig(req: Request, res: Response): Promise<
|
||||||
|
|
||||||
res.send(id);
|
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);
|
|
||||||
}
|
|
||||||
|
|
155
src/controller/admin/user/webapiController.ts
Normal file
155
src/controller/admin/user/webapiController.ts
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
import { Request, Response } from "express";
|
||||||
|
import WebapiService from "../../../service/user/webapiService";
|
||||||
|
import ApiFactory from "../../../factory/admin/user/webapi";
|
||||||
|
import WebapiPermissionService from "../../../service/user/webapiPermissionService";
|
||||||
|
import PermissionHelper from "../../../helpers/permissionHelper";
|
||||||
|
import {
|
||||||
|
CreateWebapiCommand,
|
||||||
|
DeleteWebapiCommand,
|
||||||
|
UpdateWebapiCommand,
|
||||||
|
} from "../../../command/user/webapi/webapiCommand";
|
||||||
|
import WebapiCommandHandler from "../../../command/user/webapi/webapiCommandHandler";
|
||||||
|
import { UpdateWebapiPermissionsCommand } from "../../../command/user/webapi/webapiPermissionCommand";
|
||||||
|
import WebapiPermissionCommandHandler from "../../../command/user/webapi/webapiPermissionCommandHandler";
|
||||||
|
import { JWTHelper } from "../../../helpers/jwtHelper";
|
||||||
|
import { CLUB_NAME } from "../../../env.defaults";
|
||||||
|
import { StringHelper } from "../../../helpers/stringHelper";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description get All apis
|
||||||
|
* @param req {Request} Express req object
|
||||||
|
* @param res {Response} Express res object
|
||||||
|
* @returns {Promise<*>}
|
||||||
|
*/
|
||||||
|
export async function getAllWebapis(req: Request, res: Response): Promise<any> {
|
||||||
|
let apis = await WebapiService.getAll();
|
||||||
|
|
||||||
|
res.json(ApiFactory.mapToBase(apis));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description get api by id
|
||||||
|
* @param req {Request} Express req object
|
||||||
|
* @param res {Response} Express res object
|
||||||
|
* @returns {Promise<*>}
|
||||||
|
*/
|
||||||
|
export async function getWebapiById(req: Request, res: Response): Promise<any> {
|
||||||
|
const id = parseInt(req.params.id);
|
||||||
|
let api = await WebapiService.getById(id);
|
||||||
|
|
||||||
|
res.json(ApiFactory.mapToSingle(api));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description get api token by id
|
||||||
|
* @param req {Request} Express req object
|
||||||
|
* @param res {Response} Express res object
|
||||||
|
* @returns {Promise<*>}
|
||||||
|
*/
|
||||||
|
export async function getWebapiTokenById(req: Request, res: Response): Promise<any> {
|
||||||
|
const id = parseInt(req.params.id);
|
||||||
|
let { token } = await WebapiService.getTokenById(id);
|
||||||
|
|
||||||
|
res.send(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description get permissions by api
|
||||||
|
* @param req {Request} Express req object
|
||||||
|
* @param res {Response} Express res object
|
||||||
|
* @returns {Promise<*>}
|
||||||
|
*/
|
||||||
|
export async function getWebapiPermissions(req: Request, res: Response): Promise<any> {
|
||||||
|
const id = parseInt(req.params.id);
|
||||||
|
let permissions = await WebapiPermissionService.getByApi(id);
|
||||||
|
|
||||||
|
res.json(PermissionHelper.convertToObject(permissions.map((p) => p.permission)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description create new api
|
||||||
|
* @param req {Request} Express req object
|
||||||
|
* @param res {Response} Express res object
|
||||||
|
* @returns {Promise<*>}
|
||||||
|
*/
|
||||||
|
export async function createWebapi(req: Request, res: Response): Promise<any> {
|
||||||
|
let title = req.body.title;
|
||||||
|
let expiry = req.body.expiry || null;
|
||||||
|
|
||||||
|
let token = await JWTHelper.create(
|
||||||
|
{
|
||||||
|
iss: CLUB_NAME,
|
||||||
|
sub: "api_token_retrieve",
|
||||||
|
aud: StringHelper.random(32),
|
||||||
|
},
|
||||||
|
{ useExpiration: false }
|
||||||
|
);
|
||||||
|
|
||||||
|
let createApi: CreateWebapiCommand = {
|
||||||
|
token: token,
|
||||||
|
title: title,
|
||||||
|
expiry: expiry,
|
||||||
|
};
|
||||||
|
await WebapiCommandHandler.create(createApi);
|
||||||
|
|
||||||
|
res.sendStatus(204);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description update api data
|
||||||
|
* @param req {Request} Express req object
|
||||||
|
* @param res {Response} Express res object
|
||||||
|
* @returns {Promise<*>}
|
||||||
|
*/
|
||||||
|
export async function updateWebapi(req: Request, res: Response): Promise<any> {
|
||||||
|
const id = parseInt(req.params.id);
|
||||||
|
let title = req.body.title;
|
||||||
|
let expiry = req.body.expiry || null;
|
||||||
|
|
||||||
|
let updateApi: UpdateWebapiCommand = {
|
||||||
|
id: id,
|
||||||
|
title: title,
|
||||||
|
expiry: expiry,
|
||||||
|
};
|
||||||
|
await WebapiCommandHandler.update(updateApi);
|
||||||
|
|
||||||
|
res.sendStatus(204);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description update api assigned permission strings
|
||||||
|
* @param req {Request} Express req object
|
||||||
|
* @param res {Response} Express res object
|
||||||
|
* @returns {Promise<*>}
|
||||||
|
*/
|
||||||
|
export async function updateWebapiPermissions(req: Request, res: Response): Promise<any> {
|
||||||
|
const id = parseInt(req.params.id);
|
||||||
|
let permissions = req.body.permissions;
|
||||||
|
|
||||||
|
let permissionStrings = PermissionHelper.convertToStringArray(permissions);
|
||||||
|
|
||||||
|
let updateApiPermissions: UpdateWebapiPermissionsCommand = {
|
||||||
|
webapiId: id,
|
||||||
|
permissions: permissionStrings,
|
||||||
|
};
|
||||||
|
await WebapiPermissionCommandHandler.updatePermissions(updateApiPermissions);
|
||||||
|
|
||||||
|
res.sendStatus(204);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description delete api by id
|
||||||
|
* @param req {Request} Express req object
|
||||||
|
* @param res {Response} Express res object
|
||||||
|
* @returns {Promise<*>}
|
||||||
|
*/
|
||||||
|
export async function deleteWebapi(req: Request, res: Response): Promise<any> {
|
||||||
|
const id = parseInt(req.params.id);
|
||||||
|
|
||||||
|
let deleteApi: DeleteWebapiCommand = {
|
||||||
|
id: id,
|
||||||
|
};
|
||||||
|
await WebapiCommandHandler.delete(deleteApi);
|
||||||
|
|
||||||
|
res.sendStatus(204);
|
||||||
|
}
|
|
@ -8,9 +8,6 @@ import UserService from "../service/user/userService";
|
||||||
import speakeasy from "speakeasy";
|
import speakeasy from "speakeasy";
|
||||||
import UnauthorizedRequestException from "../exceptions/unauthorizedRequestException";
|
import UnauthorizedRequestException from "../exceptions/unauthorizedRequestException";
|
||||||
import RefreshService from "../service/refreshService";
|
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
|
* @description Check authentication status by token
|
||||||
|
@ -39,6 +36,7 @@ export async function login(req: Request, res: Response): Promise<any> {
|
||||||
|
|
||||||
let refreshCommand: CreateRefreshCommand = {
|
let refreshCommand: CreateRefreshCommand = {
|
||||||
userId: id,
|
userId: id,
|
||||||
|
isFromPwa: req.isPWA,
|
||||||
};
|
};
|
||||||
let refreshToken = await RefreshCommandHandler.create(refreshCommand);
|
let refreshToken = await RefreshCommandHandler.create(refreshCommand);
|
||||||
|
|
||||||
|
@ -83,6 +81,7 @@ export async function refresh(req: Request, res: Response): Promise<any> {
|
||||||
|
|
||||||
let refreshCommand: CreateRefreshCommand = {
|
let refreshCommand: CreateRefreshCommand = {
|
||||||
userId: tokenUserId,
|
userId: tokenUserId,
|
||||||
|
isFromPwa: req.isPWA,
|
||||||
};
|
};
|
||||||
let refreshToken = await RefreshCommandHandler.create(refreshCommand);
|
let refreshToken = await RefreshCommandHandler.create(refreshCommand);
|
||||||
|
|
||||||
|
|
37
src/controller/webapiController.ts
Normal file
37
src/controller/webapiController.ts
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import { Request, Response } from "express";
|
||||||
|
import { JWTHelper } from "../helpers/jwtHelper";
|
||||||
|
import { JWTToken } from "../type/jwtTypes";
|
||||||
|
import InternalException from "../exceptions/internalException";
|
||||||
|
import RefreshCommandHandler from "../command/refreshCommandHandler";
|
||||||
|
import { CreateRefreshCommand, DeleteRefreshCommand } from "../command/refreshCommand";
|
||||||
|
import UserService from "../service/user/userService";
|
||||||
|
import speakeasy from "speakeasy";
|
||||||
|
import UnauthorizedRequestException from "../exceptions/unauthorizedRequestException";
|
||||||
|
import RefreshService from "../service/refreshService";
|
||||||
|
import WebapiService from "../service/user/webapiService";
|
||||||
|
import ForbiddenRequestException from "../exceptions/forbiddenRequestException";
|
||||||
|
import WebapiCommandHandler from "../command/user/webapi/webapiCommandHandler";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Check authentication status by token
|
||||||
|
* @param req {Request} Express req object
|
||||||
|
* @param res {Response} Express res object
|
||||||
|
* @returns {Promise<*>}
|
||||||
|
*/
|
||||||
|
export async function getWebApiAccess(req: Request, res: Response): Promise<any> {
|
||||||
|
const bearer = req.headers.authorization?.split(" ")?.[1] ?? undefined;
|
||||||
|
|
||||||
|
let { id, expiry } = await WebapiService.getByToken(bearer);
|
||||||
|
|
||||||
|
if (expiry != null && new Date() > new Date(expiry)) {
|
||||||
|
throw new ForbiddenRequestException("api token expired");
|
||||||
|
}
|
||||||
|
|
||||||
|
await WebapiCommandHandler.updateUsage({ id });
|
||||||
|
|
||||||
|
let accessToken = await JWTHelper.buildWebapiToken(bearer, expiry);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
accessToken,
|
||||||
|
});
|
||||||
|
}
|
|
@ -66,6 +66,11 @@ import { PostalCode1735927918979 } from "./migrations/1735927918979-postalCode";
|
||||||
import { ProtocolAbsent1736072179716 } from "./migrations/1736072179716-protocolAbsent";
|
import { ProtocolAbsent1736072179716 } from "./migrations/1736072179716-protocolAbsent";
|
||||||
import { Memberlist1736079005086 } from "./migrations/1736079005086-memberlist";
|
import { Memberlist1736079005086 } from "./migrations/1736079005086-memberlist";
|
||||||
import { ExtendViewValues1736084198860 } from "./migrations/1736084198860-extendViewValues";
|
import { ExtendViewValues1736084198860 } from "./migrations/1736084198860-extendViewValues";
|
||||||
|
import { FinishInternalIdTransfer1736505324488 } from "./migrations/1736505324488-finishInternalIdTransfer";
|
||||||
|
import { ProtocolPresenceExcuse1737287798828 } from "./migrations/1737287798828-protocolPresenceExcuse";
|
||||||
|
import { webapi } from "./entity/user/webapi";
|
||||||
|
import { webapiPermission } from "./entity/user/webapi_permission";
|
||||||
|
import { AddWebapiTokens1737453096674 } from "./migrations/1737453096674-addwebapiTokens";
|
||||||
|
|
||||||
const dataSource = new DataSource({
|
const dataSource = new DataSource({
|
||||||
type: DB_TYPE as any,
|
type: DB_TYPE as any,
|
||||||
|
@ -115,6 +120,8 @@ const dataSource = new DataSource({
|
||||||
memberExecutivePositionsView,
|
memberExecutivePositionsView,
|
||||||
memberQualificationsView,
|
memberQualificationsView,
|
||||||
membershipView,
|
membershipView,
|
||||||
|
webapi,
|
||||||
|
webapiPermission,
|
||||||
],
|
],
|
||||||
migrations: [
|
migrations: [
|
||||||
Initial1724317398939,
|
Initial1724317398939,
|
||||||
|
@ -142,6 +149,9 @@ const dataSource = new DataSource({
|
||||||
ProtocolAbsent1736072179716,
|
ProtocolAbsent1736072179716,
|
||||||
Memberlist1736079005086,
|
Memberlist1736079005086,
|
||||||
ExtendViewValues1736084198860,
|
ExtendViewValues1736084198860,
|
||||||
|
FinishInternalIdTransfer1736505324488,
|
||||||
|
ProtocolPresenceExcuse1737287798828,
|
||||||
|
AddWebapiTokens1737453096674,
|
||||||
],
|
],
|
||||||
migrationsRun: true,
|
migrationsRun: true,
|
||||||
migrationsTransactionMode: "each",
|
migrationsTransactionMode: "each",
|
||||||
|
|
|
@ -40,17 +40,17 @@ export class communication {
|
||||||
@Column()
|
@Column()
|
||||||
typeId: number;
|
typeId: number;
|
||||||
|
|
||||||
@ManyToOne(() => communicationType, (communicationType) => communicationType.communications, {
|
|
||||||
nullable: false,
|
|
||||||
onDelete: "RESTRICT",
|
|
||||||
onUpdate: "RESTRICT",
|
|
||||||
})
|
|
||||||
type: communicationType;
|
|
||||||
|
|
||||||
@ManyToOne(() => member, (member) => member.awards, {
|
@ManyToOne(() => member, (member) => member.awards, {
|
||||||
nullable: false,
|
nullable: false,
|
||||||
onDelete: "CASCADE",
|
onDelete: "CASCADE",
|
||||||
onUpdate: "RESTRICT",
|
onUpdate: "RESTRICT",
|
||||||
})
|
})
|
||||||
member: member;
|
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" })
|
@PrimaryColumn({ generated: "increment", type: "int" })
|
||||||
id: number;
|
id: number;
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated
|
|
||||||
*/
|
|
||||||
@Column({ type: "varchar", length: 255, unique: true, nullable: true })
|
|
||||||
internalId?: string;
|
|
||||||
|
|
||||||
@Column({ type: "date" })
|
@Column({ type: "date" })
|
||||||
start: Date;
|
start: Date;
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,9 @@ export class protocolPresence {
|
||||||
@Column({ type: "boolean", default: false })
|
@Column({ type: "boolean", default: false })
|
||||||
absent: boolean;
|
absent: boolean;
|
||||||
|
|
||||||
|
@Column({ type: "boolean", default: true })
|
||||||
|
excused: boolean;
|
||||||
|
|
||||||
@ManyToOne(() => member, {
|
@ManyToOne(() => member, {
|
||||||
nullable: false,
|
nullable: false,
|
||||||
onDelete: "CASCADE",
|
onDelete: "CASCADE",
|
||||||
|
|
|
@ -9,7 +9,7 @@ export class protocolPrintout {
|
||||||
@Column({ type: "varchar", length: 255 })
|
@Column({ type: "varchar", length: 255 })
|
||||||
title: string;
|
title: string;
|
||||||
|
|
||||||
@Column({ type: "int" })
|
@Column({ type: "int", default: "1" })
|
||||||
iteration: number;
|
iteration: number;
|
||||||
|
|
||||||
@Column({ type: "varchar", length: 255 })
|
@Column({ type: "varchar", length: 255 })
|
||||||
|
|
26
src/entity/user/webapi.ts
Normal file
26
src/entity/user/webapi.ts
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import { Column, CreateDateColumn, Entity, OneToMany, PrimaryColumn } from "typeorm";
|
||||||
|
import { webapiPermission } from "./webapi_permission";
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class webapi {
|
||||||
|
@PrimaryColumn({ generated: "increment", type: "int" })
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
@Column({ type: "text", unique: true, select: false })
|
||||||
|
token: string;
|
||||||
|
|
||||||
|
@Column({ type: "varchar", length: 255, unique: true })
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
@CreateDateColumn()
|
||||||
|
createdAt: Date;
|
||||||
|
|
||||||
|
@Column({ type: "datetime", nullable: true })
|
||||||
|
lastUsage?: Date;
|
||||||
|
|
||||||
|
@Column({ type: "date", nullable: true })
|
||||||
|
expiry?: Date;
|
||||||
|
|
||||||
|
@OneToMany(() => webapiPermission, (apiPermission) => apiPermission.webapi)
|
||||||
|
permissions: webapiPermission[];
|
||||||
|
}
|
19
src/entity/user/webapi_permission.ts
Normal file
19
src/entity/user/webapi_permission.ts
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import { Column, Entity, ManyToOne, OneToMany, PrimaryColumn } from "typeorm";
|
||||||
|
import { PermissionObject, PermissionString } from "../../type/permissionTypes";
|
||||||
|
import { webapi } from "./webapi";
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class webapiPermission {
|
||||||
|
@PrimaryColumn({ type: "int" })
|
||||||
|
webapiId: number;
|
||||||
|
|
||||||
|
@PrimaryColumn({ type: "varchar", length: 255 })
|
||||||
|
permission: PermissionString;
|
||||||
|
|
||||||
|
@ManyToOne(() => webapi, {
|
||||||
|
nullable: false,
|
||||||
|
onDelete: "CASCADE",
|
||||||
|
onUpdate: "RESTRICT",
|
||||||
|
})
|
||||||
|
webapi: webapi;
|
||||||
|
}
|
|
@ -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_SECRET = process.env.JWT_SECRET ?? "my_jwt_secret_string_ilughfnadiuhgq§$IUZGFVRweiouarbt1oub3h5q4a";
|
||||||
export const JWT_EXPIRATION = process.env.JWT_EXPIRATION ?? "15m";
|
export const JWT_EXPIRATION = process.env.JWT_EXPIRATION ?? "15m";
|
||||||
export const REFRESH_EXPIRATION = process.env.REFRESH_EXPIRATION ?? "1d";
|
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_USERNAME = process.env.MAIL_USERNAME ?? "";
|
||||||
export const MAIL_PASSWORD = process.env.MAIL_PASSWORD ?? "";
|
export const MAIL_PASSWORD = process.env.MAIL_PASSWORD ?? "";
|
||||||
|
@ -20,7 +21,7 @@ export const MAIL_HOST = process.env.MAIL_HOST ?? "";
|
||||||
export const MAIL_PORT = Number(process.env.MAIL_PORT ?? "587");
|
export const MAIL_PORT = Number(process.env.MAIL_PORT ?? "587");
|
||||||
export const MAIL_SECURE = process.env.MAIL_SECURE ?? "false";
|
export const MAIL_SECURE = process.env.MAIL_SECURE ?? "false";
|
||||||
|
|
||||||
export const CLUB_NAME = process.env.CLUB_NAME ?? "";
|
export const CLUB_NAME = process.env.CLUB_NAME ?? "FF Admin";
|
||||||
export const CLUB_WEBSITE = process.env.CLUB_WEBSITE ?? "";
|
export const CLUB_WEBSITE = process.env.CLUB_WEBSITE ?? "";
|
||||||
|
|
||||||
export function configCheck() {
|
export function configCheck() {
|
||||||
|
@ -35,6 +36,7 @@ export function configCheck() {
|
||||||
if (JWT_SECRET == "" || typeof JWT_SECRET != "string") throw new Error("set valid value to JWT_SECRET");
|
if (JWT_SECRET == "" || typeof JWT_SECRET != "string") throw new Error("set valid value to JWT_SECRET");
|
||||||
checkMS(JWT_EXPIRATION, "JWT_EXPIRATION");
|
checkMS(JWT_EXPIRATION, "JWT_EXPIRATION");
|
||||||
checkMS(REFRESH_EXPIRATION, "REFRESH_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_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");
|
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 (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");
|
if (MAIL_SECURE != "true" && MAIL_SECURE != "false") throw new Error("set 'true' or 'false' to MAIL_SECURE");
|
||||||
|
|
||||||
console.log(CLUB_WEBSITE);
|
|
||||||
if (
|
if (
|
||||||
CLUB_WEBSITE != "" &&
|
CLUB_WEBSITE != "" &&
|
||||||
!/^(http(s):\/\/.)[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)$/.test(CLUB_WEBSITE)
|
!/^(http(s):\/\/.)[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)$/.test(CLUB_WEBSITE)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { member } from "../../../../entity/club/member/member";
|
import { member } from "../../../../entity/club/member/member";
|
||||||
import { MemberViewModel } from "../../../../viewmodel/admin/club/member/member.models";
|
import { MemberStatisticsViewModel, MemberViewModel } from "../../../../viewmodel/admin/club/member/member.models";
|
||||||
|
import { memberView } from "../../../../views/memberView";
|
||||||
import CommunicationFactory from "./communication";
|
import CommunicationFactory from "./communication";
|
||||||
import MembershipFactory from "./membership";
|
import MembershipFactory from "./membership";
|
||||||
|
|
||||||
|
@ -40,4 +41,23 @@ export default abstract class MemberFactory {
|
||||||
public static mapToBase(records: Array<member>): Array<MemberViewModel> {
|
public static mapToBase(records: Array<member>): Array<MemberViewModel> {
|
||||||
return records.map((r) => this.mapToSingle(r));
|
return records.map((r) => this.mapToSingle(r));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description map view record to MemberMembershipStatisticsViewModel
|
||||||
|
* @param {memberView} record
|
||||||
|
* @returns {MemberStatisticsViewModel}
|
||||||
|
*/
|
||||||
|
public static mapToMemberStatistic(record: memberView): MemberStatisticsViewModel {
|
||||||
|
return {
|
||||||
|
id: record.id,
|
||||||
|
salutation: record.salutation,
|
||||||
|
firstname: record.firstname,
|
||||||
|
lastname: record.lastname,
|
||||||
|
nameaffix: record.nameaffix,
|
||||||
|
birthdate: record.birthdate,
|
||||||
|
todayAge: record.todayAge,
|
||||||
|
ageThisYear: record.ageThisYear,
|
||||||
|
exactAge: record.exactAge,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
import { membership } from "../../../../entity/club/member/membership";
|
import { membership } from "../../../../entity/club/member/membership";
|
||||||
import { MembershipViewModel } from "../../../../viewmodel/admin/club/member/membership.models";
|
import {
|
||||||
|
MembershipStatisticsViewModel,
|
||||||
|
MembershipViewModel,
|
||||||
|
} from "../../../../viewmodel/admin/club/member/membership.models";
|
||||||
|
import { membershipView } from "../../../../views/membershipsView";
|
||||||
|
|
||||||
export default abstract class MembershipFactory {
|
export default abstract class MembershipFactory {
|
||||||
/**
|
/**
|
||||||
|
@ -26,4 +30,33 @@ export default abstract class MembershipFactory {
|
||||||
public static mapToBase(records: Array<membership>): Array<MembershipViewModel> {
|
public static mapToBase(records: Array<membership>): Array<MembershipViewModel> {
|
||||||
return records.map((r) => this.mapToSingle(r));
|
return records.map((r) => this.mapToSingle(r));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description map view record to MembershipStatisticsViewModel
|
||||||
|
* @param {membershipView} record
|
||||||
|
* @returns {MembershipStatisticsViewModel}
|
||||||
|
*/
|
||||||
|
public static mapToSingleStatistic(record: membershipView): MembershipStatisticsViewModel {
|
||||||
|
return {
|
||||||
|
durationInDays: record.durationInDays,
|
||||||
|
durationInYears: record.durationInYears,
|
||||||
|
status: record.status,
|
||||||
|
statusId: record.statusId,
|
||||||
|
memberId: record.memberId,
|
||||||
|
memberSalutation: record.memberSalutation,
|
||||||
|
memberFirstname: record.memberFirstname,
|
||||||
|
memberLastname: record.memberLastname,
|
||||||
|
memberNameaffix: record.memberNameaffix,
|
||||||
|
memberBirthdate: record.memberBirthdate,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description map records to MembershipStatisticsViewModel
|
||||||
|
* @param {Array<membershipView>} records
|
||||||
|
* @returns {Array<MembershipStatisticsViewModel>}
|
||||||
|
*/
|
||||||
|
public static mapToBaseStatistics(records: Array<membershipView>): Array<MembershipStatisticsViewModel> {
|
||||||
|
return records.map((r) => this.mapToSingleStatistic(r));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ export default abstract class ProtocolPresenceFactory {
|
||||||
return {
|
return {
|
||||||
memberId: record.member.id,
|
memberId: record.member.id,
|
||||||
absent: record.absent,
|
absent: record.absent,
|
||||||
|
excused: record.excused,
|
||||||
protocolId: record.protocolId,
|
protocolId: record.protocolId,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
30
src/factory/admin/user/webapi.ts
Normal file
30
src/factory/admin/user/webapi.ts
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import { webapi } from "../../../entity/user/webapi";
|
||||||
|
import PermissionHelper from "../../../helpers/permissionHelper";
|
||||||
|
import { ApiViewModel } from "../../../viewmodel/admin/user/webapi.models";
|
||||||
|
|
||||||
|
export default abstract class ApiFactory {
|
||||||
|
/**
|
||||||
|
* @description map record to api
|
||||||
|
* @param {webapi} record
|
||||||
|
* @returns {apiViewModel}
|
||||||
|
*/
|
||||||
|
public static mapToSingle(record: webapi): ApiViewModel {
|
||||||
|
return {
|
||||||
|
id: record.id,
|
||||||
|
permissions: PermissionHelper.convertToObject(record.permissions.map((e) => e.permission)),
|
||||||
|
title: record.title,
|
||||||
|
expiry: record.expiry,
|
||||||
|
lastUsage: record.lastUsage,
|
||||||
|
createdAt: record.createdAt,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description map records to api
|
||||||
|
* @param {Array<webapi>} records
|
||||||
|
* @returns {Array<apiViewModel>}
|
||||||
|
*/
|
||||||
|
public static mapToBase(records: Array<webapi>): Array<ApiViewModel> {
|
||||||
|
return records.map((r) => this.mapToSingle(r));
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,9 @@ import RolePermissionService from "../service/user/rolePermissionService";
|
||||||
import UserPermissionService from "../service/user/userPermissionService";
|
import UserPermissionService from "../service/user/userPermissionService";
|
||||||
import UserService from "../service/user/userService";
|
import UserService from "../service/user/userService";
|
||||||
import PermissionHelper from "./permissionHelper";
|
import PermissionHelper from "./permissionHelper";
|
||||||
|
import WebapiService from "../service/user/webapiService";
|
||||||
|
import WebapiPermissionService from "../service/user/webapiPermissionService";
|
||||||
|
import ms from "ms";
|
||||||
|
|
||||||
export abstract class JWTHelper {
|
export abstract class JWTHelper {
|
||||||
static validate(token: string): Promise<string | jwt.JwtPayload> {
|
static validate(token: string): Promise<string | jwt.JwtPayload> {
|
||||||
|
@ -17,13 +20,16 @@ export abstract class JWTHelper {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static create(data: JWTData): Promise<string> {
|
static create(
|
||||||
|
data: JWTData,
|
||||||
|
{ expOverwrite, useExpiration }: { expOverwrite?: number; useExpiration?: boolean } = { useExpiration: true }
|
||||||
|
): Promise<string> {
|
||||||
return new Promise<string>((resolve, reject) => {
|
return new Promise<string>((resolve, reject) => {
|
||||||
jwt.sign(
|
jwt.sign(
|
||||||
data,
|
data,
|
||||||
JWT_SECRET,
|
JWT_SECRET,
|
||||||
{
|
{
|
||||||
expiresIn: JWT_EXPIRATION,
|
...(useExpiration ?? true ? { expiresIn: expOverwrite ?? JWT_EXPIRATION } : {}),
|
||||||
},
|
},
|
||||||
(err, token) => {
|
(err, token) => {
|
||||||
if (err) reject(err.message);
|
if (err) reject(err.message);
|
||||||
|
@ -72,4 +78,35 @@ export abstract class JWTHelper {
|
||||||
throw new InternalException("Failed accessToken creation", err);
|
throw new InternalException("Failed accessToken creation", err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async buildWebapiToken(token: string, expiration?: Date): Promise<string> {
|
||||||
|
let { id, title } = await WebapiService.getByToken(token);
|
||||||
|
let webapiPermissions = await WebapiPermissionService.getByApi(id);
|
||||||
|
let webapiPermissionStrings = webapiPermissions.map((e) => e.permission);
|
||||||
|
let permissionObject = PermissionHelper.convertToObject(webapiPermissionStrings);
|
||||||
|
|
||||||
|
let jwtData: JWTToken = {
|
||||||
|
userId: id,
|
||||||
|
mail: "",
|
||||||
|
username: title,
|
||||||
|
firstname: "",
|
||||||
|
lastname: "",
|
||||||
|
isOwner: false,
|
||||||
|
permissions: permissionObject,
|
||||||
|
sub: "webapi_access_token",
|
||||||
|
};
|
||||||
|
|
||||||
|
let overwriteExpiration =
|
||||||
|
ms(JWT_EXPIRATION) < new Date().getTime() - new Date(expiration).getTime()
|
||||||
|
? null
|
||||||
|
: Date.now() - new Date(expiration).getTime();
|
||||||
|
|
||||||
|
return await JWTHelper.create(jwtData, { expOverwrite: overwriteExpiration, useExpiration: true })
|
||||||
|
.then((result) => {
|
||||||
|
return result;
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
throw new InternalException("Failed webapi accessToken creation", err);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,12 +33,21 @@ export abstract class NewsletterHelper {
|
||||||
private static formatJobEmit(
|
private static formatJobEmit(
|
||||||
event: "progress" | "complete",
|
event: "progress" | "complete",
|
||||||
kind: "pdf" | "mail",
|
kind: "pdf" | "mail",
|
||||||
|
factor: "success" | "failed" | "info",
|
||||||
newsletterId: number,
|
newsletterId: number,
|
||||||
total: number,
|
total: number,
|
||||||
iteration: number,
|
iteration: number,
|
||||||
msg: string
|
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(
|
public static buildData(
|
||||||
|
@ -132,9 +141,7 @@ export abstract class NewsletterHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(queryMemberIds);
|
let members = await MemberService.getAll({ noLimit: true });
|
||||||
|
|
||||||
let members = await MemberService.getAll(0, 1000);
|
|
||||||
|
|
||||||
return members[0].filter((m) => queryMemberIds.includes(m.id));
|
return members[0].filter((m) => queryMemberIds.includes(m.id));
|
||||||
}
|
}
|
||||||
|
@ -171,18 +178,9 @@ export abstract class NewsletterHelper {
|
||||||
allowedForMail.includes(m.sendNewsletter?.type?.id)
|
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()) {
|
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);
|
let data = this.buildData(newsletter, dates, rec);
|
||||||
|
|
||||||
const { body } = await TemplateHelper.renderFileForModule({
|
const { body } = await TemplateHelper.renderFileForModule({
|
||||||
|
@ -197,9 +195,10 @@ export abstract class NewsletterHelper {
|
||||||
this.formatJobEmit(
|
this.formatJobEmit(
|
||||||
"progress",
|
"progress",
|
||||||
"mail",
|
"mail",
|
||||||
|
"success",
|
||||||
newsletterId,
|
newsletterId,
|
||||||
mailRecipients.length,
|
mailRecipients.length,
|
||||||
index,
|
index + 1,
|
||||||
`successfully sent to ${rec.sendNewsletter.email}`
|
`successfully sent to ${rec.sendNewsletter.email}`
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
@ -207,18 +206,19 @@ export abstract class NewsletterHelper {
|
||||||
this.formatJobEmit(
|
this.formatJobEmit(
|
||||||
"progress",
|
"progress",
|
||||||
"mail",
|
"mail",
|
||||||
|
"failed",
|
||||||
newsletterId,
|
newsletterId,
|
||||||
mailRecipients.length,
|
mailRecipients.length,
|
||||||
index,
|
index + 1,
|
||||||
`failed to send to ${rec.sendNewsletter.email}`
|
`failed to send to ${rec.sendNewsletter.email}`
|
||||||
);
|
);
|
||||||
console.log("mail send", err);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.formatJobEmit(
|
this.formatJobEmit(
|
||||||
"complete",
|
"complete",
|
||||||
"mail",
|
"mail",
|
||||||
|
"info",
|
||||||
newsletterId,
|
newsletterId,
|
||||||
mailRecipients.length,
|
mailRecipients.length,
|
||||||
mailRecipients.length,
|
mailRecipients.length,
|
||||||
|
@ -249,21 +249,12 @@ export abstract class NewsletterHelper {
|
||||||
(m) => !notAllowedForPdf.includes(m.sendNewsletter?.type?.id) || m.sendNewsletter == null
|
(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 [
|
for (const [index, rec] of [
|
||||||
...pdfRecipients,
|
...pdfRecipients,
|
||||||
{ id: 0, firstname: "Alle Mitglieder", lastname: CLUB_NAME } as member,
|
{ id: 0, firstname: "Alle Mitglieder", lastname: CLUB_NAME } as member,
|
||||||
].entries()) {
|
].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));
|
let data = this.buildData(newsletter, dates, rec, printWithAdress.includes(rec.sendNewsletter?.type?.id));
|
||||||
|
|
||||||
await PdfExport.renderFile({
|
await PdfExport.renderFile({
|
||||||
|
@ -277,9 +268,10 @@ export abstract class NewsletterHelper {
|
||||||
this.formatJobEmit(
|
this.formatJobEmit(
|
||||||
"progress",
|
"progress",
|
||||||
"pdf",
|
"pdf",
|
||||||
|
"success",
|
||||||
newsletterId,
|
newsletterId,
|
||||||
pdfRecipients.length,
|
pdfRecipients.length + 1,
|
||||||
index,
|
index + 1,
|
||||||
`successfully printed for ${rec.lastname}, ${rec.firstname}`
|
`successfully printed for ${rec.lastname}, ${rec.firstname}`
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
@ -287,24 +279,15 @@ export abstract class NewsletterHelper {
|
||||||
this.formatJobEmit(
|
this.formatJobEmit(
|
||||||
"progress",
|
"progress",
|
||||||
"pdf",
|
"pdf",
|
||||||
|
"failed",
|
||||||
newsletterId,
|
newsletterId,
|
||||||
pdfRecipients.length,
|
pdfRecipients.length + 1,
|
||||||
index,
|
index + 1,
|
||||||
`failed print for ${rec.lastname}, ${rec.firstname}`
|
`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(
|
await PdfExport.sqashToSingleFile(
|
||||||
`newsletter/${newsletter.id}_${newsletter.title.replace(" ", "")}`,
|
`newsletter/${newsletter.id}_${newsletter.title.replace(" ", "")}`,
|
||||||
"allPdfsTogether",
|
"allPdfsTogether",
|
||||||
|
@ -314,9 +297,10 @@ export abstract class NewsletterHelper {
|
||||||
this.formatJobEmit(
|
this.formatJobEmit(
|
||||||
"progress",
|
"progress",
|
||||||
"pdf",
|
"pdf",
|
||||||
|
"success",
|
||||||
newsletterId,
|
newsletterId,
|
||||||
pdfRecipients.length,
|
pdfRecipients.length + 1,
|
||||||
pdfRecipients.length,
|
pdfRecipients.length + 1,
|
||||||
"sucessfully combined pdf"
|
"sucessfully combined pdf"
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
@ -324,20 +308,21 @@ export abstract class NewsletterHelper {
|
||||||
this.formatJobEmit(
|
this.formatJobEmit(
|
||||||
"progress",
|
"progress",
|
||||||
"pdf",
|
"pdf",
|
||||||
|
"failed",
|
||||||
newsletterId,
|
newsletterId,
|
||||||
pdfRecipients.length,
|
pdfRecipients.length + 1,
|
||||||
pdfRecipients.length,
|
pdfRecipients.length + 1,
|
||||||
"failed combining pdf"
|
"failed combining pdf"
|
||||||
);
|
);
|
||||||
console.log("pdf squash", err);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.formatJobEmit(
|
this.formatJobEmit(
|
||||||
"complete",
|
"complete",
|
||||||
"pdf",
|
"pdf",
|
||||||
|
"info",
|
||||||
newsletterId,
|
newsletterId,
|
||||||
pdfRecipients.length,
|
pdfRecipients.length + 1,
|
||||||
pdfRecipients.length,
|
pdfRecipients.length + 1,
|
||||||
`completed printing process`
|
`completed printing process`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,6 +89,19 @@ export default class PermissionHelper {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static isAdminMiddleware(): (req: Request, res: Response, next: Function) => void {
|
||||||
|
return (req: Request, res: Response, next: Function) => {
|
||||||
|
const permissions = req.permissions;
|
||||||
|
const isOwner = req.isOwner;
|
||||||
|
|
||||||
|
if (isOwner || permissions.admin) {
|
||||||
|
next();
|
||||||
|
} else {
|
||||||
|
throw new ForbiddenRequestException(`missing admin permission`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
static convertToObject(permissions: Array<PermissionString>): PermissionObject {
|
static convertToObject(permissions: Array<PermissionString>): PermissionObject {
|
||||||
if (permissions.includes("*")) {
|
if (permissions.includes("*")) {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -12,6 +12,8 @@ declare global {
|
||||||
username: string;
|
username: string;
|
||||||
isOwner: boolean;
|
isOwner: boolean;
|
||||||
permissions: PermissionObject;
|
permissions: PermissionObject;
|
||||||
|
isPWA: boolean;
|
||||||
|
isWebApiRequest: boolean;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,10 +29,15 @@ export default async function authenticate(req: Request, res: Response, next: Fu
|
||||||
throw new InternalException("process failed");
|
throw new InternalException("process failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (decoded?.sub == "api_token_retrieve") {
|
||||||
|
throw new BadRequestException("This token is only authorized to get temporary access tokens via GET /api/webapi");
|
||||||
|
}
|
||||||
|
|
||||||
req.userId = decoded.userId;
|
req.userId = decoded.userId;
|
||||||
req.username = decoded.username;
|
req.username = decoded.username;
|
||||||
req.isOwner = decoded.isOwner;
|
req.isOwner = decoded.isOwner;
|
||||||
req.permissions = decoded.permissions;
|
req.permissions = decoded.permissions;
|
||||||
|
req.isWebApiRequest = decoded?.sub == "webapi_access_token";
|
||||||
|
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
|
37
src/middleware/authenticateAPI.ts
Normal file
37
src/middleware/authenticateAPI.ts
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import { Request, Response } from "express";
|
||||||
|
import jwt from "jsonwebtoken";
|
||||||
|
import BadRequestException from "../exceptions/badRequestException";
|
||||||
|
import UnauthorizedRequestException from "../exceptions/unauthorizedRequestException";
|
||||||
|
import InternalException from "../exceptions/internalException";
|
||||||
|
import { JWTHelper } from "../helpers/jwtHelper";
|
||||||
|
|
||||||
|
export default async function authenticateAPI(req: Request, res: Response, next: Function) {
|
||||||
|
const bearer = req.headers.authorization?.split(" ")?.[1] ?? undefined;
|
||||||
|
|
||||||
|
if (!bearer) {
|
||||||
|
throw new BadRequestException("Provide valid Authorization Header");
|
||||||
|
}
|
||||||
|
|
||||||
|
let decoded: string | jwt.JwtPayload;
|
||||||
|
await JWTHelper.validate(bearer)
|
||||||
|
.then((result) => {
|
||||||
|
decoded = result;
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
if (err == "jwt expired") {
|
||||||
|
throw new UnauthorizedRequestException("Token expired", err);
|
||||||
|
} else {
|
||||||
|
throw new BadRequestException("Failed Authorization Header decoding", err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (typeof decoded == "string" || !decoded) {
|
||||||
|
throw new InternalException("process failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (decoded?.sub != "api_token_retrieve") {
|
||||||
|
throw new BadRequestException("This route can only be accessed via a api token");
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
}
|
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 { Request, Response } from "express";
|
||||||
import { ExceptionBase } from "../exceptions/exceptionsBaseType";
|
import { ExceptionBase } from "../exceptions/exceptionsBaseType";
|
||||||
import CustomRequestException from "../exceptions/customRequestException";
|
import CustomRequestException from "../exceptions/customRequestException";
|
||||||
|
import UnauthorizedRequestException from "../exceptions/unauthorizedRequestException";
|
||||||
|
|
||||||
export default function errorHandler(err: ExceptionBase | Error, req: Request, res: Response, next: Function) {
|
export default function errorHandler(err: ExceptionBase | Error, req: Request, res: Response, next: Function) {
|
||||||
let status = 500;
|
let status = 500;
|
||||||
|
@ -11,7 +12,11 @@ export default function errorHandler(err: ExceptionBase | Error, req: Request, r
|
||||||
msg = err.message;
|
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);
|
res.status(status).send(msg);
|
||||||
}
|
}
|
||||||
|
|
10
src/middleware/preventWebApiAccess.ts
Normal file
10
src/middleware/preventWebApiAccess.ts
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import { Request, Response } from "express";
|
||||||
|
import ForbiddenRequestException from "../exceptions/forbiddenRequestException";
|
||||||
|
|
||||||
|
export default async function preventWebapiAccess(req: Request, res: Response, next: Function) {
|
||||||
|
if (req.isWebApiRequest) {
|
||||||
|
throw new ForbiddenRequestException("This route cannot be accessed via webapi");
|
||||||
|
} else {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,27 +18,25 @@ export class InternalId1735822722235 implements MigrationInterface {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
let memberships = await queryRunner.manager.getRepository(membership).find();
|
// let memberships = await queryRunner.manager.getRepository(membership).find();
|
||||||
console.log(memberships);
|
// let internalIds = memberships.reduce<{ [key: number]: Array<string> }>((acc, cur) => {
|
||||||
let internalIds = memberships.reduce<{ [key: number]: Array<string> }>((acc, cur) => {
|
// let memberId = cur.memberId;
|
||||||
let memberId = cur.memberId;
|
// let setIds = acc[memberId] ?? [];
|
||||||
let setIds = acc[memberId] ?? [];
|
// if (cur?.internalId) {
|
||||||
if (cur?.internalId) {
|
// setIds.push(cur.internalId);
|
||||||
setIds.push(cur.internalId);
|
// }
|
||||||
}
|
// acc[memberId] = setIds;
|
||||||
acc[memberId] = setIds;
|
// return acc;
|
||||||
return acc;
|
// }, {});
|
||||||
}, {});
|
|
||||||
|
|
||||||
console.log(internalIds);
|
// for (const [id, value] of Object.entries(internalIds)) {
|
||||||
for (const [id, value] of Object.entries(internalIds)) {
|
// const ids = value.filter((v) => v != null).join(", ");
|
||||||
const ids = value.filter((v) => v != null).join(", ");
|
// if (ids) {
|
||||||
if (ids) {
|
// let m = await queryRunner.manager.getRepository(member).findOneByOrFail({ id: parseInt(id) });
|
||||||
let m = await queryRunner.manager.getRepository(member).findOneByOrFail({ id: parseInt(id) });
|
// m.internalId = ids;
|
||||||
m.internalId = ids;
|
// await queryRunner.manager.getRepository(member).save(m);
|
||||||
await queryRunner.manager.getRepository(member).save(m);
|
// }
|
||||||
}
|
// }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
|
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");
|
||||||
|
}
|
||||||
|
}
|
55
src/migrations/1737453096674-addwebapiTokens.ts
Normal file
55
src/migrations/1737453096674-addwebapiTokens.ts
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
import { MigrationInterface, QueryRunner, Table, TableForeignKey } from "typeorm";
|
||||||
|
import { DB_TYPE } from "../env.defaults";
|
||||||
|
|
||||||
|
export class AddWebapiTokens1737453096674 implements MigrationInterface {
|
||||||
|
name = "AddWebApiTokens1737453096674";
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
const variableType_int = DB_TYPE == "mysql" ? "int" : "integer";
|
||||||
|
|
||||||
|
await queryRunner.createTable(
|
||||||
|
new Table({
|
||||||
|
name: "webapi",
|
||||||
|
columns: [
|
||||||
|
{ name: "id", type: variableType_int, isPrimary: true, isGenerated: true, generationStrategy: "increment" },
|
||||||
|
{ name: "token", type: "text", isUnique: true, isNullable: false },
|
||||||
|
{ name: "title", type: "varchar", isUnique: true, length: "255", isNullable: false },
|
||||||
|
{ name: "createdAt", type: "datetime", default: "CURRENT_TIMESTAMP(6)", isNullable: false },
|
||||||
|
{ name: "lastUsage", type: "datetime", isNullable: true, default: null },
|
||||||
|
{ name: "expiry", type: "date", isNullable: true, default: null },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
await queryRunner.createTable(
|
||||||
|
new Table({
|
||||||
|
name: "webapi_permission",
|
||||||
|
columns: [
|
||||||
|
{ name: "webapiId", type: variableType_int, isPrimary: true, isNullable: false },
|
||||||
|
{ name: "permission", type: "varchar", length: "255", isPrimary: true, isNullable: false },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
await queryRunner.createForeignKey(
|
||||||
|
"webapi_permission",
|
||||||
|
new TableForeignKey({
|
||||||
|
columnNames: ["webapiId"],
|
||||||
|
referencedColumnNames: ["id"],
|
||||||
|
referencedTableName: "webapi",
|
||||||
|
onDelete: "CASCADE",
|
||||||
|
onUpdate: "RESTRICT",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
const table = await queryRunner.getTable("webapi_permission");
|
||||||
|
const foreignKey = table.foreignKeys.find((fk) => fk.columnNames.indexOf("webapiId") !== -1);
|
||||||
|
await queryRunner.dropForeignKey("webapi_permission", foreignKey);
|
||||||
|
await queryRunner.dropTable("webapi_permission");
|
||||||
|
await queryRunner.dropTable("webapi");
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,36 +1,38 @@
|
||||||
import express, { Request, Response } from "express";
|
import express, { Request, Response } from "express";
|
||||||
import {
|
import {
|
||||||
addAwardToMember,
|
addAwardToMember,
|
||||||
addCommunicationToMember,
|
addCommunicationToMember,
|
||||||
addExecutivePositionToMember,
|
addExecutivePositionToMember,
|
||||||
addMembershipToMember,
|
addMembershipToMember,
|
||||||
addQualificationToMember,
|
addQualificationToMember,
|
||||||
createMember,
|
createMember,
|
||||||
createMemberPrintoutList,
|
createMemberPrintoutList,
|
||||||
deleteAwardOfMember,
|
deleteAwardOfMember,
|
||||||
deleteCommunicationOfMember,
|
deleteCommunicationOfMember,
|
||||||
deleteExecutivePositionOfMember,
|
deleteExecutivePositionOfMember,
|
||||||
deleteMemberById,
|
deleteMemberById,
|
||||||
deleteMembershipOfMember,
|
deleteMembershipOfMember,
|
||||||
deleteQualificationOfMember,
|
deleteQualificationOfMember,
|
||||||
getAllMembers,
|
getAllMembers,
|
||||||
getAwardByMemberAndRecord,
|
getAwardByMemberAndRecord,
|
||||||
getAwardsByMember,
|
getAwardsByMember,
|
||||||
getCommunicationByMemberAndRecord,
|
getCommunicationByMemberAndRecord,
|
||||||
getCommunicationsByMember,
|
getCommunicationsByMember,
|
||||||
getExecutivePositionByMemberAndRecord,
|
getExecutivePositionByMemberAndRecord,
|
||||||
getExecutivePositionsByMember,
|
getExecutivePositionsByMember,
|
||||||
getMemberById,
|
getMemberById,
|
||||||
getMembershipByMemberAndRecord,
|
getMembershipByMemberAndRecord,
|
||||||
getMembershipsByMember,
|
getMembershipsByMember,
|
||||||
getQualificationByMemberAndRecord,
|
getMembershipStatisticsById,
|
||||||
getQualificationsByMember,
|
getMemberStatisticsById,
|
||||||
updateAwardOfMember,
|
getQualificationByMemberAndRecord,
|
||||||
updateCommunicationOfMember,
|
getQualificationsByMember,
|
||||||
updateExecutivePositionOfMember,
|
updateAwardOfMember,
|
||||||
updateMemberById,
|
updateCommunicationOfMember,
|
||||||
updateMembershipOfMember,
|
updateExecutivePositionOfMember,
|
||||||
updateQualificationOfMember,
|
updateMemberById,
|
||||||
|
updateMembershipOfMember,
|
||||||
|
updateQualificationOfMember,
|
||||||
} from "../../../controller/admin/club/memberController";
|
} from "../../../controller/admin/club/memberController";
|
||||||
import PermissionHelper from "../../../helpers/permissionHelper";
|
import PermissionHelper from "../../../helpers/permissionHelper";
|
||||||
|
|
||||||
|
@ -44,14 +46,22 @@ router.get("/:id", async (req: Request, res: Response) => {
|
||||||
await getMemberById(req, res);
|
await getMemberById(req, res);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.get("/:id/statistics", async (req: Request, res: Response) => {
|
||||||
|
await getMemberStatisticsById(req, res);
|
||||||
|
});
|
||||||
|
|
||||||
router.get("/print/namelist", async (req: Request, res: Response) => {
|
router.get("/print/namelist", async (req: Request, res: Response) => {
|
||||||
await createMemberPrintoutList(req, res);
|
await createMemberPrintoutList(req, res);
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get("/:memberId/memberships", async (req: Request, res: Response) => {
|
router.get("/:memberId/memberships", async (req: Request, res: Response) => {
|
||||||
await getMembershipsByMember(req, res);
|
await getMembershipsByMember(req, res);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.get("/:memberId/memberships/statistics", async (req: Request, res: Response) => {
|
||||||
|
await getMembershipStatisticsById(req, res);
|
||||||
|
});
|
||||||
|
|
||||||
router.get("/:memberId/membership/:id", async (req: Request, res: Response) => {
|
router.get("/:memberId/membership/:id", async (req: Request, res: Response) => {
|
||||||
await getMembershipByMemberAndRecord(req, res);
|
await getMembershipByMemberAndRecord(req, res);
|
||||||
});
|
});
|
||||||
|
|
|
@ -21,6 +21,8 @@ import newsletter from "./club/newsletter";
|
||||||
import role from "./user/role";
|
import role from "./user/role";
|
||||||
import user from "./user/user";
|
import user from "./user/user";
|
||||||
import invite from "./user/invite";
|
import invite from "./user/invite";
|
||||||
|
import api from "./user/webapi";
|
||||||
|
import preventWebapiAccess from "../../middleware/preventWebApiAccess";
|
||||||
|
|
||||||
var router = express.Router({ mergeParams: true });
|
var router = express.Router({ mergeParams: true });
|
||||||
|
|
||||||
|
@ -60,5 +62,6 @@ router.use("/newsletter", PermissionHelper.passCheckMiddleware("read", "club", "
|
||||||
router.use("/role", PermissionHelper.passCheckMiddleware("read", "user", "role"), role);
|
router.use("/role", PermissionHelper.passCheckMiddleware("read", "user", "role"), role);
|
||||||
router.use("/user", PermissionHelper.passCheckMiddleware("read", "user", "user"), user);
|
router.use("/user", PermissionHelper.passCheckMiddleware("read", "user", "user"), user);
|
||||||
router.use("/invite", PermissionHelper.passCheckMiddleware("read", "user", "user"), invite);
|
router.use("/invite", PermissionHelper.passCheckMiddleware("read", "user", "user"), invite);
|
||||||
|
router.use("/webapi", preventWebapiAccess, PermissionHelper.passCheckMiddleware("read", "user", "webapi"), api);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import express, { Request, Response } from "express";
|
import express, { Request, Response } from "express";
|
||||||
import {
|
import {
|
||||||
deleteNewsletterConfig,
|
|
||||||
getAllNewsletterConfigs,
|
getAllNewsletterConfigs,
|
||||||
getNewsletterConfigById,
|
getNewsletterConfigById,
|
||||||
setNewsletterConfig,
|
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;
|
export default router;
|
||||||
|
|
64
src/routes/admin/user/webapi.ts
Normal file
64
src/routes/admin/user/webapi.ts
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
import express, { Request, Response } from "express";
|
||||||
|
import PermissionHelper from "../../../helpers/permissionHelper";
|
||||||
|
import {
|
||||||
|
createWebapi,
|
||||||
|
deleteWebapi,
|
||||||
|
getAllWebapis,
|
||||||
|
getWebapiById,
|
||||||
|
getWebapiPermissions,
|
||||||
|
getWebapiTokenById,
|
||||||
|
updateWebapi,
|
||||||
|
updateWebapiPermissions,
|
||||||
|
} from "../../../controller/admin/user/webapiController";
|
||||||
|
|
||||||
|
var router = express.Router({ mergeParams: true });
|
||||||
|
|
||||||
|
router.get("/", async (req: Request, res: Response) => {
|
||||||
|
await getAllWebapis(req, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get("/:id", async (req: Request, res: Response) => {
|
||||||
|
await getWebapiById(req, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get("/:id/token", async (req: Request, res: Response) => {
|
||||||
|
await getWebapiTokenById(req, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get("/:id/permissions", async (req: Request, res: Response) => {
|
||||||
|
await getWebapiPermissions(req, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post(
|
||||||
|
"/",
|
||||||
|
PermissionHelper.passCheckMiddleware("create", "user", "webapi"),
|
||||||
|
async (req: Request, res: Response) => {
|
||||||
|
await createWebapi(req, res);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
router.patch(
|
||||||
|
"/:id",
|
||||||
|
PermissionHelper.passCheckMiddleware("update", "user", "webapi"),
|
||||||
|
async (req: Request, res: Response) => {
|
||||||
|
await updateWebapi(req, res);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
router.patch(
|
||||||
|
"/:id/permissions",
|
||||||
|
PermissionHelper.passCheckMiddleware("admin", "user", "webapi"),
|
||||||
|
async (req: Request, res: Response) => {
|
||||||
|
await updateWebapiPermissions(req, res);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
router.delete(
|
||||||
|
"/:id",
|
||||||
|
PermissionHelper.passCheckMiddleware("delete", "user", "webapi"),
|
||||||
|
async (req: Request, res: Response) => {
|
||||||
|
await deleteWebapi(req, res);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export default router;
|
|
@ -1,584 +0,0 @@
|
||||||
import express from "express";
|
|
||||||
import knex from "knex";
|
|
||||||
import moment from "moment";
|
|
||||||
import AwardService from "../service/settings/awardService";
|
|
||||||
import { CreateAwardCommand, DeleteAwardCommand } from "../command/settings/award/awardCommand";
|
|
||||||
import AwardCommandHandler from "../command/settings/award/awardCommandHandler";
|
|
||||||
import MemberService from "../service/club/member/memberService";
|
|
||||||
import { CreateMemberCommand, DeleteMemberCommand } from "../command/club/member/memberCommand";
|
|
||||||
import MemberCommandHandler from "../command/club/member/memberCommandHandler";
|
|
||||||
import MemberAwardCommandHandler from "../command/club/member/memberAwardCommandHandler";
|
|
||||||
import { CreateMemberAwardCommand, DeleteMemberAwardCommand } from "../command/club/member/memberAwardCommand";
|
|
||||||
import MemberAwardService from "../service/club/member/memberAwardService";
|
|
||||||
import CommunicationService from "../service/club/member/communicationService";
|
|
||||||
import { CreateCommunicationCommand, DeleteCommunicationCommand } from "../command/club/member/communicationCommand";
|
|
||||||
import CommunicationCommandHandler from "../command/club/member/communicationCommandHandler";
|
|
||||||
import MembershipService from "../service/club/member/membershipService";
|
|
||||||
import {
|
|
||||||
CreateMembershipCommand,
|
|
||||||
DeleteMembershipCommand,
|
|
||||||
UpdateMembershipCommand,
|
|
||||||
} from "../command/club/member/membershipCommand";
|
|
||||||
import MembershipCommandHandler from "../command/club/member/membershipCommandHandler";
|
|
||||||
import MemberExecutivePositionService from "../service/club/member/memberExecutivePositionService";
|
|
||||||
import {
|
|
||||||
CreateMemberExecutivePositionCommand,
|
|
||||||
DeleteMemberExecutivePositionCommand,
|
|
||||||
} from "../command/club/member/memberExecutivePositionCommand";
|
|
||||||
import MemberExecutivePositionCommandHandler from "../command/club/member/memberExecutivePositionCommandHandler";
|
|
||||||
import CommunicationTypeService from "../service/settings/communicationTypeService";
|
|
||||||
import {
|
|
||||||
CreateCommunicationTypeCommand,
|
|
||||||
DeleteCommunicationTypeCommand,
|
|
||||||
} from "../command/settings/communicationType/communicationTypeCommand";
|
|
||||||
import CommunicationTypeCommandHandler from "../command/settings/communicationType/communicationTypeCommandHandler";
|
|
||||||
import ExecutivePositionService from "../service/settings/executivePositionService";
|
|
||||||
import {
|
|
||||||
CreateExecutivePositionCommand,
|
|
||||||
DeleteExecutivePositionCommand,
|
|
||||||
} from "../command/settings/executivePosition/executivePositionCommand";
|
|
||||||
import ExecutivePositionCommandHandler from "../command/settings/executivePosition/executivePositionCommandHandler";
|
|
||||||
import MembershipStatusService from "../service/settings/membershipStatusService";
|
|
||||||
import {
|
|
||||||
CreateMembershipStatusCommand,
|
|
||||||
DeleteMembershipStatusCommand,
|
|
||||||
} from "../command/settings/membershipStatus/membershipStatusCommand";
|
|
||||||
import MembershipStatusCommandHandler from "../command/settings/membershipStatus/membershipStatusCommandHandler";
|
|
||||||
|
|
||||||
var router = express.Router({ mergeParams: true });
|
|
||||||
|
|
||||||
router.get("/", async (req, res) => {
|
|
||||||
|
|
||||||
const dbhost = process.env.OLD_DB_HOST;
|
|
||||||
const dbport = Number(process.env.OLD_DB_PORT);
|
|
||||||
const dbuser = process.env.OLD_DB_USER;
|
|
||||||
const dbuserpass = process.env.OLD_DB_USERPASS;
|
|
||||||
const dbname = process.env.OLD_DB_NAME;
|
|
||||||
|
|
||||||
const db = knex({
|
|
||||||
client: 'mssql',
|
|
||||||
connection: {
|
|
||||||
host: dbhost,
|
|
||||||
port: dbport,
|
|
||||||
user: dbuser,
|
|
||||||
password: dbuserpass,
|
|
||||||
database: dbname,
|
|
||||||
options: {
|
|
||||||
trustServerCertificate: true,
|
|
||||||
enableArithAbort: true,
|
|
||||||
encrypt: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
debug: false,
|
|
||||||
pool: {
|
|
||||||
min: 0, max: 30
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let queryResult = await db('Mitglieder');
|
|
||||||
console.log(`Read ${queryResult.length} Mitglieder`);
|
|
||||||
|
|
||||||
// Delete members
|
|
||||||
let [members, total] = await MemberService.getAll(0, 9999);
|
|
||||||
for (const m of members) {
|
|
||||||
// delete all member awards of this member
|
|
||||||
let memberAwards = await MemberAwardService.getAll(m.id);
|
|
||||||
for (const award of memberAwards) {
|
|
||||||
let deleteMemberAward: DeleteMemberAwardCommand = {
|
|
||||||
id: award.id, memberId: m.id
|
|
||||||
};
|
|
||||||
console.log(`Deleting award ${award.id} from ${m.firstname} ${m.lastname}`);
|
|
||||||
await MemberAwardCommandHandler.delete(deleteMemberAward)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete communication from this member
|
|
||||||
let communications = await CommunicationService.getAll(m.id);
|
|
||||||
for (const comm of communications) {
|
|
||||||
let deleteCommunication: DeleteCommunicationCommand = {
|
|
||||||
id: comm.id, memberId: 0
|
|
||||||
};
|
|
||||||
console.log(`Delete communication ${comm.type} from ${m.firstname} ${m.lastname}`);
|
|
||||||
await CommunicationCommandHandler.delete(deleteCommunication);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete membership entries
|
|
||||||
let memberships = await MembershipService.getAll(m.id);
|
|
||||||
for (const membership of memberships) {
|
|
||||||
let deleteMembership: DeleteMembershipCommand = {
|
|
||||||
id: membership.id, memberId: m.id
|
|
||||||
};
|
|
||||||
console.log(`Delete membership ${membership.id} from ${m.firstname} ${m.lastname}`);
|
|
||||||
await MembershipCommandHandler.delete(deleteMembership);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete membership positions entries
|
|
||||||
let memberExecutivePositions = await MemberExecutivePositionService.getAll(m.id);
|
|
||||||
for (const memberPos of memberExecutivePositions) {
|
|
||||||
let deleteMemberExecutivePosition: DeleteMemberExecutivePositionCommand = {
|
|
||||||
id: memberPos.id, memberId: m.id
|
|
||||||
};
|
|
||||||
console.log(`Delete position ${deleteMemberExecutivePosition.id} from ${m.firstname} ${m.lastname}`);
|
|
||||||
await MemberExecutivePositionCommandHandler.delete(deleteMemberExecutivePosition);
|
|
||||||
}
|
|
||||||
|
|
||||||
// delete member itself
|
|
||||||
let deleteMember: DeleteMemberCommand = {
|
|
||||||
id: m.id,
|
|
||||||
};
|
|
||||||
console.log(`Delete member ${m.id}`);
|
|
||||||
await MemberCommandHandler.delete(deleteMember);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete awards
|
|
||||||
let awards = await AwardService.getAll();
|
|
||||||
for (const award of awards) {
|
|
||||||
let deleteAward: DeleteAwardCommand = {
|
|
||||||
id: award.id,
|
|
||||||
};
|
|
||||||
console.log(`Delete award ${award.id}`);
|
|
||||||
await AwardCommandHandler.delete(deleteAward);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete communication types
|
|
||||||
let commTypes = await CommunicationTypeService.getAll();
|
|
||||||
for (const commType of commTypes) {
|
|
||||||
let deleteCommunicationType: DeleteCommunicationTypeCommand = {
|
|
||||||
id: commType.id,
|
|
||||||
};
|
|
||||||
console.log(`Delete communication type ${commType.type}`);
|
|
||||||
await CommunicationTypeCommandHandler.delete(deleteCommunicationType);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete postions
|
|
||||||
let positions = await ExecutivePositionService.getAll();
|
|
||||||
for (const position of positions) {
|
|
||||||
let deletePosition: DeleteExecutivePositionCommand = {
|
|
||||||
id: position.id,
|
|
||||||
};
|
|
||||||
console.log(`Delete position ${position.position}`);
|
|
||||||
await ExecutivePositionCommandHandler.delete(deletePosition);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete membership states
|
|
||||||
let states = await MembershipStatusService.getAll();
|
|
||||||
for (const state of states) {
|
|
||||||
let deleteMembershipStatus: DeleteMembershipStatusCommand = {
|
|
||||||
id: state.id
|
|
||||||
};
|
|
||||||
console.log(`Delete membership status ${state.status}`);
|
|
||||||
await MembershipStatusCommandHandler.delete(deleteMembershipStatus);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create Awards
|
|
||||||
const sAwardsById :any = {};
|
|
||||||
const sAwardsByName :any = {};
|
|
||||||
const awardNames = ['Ehrennadel Silber', 'Ehrennadel Gold', 'Ehrenkreuz Silber', 'Ehrenkreuz Gold', 'Ehrenmitgliedschaft', 'Ehrung 25 Jahre aktiv', 'Ehrung 40 Jahre aktiv'];
|
|
||||||
for (const awardName of awardNames) {
|
|
||||||
let createAward: CreateAwardCommand = {
|
|
||||||
award: awardName,
|
|
||||||
};
|
|
||||||
const awardId = await AwardCommandHandler.create(createAward);
|
|
||||||
console.log(`Created award ${awardName} with id ${awardId}`);
|
|
||||||
sAwardsById[awardId] = awardName;
|
|
||||||
sAwardsByName[awardName] = awardId;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create communication types
|
|
||||||
const createCommunicationTypeMail: CreateCommunicationTypeCommand = {
|
|
||||||
type: "Post", useColumns: ['city','street','streetNumber','streetNumberAddition']
|
|
||||||
}
|
|
||||||
const createCommunicationTypeEMail: CreateCommunicationTypeCommand = {
|
|
||||||
type: "Email", useColumns: ['email']
|
|
||||||
}
|
|
||||||
const createCommunicationTypeSMS: CreateCommunicationTypeCommand = {
|
|
||||||
type: "SMS", useColumns: ['mobile']
|
|
||||||
}
|
|
||||||
const createCommunicationTypeWhatsApp: CreateCommunicationTypeCommand = {
|
|
||||||
type: "WhatsApp", useColumns: ['mobile']
|
|
||||||
}
|
|
||||||
const createCommTypes = [createCommunicationTypeMail, createCommunicationTypeEMail, createCommunicationTypeSMS, createCommunicationTypeWhatsApp];
|
|
||||||
const sCommTypesByName: any = {};
|
|
||||||
const sCommTypesById: any = {};
|
|
||||||
for (const createCommType of createCommTypes) {
|
|
||||||
const commTypeId = await CommunicationTypeCommandHandler.create(createCommType);
|
|
||||||
console.log(`Create communication type ${createCommType.type} with id ${commTypeId}`);
|
|
||||||
sCommTypesByName[createCommType.type] = commTypeId;
|
|
||||||
sCommTypesById[commTypeId] = createCommType.type;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create positions
|
|
||||||
const sFunktionenByOldId :any = {};
|
|
||||||
let positionsResult: any[] = await db('Funktionen');
|
|
||||||
for (const position of positionsResult) {
|
|
||||||
const posName: string = position.Name.trim();
|
|
||||||
let createExecutivePosition: CreateExecutivePositionCommand = {
|
|
||||||
position: posName,
|
|
||||||
}
|
|
||||||
sFunktionenByOldId[position.IdFunktion] = await ExecutivePositionCommandHandler.create(createExecutivePosition);
|
|
||||||
}
|
|
||||||
|
|
||||||
// create membership states
|
|
||||||
let sStatesByName : any = {};
|
|
||||||
for (const state of ['aktiv', 'passiv', 'fördernd']) {
|
|
||||||
let createMembershipStatus: CreateMembershipStatusCommand = {
|
|
||||||
status: state
|
|
||||||
}
|
|
||||||
sStatesByName[state] = await MembershipStatusCommandHandler.create(createMembershipStatus);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Add all members
|
|
||||||
for (const queryResultElement of queryResult) {
|
|
||||||
const ineternalID = queryResultElement.ID
|
|
||||||
let nameAffix: string = "";
|
|
||||||
let nn = queryResultElement.Nachname.trim();
|
|
||||||
if (nn.endsWith(", jun.")) {
|
|
||||||
nameAffix = "jun.";
|
|
||||||
nn = nn.substring(0, nn.length - 6);
|
|
||||||
}
|
|
||||||
if (nn.endsWith(", sen.")) {
|
|
||||||
nameAffix = "sen.";
|
|
||||||
nn = nn.substring(0, nn.length - 6);
|
|
||||||
}
|
|
||||||
|
|
||||||
// before 1.1.2009: Altersobergrenze für Aktive: 60
|
|
||||||
const limit60 = moment('2009-01-01');
|
|
||||||
// before 1.7.2017: 63
|
|
||||||
const limit63 = moment('2017-07-01');
|
|
||||||
// from 1.7.2017: 65
|
|
||||||
|
|
||||||
const alter60 = moment(queryResultElement.Geboren).add(60, 'years');
|
|
||||||
const alter63 = moment(queryResultElement.Geboren).add(63, 'years');
|
|
||||||
const alter65 = moment(queryResultElement.Geboren).add(65, 'years');
|
|
||||||
const eintritt = moment(queryResultElement.Eingetreten);
|
|
||||||
const verstorben = moment(queryResultElement.verstorben);
|
|
||||||
|
|
||||||
|
|
||||||
let newMember: CreateMemberCommand = {
|
|
||||||
salutation: queryResultElement.Anrede.trim(),
|
|
||||||
firstname: queryResultElement.Vorname.trim(),
|
|
||||||
lastname: nn,
|
|
||||||
nameaffix: nameAffix,
|
|
||||||
birthdate: queryResultElement.Geboren ? queryResultElement.Geboren : "1900-01-01",
|
|
||||||
internalId: queryResultElement.ID,
|
|
||||||
};
|
|
||||||
const memberId = await MemberCommandHandler.create(newMember);
|
|
||||||
console.log(`Created member ${newMember.firstname} ${newMember.lastname} with id ${memberId}`);
|
|
||||||
|
|
||||||
let stateId = sStatesByName["aktiv"];
|
|
||||||
if (!queryResultElement.Eingetreten) {
|
|
||||||
throw new Error('Eingetreten missing');
|
|
||||||
}
|
|
||||||
if (queryResultElement["Übergang Passiv"] && moment(queryResultElement["Übergang Passiv"]).format('L') === moment(queryResultElement.Eingetreten).format('L')) {
|
|
||||||
stateId = sStatesByName["fördernd"];
|
|
||||||
}
|
|
||||||
let start = queryResultElement.Eingetreten;
|
|
||||||
let createMembership: CreateMembershipCommand = {
|
|
||||||
memberId: memberId, start: start, statusId: stateId,
|
|
||||||
};
|
|
||||||
let ms1 = await MembershipCommandHandler.create(createMembership);
|
|
||||||
|
|
||||||
if (queryResultElement["Übergang Passiv"] && moment(queryResultElement["Übergang Passiv"]).format('L') !== moment(queryResultElement.Eingetreten).format('L')) {
|
|
||||||
// membership transitioned to passiv
|
|
||||||
// no knowledge about active time => assume active until age of 60 or 65
|
|
||||||
let end: Date = queryResultElement["Übergang Passiv"];
|
|
||||||
let terminationReason: string = "aktiv >> passiv";
|
|
||||||
let newStart = queryResultElement["Übergang Passiv"];
|
|
||||||
let newStateId = sStatesByName["passiv"];
|
|
||||||
|
|
||||||
if (queryResultElement.Geboren == null && queryResultElement.verzogenDatum) {
|
|
||||||
terminationReason = "verzogen";
|
|
||||||
end = queryResultElement.verzogenDatum
|
|
||||||
} else {
|
|
||||||
if (queryResultElement["Übergang Passiv"] < queryResultElement.Eingetreten) {
|
|
||||||
if (alter60.isBefore(limit60)) {
|
|
||||||
terminationReason = "passiv (Altersobergrenze: 60)";
|
|
||||||
end = alter60.toDate();
|
|
||||||
} else {
|
|
||||||
if (alter63.isBefore(limit63)) {
|
|
||||||
terminationReason = "passiv (Altersobergrenze: 63)";
|
|
||||||
end = alter63.toDate();
|
|
||||||
} else {
|
|
||||||
terminationReason = "passiv (Altersobergrenze: 65)";
|
|
||||||
end = alter65.toDate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (end) {
|
|
||||||
newStart = end;
|
|
||||||
|
|
||||||
if (!(queryResultElement.verstorben && moment(queryResultElement.verstorben).isBefore(moment(end)))) {
|
|
||||||
// finish current membership (but only if member was alive at this time)
|
|
||||||
let updateMembership: UpdateMembershipCommand = {
|
|
||||||
end: end,
|
|
||||||
id: ms1,
|
|
||||||
memberId: memberId,
|
|
||||||
start: start,
|
|
||||||
statusId: stateId,
|
|
||||||
terminationReason: terminationReason,
|
|
||||||
};
|
|
||||||
await MembershipCommandHandler.update(updateMembership);
|
|
||||||
|
|
||||||
// create new membership with passiv state
|
|
||||||
stateId = newStateId;
|
|
||||||
start = newStart;
|
|
||||||
let createMembership: CreateMembershipCommand = {
|
|
||||||
// internalId: queryResultElement.ID,
|
|
||||||
memberId: memberId,
|
|
||||||
start: start,
|
|
||||||
statusId: stateId,
|
|
||||||
};
|
|
||||||
ms1 = await MembershipCommandHandler.create(createMembership);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!queryResultElement["Übergang Passiv"] && queryResultElement.verstorben) {
|
|
||||||
let terminationReason;
|
|
||||||
let end;
|
|
||||||
let newStart;
|
|
||||||
let newStateId;
|
|
||||||
|
|
||||||
if (alter60.isBefore(limit60) && alter60.isBefore(verstorben)) {
|
|
||||||
if (alter60.isBefore(eintritt)) {
|
|
||||||
stateId = sStatesByName["passiv"];
|
|
||||||
} else {
|
|
||||||
terminationReason = "passiv (Altersobergrenze: 60)";
|
|
||||||
end = alter60.toDate();
|
|
||||||
newStart = end;
|
|
||||||
newStateId = sStatesByName["passiv"];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (alter63.isBefore(limit63) && alter63.isBefore(verstorben)) {
|
|
||||||
if (alter63.isBefore(eintritt)) {
|
|
||||||
stateId = sStatesByName["passiv"];
|
|
||||||
} else {
|
|
||||||
terminationReason = "passiv (Altersobergrenze: 63)";
|
|
||||||
end = alter63.toDate();
|
|
||||||
newStart = end;
|
|
||||||
newStateId = sStatesByName["passiv"];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (alter65.isBefore(verstorben)) {
|
|
||||||
if (alter65.isBefore(eintritt)) {
|
|
||||||
stateId = sStatesByName["passiv"];
|
|
||||||
} else {
|
|
||||||
terminationReason = "passiv (Altersobergrenze: 65)";
|
|
||||||
end = alter65.toDate();
|
|
||||||
newStart = end;
|
|
||||||
newStateId = sStatesByName["passiv"];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
end = queryResultElement.verstorben;
|
|
||||||
terminationReason = "verstorben";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (end) {
|
|
||||||
let updateMembership: UpdateMembershipCommand = {
|
|
||||||
end: end,
|
|
||||||
id: ms1,
|
|
||||||
memberId: memberId,
|
|
||||||
start: start,
|
|
||||||
statusId: stateId,
|
|
||||||
terminationReason: terminationReason,
|
|
||||||
};
|
|
||||||
await MembershipCommandHandler.update(updateMembership);
|
|
||||||
|
|
||||||
if (newStart) {
|
|
||||||
// create new membership with passiv state
|
|
||||||
stateId = newStateId;
|
|
||||||
start = newStart;
|
|
||||||
let createMembership: CreateMembershipCommand = {
|
|
||||||
memberId: memberId,
|
|
||||||
start: start,
|
|
||||||
statusId: stateId,
|
|
||||||
};
|
|
||||||
ms1 = await MembershipCommandHandler.create(createMembership);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (queryResultElement.Ausgetreten) {
|
|
||||||
let updateMembership: UpdateMembershipCommand = {
|
|
||||||
end: queryResultElement.Ausgetreten,
|
|
||||||
id: ms1,
|
|
||||||
memberId: memberId,
|
|
||||||
start: start,
|
|
||||||
statusId: stateId,
|
|
||||||
terminationReason: "Austritt",
|
|
||||||
};
|
|
||||||
await MembershipCommandHandler.update(updateMembership);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (queryResultElement.verstorben) {
|
|
||||||
let updateMembership: UpdateMembershipCommand = {
|
|
||||||
end: queryResultElement.verstorben,
|
|
||||||
id: ms1,
|
|
||||||
memberId: memberId,
|
|
||||||
start: start,
|
|
||||||
statusId: stateId,
|
|
||||||
terminationReason: "verstorben",
|
|
||||||
};
|
|
||||||
await MembershipCommandHandler.update(updateMembership);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!queryResultElement.Ausgetreten && !queryResultElement.verstorben && queryResultElement.verzogen) {
|
|
||||||
let end = new Date('2024-12-31');
|
|
||||||
if (queryResultElement.verzogenDatum) {
|
|
||||||
end = queryResultElement.verzogenDatum;
|
|
||||||
} else {
|
|
||||||
if (queryResultElement["Übergang Passiv"]) {
|
|
||||||
end = queryResultElement["Übergang Passiv"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let updateMembership: UpdateMembershipCommand = {
|
|
||||||
end: end,
|
|
||||||
id: ms1,
|
|
||||||
memberId: memberId,
|
|
||||||
start: start,
|
|
||||||
statusId: stateId,
|
|
||||||
terminationReason: "verzogen",
|
|
||||||
};
|
|
||||||
await MembershipCommandHandler.update(updateMembership);
|
|
||||||
}
|
|
||||||
|
|
||||||
// add member's awards
|
|
||||||
for (const awardId in sAwardsById) {
|
|
||||||
const awardName = sAwardsById[awardId];
|
|
||||||
if (queryResultElement[awardName]) {
|
|
||||||
const awardDate = queryResultElement[awardName];
|
|
||||||
console.log(`Member ${newMember.firstname} ${newMember.lastname} got award ${awardName} at ${awardDate}`);
|
|
||||||
|
|
||||||
let given = true;
|
|
||||||
let note = "";
|
|
||||||
if (awardName === "Ehrung 25 Jahre aktiv" && queryResultElement["keineEhrung25JahreAktivGrund"] !== null) {
|
|
||||||
given = false;
|
|
||||||
note = queryResultElement["keineEhrung25JahreAktivGrund"].trim();
|
|
||||||
}
|
|
||||||
if (awardName === "Ehrennadel Silber" && queryResultElement["keineEhrennadelSilberGrund"] !== null) {
|
|
||||||
given = false;
|
|
||||||
note = queryResultElement["keineEhrennadelSilberGrund"].trim();
|
|
||||||
}
|
|
||||||
if (awardName === "Ehrennadel Gold" && queryResultElement["keineEhrennadelGoldGrund"] !== null) {
|
|
||||||
given = false;
|
|
||||||
note = queryResultElement["keineEhrennadelGoldGrund"].trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
let newMemberAward: CreateMemberAwardCommand = {
|
|
||||||
awardId: parseInt(awardId), date: awardDate, given: given, memberId: memberId, note: note,
|
|
||||||
};
|
|
||||||
await MemberAwardCommandHandler.create(newMemberAward);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// add member's executive positions (if there are any)
|
|
||||||
let memberPositionsResult: any[] = await db('MitgliedFunktionen').where('MitgliedFunktionen.IdMitglied', '=', ineternalID);
|
|
||||||
for (const position of memberPositionsResult) {
|
|
||||||
const start = position.Beginn;
|
|
||||||
const end = position.Ende;
|
|
||||||
const executivePositionId: number = sFunktionenByOldId[position.IdFunktion]
|
|
||||||
let createMemberExecutivePosition: CreateMemberExecutivePositionCommand = {
|
|
||||||
end: end, executivePositionId: executivePositionId, memberId: memberId, note: "", start: start
|
|
||||||
}
|
|
||||||
console.log(`Adding position ${executivePositionId} to ${newMember.firstname} ${newMember.lastname}`);
|
|
||||||
await MemberExecutivePositionCommandHandler.create(createMemberExecutivePosition);
|
|
||||||
}
|
|
||||||
|
|
||||||
// add communications
|
|
||||||
if (!queryResultElement.verzogen) {
|
|
||||||
const createCommunicationCommands: CreateCommunicationCommand[] = [];
|
|
||||||
let havePreferred = false;
|
|
||||||
if (queryResultElement.Mobiltelefon && !queryResultElement.verstorben && !queryResultElement.verzogenDatum) {
|
|
||||||
const createCommunicationMobile: CreateCommunicationCommand = {
|
|
||||||
memberId: memberId,
|
|
||||||
typeId: sCommTypesByName['SMS'],
|
|
||||||
isSMSAlarming: false,
|
|
||||||
city: "",
|
|
||||||
email: "",
|
|
||||||
mobile: queryResultElement.Mobiltelefon.trim(),
|
|
||||||
postalCode: "",
|
|
||||||
preferred: false,
|
|
||||||
street: "",
|
|
||||||
streetNumber: 0,
|
|
||||||
streetNumberAddition: "",
|
|
||||||
}
|
|
||||||
createCommunicationCommands.push(createCommunicationMobile);
|
|
||||||
}
|
|
||||||
if (queryResultElement.email && !queryResultElement.verstorben && !queryResultElement.verzogenDatum) {
|
|
||||||
const createCommunicationEmail: CreateCommunicationCommand = {
|
|
||||||
memberId: memberId,
|
|
||||||
typeId: sCommTypesByName['Email'],
|
|
||||||
isSMSAlarming: false,
|
|
||||||
city: "",
|
|
||||||
email: queryResultElement.email,
|
|
||||||
mobile: "",
|
|
||||||
preferred: queryResultElement.EinladungNurPerEmail,
|
|
||||||
postalCode: "",
|
|
||||||
street: "",
|
|
||||||
streetNumber: 0,
|
|
||||||
streetNumberAddition: "",
|
|
||||||
}
|
|
||||||
if (createCommunicationEmail.preferred) {
|
|
||||||
havePreferred = true;
|
|
||||||
}
|
|
||||||
createCommunicationCommands.push(createCommunicationEmail);
|
|
||||||
}
|
|
||||||
if (queryResultElement.Mobiltelefon && queryResultElement.EinladungNurPerEmail && !queryResultElement.email && !queryResultElement.verstorben && !queryResultElement.verzogenDatum) {
|
|
||||||
const preferred = havePreferred === false;
|
|
||||||
const createCommunicationWhatsApp: CreateCommunicationCommand = {
|
|
||||||
memberId: memberId,
|
|
||||||
typeId: sCommTypesByName['WhatsApp'],
|
|
||||||
isSMSAlarming: false,
|
|
||||||
city: "",
|
|
||||||
email: "",
|
|
||||||
mobile: queryResultElement.Mobiltelefon.trim(),
|
|
||||||
postalCode: "",
|
|
||||||
preferred: preferred,
|
|
||||||
street: "",
|
|
||||||
streetNumber: 0,
|
|
||||||
streetNumberAddition: "",
|
|
||||||
}
|
|
||||||
if (createCommunicationWhatsApp.preferred) {
|
|
||||||
havePreferred = true;
|
|
||||||
}
|
|
||||||
createCommunicationCommands.push(createCommunicationWhatsApp);
|
|
||||||
}
|
|
||||||
if (queryResultElement.Ort && queryResultElement.street) {
|
|
||||||
const preferred = havePreferred === false;
|
|
||||||
const createCommunicationMail: CreateCommunicationCommand = {
|
|
||||||
memberId: memberId,
|
|
||||||
typeId: sCommTypesByName['Post'],
|
|
||||||
isSMSAlarming: false,
|
|
||||||
city: queryResultElement.Ort,
|
|
||||||
email: "",
|
|
||||||
mobile: "",
|
|
||||||
preferred: preferred,
|
|
||||||
postalCode: queryResultElement.PLZ,
|
|
||||||
street: queryResultElement.street.trim(),
|
|
||||||
streetNumber: queryResultElement.streetnumber,
|
|
||||||
streetNumberAddition: queryResultElement.streetnumberaddition.trim(),
|
|
||||||
}
|
|
||||||
if (createCommunicationMail.preferred) {
|
|
||||||
havePreferred = true;
|
|
||||||
}
|
|
||||||
createCommunicationCommands.push(createCommunicationMail);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (createCommunicationCommands.length > 0 && !havePreferred) {
|
|
||||||
console.log(`No preferred communication for ${newMember.firstname} ${newMember.lastname}`)
|
|
||||||
}
|
|
||||||
for (const createCommunicationCommand of createCommunicationCommands) {
|
|
||||||
console.log(`Adding communication for ${sCommTypesById[createCommunicationCommand.typeId]} to ${newMember.firstname} ${newMember.lastname}`)
|
|
||||||
await CommunicationCommandHandler.create(createCommunicationCommand);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
res.sendStatus(200);
|
|
||||||
});
|
|
||||||
|
|
||||||
export default router;
|
|
|
@ -13,7 +13,11 @@ import reset from "./reset";
|
||||||
import auth from "./auth";
|
import auth from "./auth";
|
||||||
import admin from "./admin/index";
|
import admin from "./admin/index";
|
||||||
import user from "./user";
|
import user from "./user";
|
||||||
import fillfromold from "./fillfromold";
|
import detectPWA from "../middleware/detectPWA";
|
||||||
|
import api from "./webapi";
|
||||||
|
import authenticateAPI from "../middleware/authenticateAPI";
|
||||||
|
import server from "./server";
|
||||||
|
import PermissionHelper from "../helpers/permissionHelper";
|
||||||
|
|
||||||
export default (app: Express) => {
|
export default (app: Express) => {
|
||||||
app.set("query parser", "extended");
|
app.set("query parser", "extended");
|
||||||
|
@ -26,14 +30,16 @@ export default (app: Express) => {
|
||||||
app.use(cors());
|
app.use(cors());
|
||||||
app.options("*", cors());
|
app.options("*", cors());
|
||||||
|
|
||||||
|
app.use(detectPWA);
|
||||||
app.use("/api/public", publicAvailable);
|
app.use("/api/public", publicAvailable);
|
||||||
app.use("/api/setup", allowSetup, setup);
|
app.use("/api/setup", allowSetup, setup);
|
||||||
app.use("/api/fillfromold", fillfromold);
|
|
||||||
app.use("/api/reset", reset);
|
app.use("/api/reset", reset);
|
||||||
app.use("/api/invite", invite);
|
app.use("/api/invite", invite);
|
||||||
app.use("/api/auth", auth);
|
app.use("/api/auth", auth);
|
||||||
|
app.use("/api/webapi", authenticateAPI, api);
|
||||||
app.use(authenticate);
|
app.use(authenticate);
|
||||||
app.use("/api/admin", admin);
|
app.use("/api/admin", admin);
|
||||||
app.use("/api/user", user);
|
app.use("/api/user", user);
|
||||||
|
app.use("/api/server", PermissionHelper.isAdminMiddleware(), server);
|
||||||
app.use(errorHandler);
|
app.use(errorHandler);
|
||||||
};
|
};
|
||||||
|
|
35
src/routes/server.ts
Normal file
35
src/routes/server.ts
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import express, { Request, Response } from "express";
|
||||||
|
import { FileSystemHelper } from "../helpers/fileSystemHelper";
|
||||||
|
import Parser from "rss-parser";
|
||||||
|
|
||||||
|
var router = express.Router({ mergeParams: true });
|
||||||
|
|
||||||
|
router.get("/version", async (req: Request, res: Response) => {
|
||||||
|
let serverPackage = FileSystemHelper.readTemplateFile("/package.json");
|
||||||
|
let serverJson = JSON.parse(serverPackage);
|
||||||
|
res.send({
|
||||||
|
name: serverJson.name,
|
||||||
|
description: serverJson.description,
|
||||||
|
version: serverJson.version,
|
||||||
|
author: serverJson.author,
|
||||||
|
license: serverJson.license,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get("/settings", async (req: Request, res: Response) => {
|
||||||
|
res.json({});
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get("/serverrss", async (req: Request, res: Response) => {
|
||||||
|
const parser = new Parser();
|
||||||
|
let feed = await parser.parseURL("https://forgejo.jk-effects.cloud/Ehrenamt/ff-admin-server/releases.rss");
|
||||||
|
res.json(feed);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get("/clientrss", async (req: Request, res: Response) => {
|
||||||
|
const parser = new Parser();
|
||||||
|
let feed = await parser.parseURL("https://forgejo.jk-effects.cloud/Ehrenamt/ff-admin/releases.rss");
|
||||||
|
res.json(feed);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
10
src/routes/webapi.ts
Normal file
10
src/routes/webapi.ts
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import express, { Request, Response } from "express";
|
||||||
|
import { getWebApiAccess } from "../controller/webapiController";
|
||||||
|
|
||||||
|
var router = express.Router({ mergeParams: true });
|
||||||
|
|
||||||
|
router.get("/retrieve", async (req: Request, res: Response) => {
|
||||||
|
await getWebApiAccess(req, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
|
@ -12,6 +12,7 @@ export default abstract class CalendarService {
|
||||||
.getRepository(calendar)
|
.getRepository(calendar)
|
||||||
.createQueryBuilder("calendar")
|
.createQueryBuilder("calendar")
|
||||||
.leftJoinAndSelect("calendar.type", "type")
|
.leftJoinAndSelect("calendar.type", "type")
|
||||||
|
.orderBy("starttime", "ASC")
|
||||||
.getMany()
|
.getMany()
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
return res;
|
return res;
|
||||||
|
|
|
@ -16,6 +16,7 @@ export default abstract class CommunicationService {
|
||||||
.leftJoinAndSelect("communication.member", "member")
|
.leftJoinAndSelect("communication.member", "member")
|
||||||
.leftJoinAndSelect("member.sendNewsletter", "sendNewsletter")
|
.leftJoinAndSelect("member.sendNewsletter", "sendNewsletter")
|
||||||
.where("communication.memberId = :memberId", { memberId: memberId })
|
.where("communication.memberId = :memberId", { memberId: memberId })
|
||||||
|
.orderBy("communicationType.type", "ASC")
|
||||||
.getMany()
|
.getMany()
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
return res;
|
return res;
|
||||||
|
|
|
@ -14,6 +14,7 @@ export default abstract class MemberAwardService {
|
||||||
.createQueryBuilder("memberAwards")
|
.createQueryBuilder("memberAwards")
|
||||||
.leftJoinAndSelect("memberAwards.award", "award")
|
.leftJoinAndSelect("memberAwards.award", "award")
|
||||||
.where("memberAwards.memberId = :memberId", { memberId: memberId })
|
.where("memberAwards.memberId = :memberId", { memberId: memberId })
|
||||||
|
.orderBy("award.award", "ASC")
|
||||||
.getMany()
|
.getMany()
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
return res;
|
return res;
|
||||||
|
|
|
@ -14,6 +14,8 @@ export default abstract class MemberExecutivePositionService {
|
||||||
.createQueryBuilder("memberExecutivePositions")
|
.createQueryBuilder("memberExecutivePositions")
|
||||||
.leftJoinAndSelect("memberExecutivePositions.executivePosition", "executivePosition")
|
.leftJoinAndSelect("memberExecutivePositions.executivePosition", "executivePosition")
|
||||||
.where("memberExecutivePositions.memberId = :memberId", { memberId: memberId })
|
.where("memberExecutivePositions.memberId = :memberId", { memberId: memberId })
|
||||||
|
.orderBy("executivePosition.position", "ASC")
|
||||||
|
.addOrderBy("memberExecutivePositions.start", "DESC")
|
||||||
.getMany()
|
.getMany()
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
return res;
|
return res;
|
||||||
|
|
|
@ -14,6 +14,7 @@ export default abstract class MemberQualificationService {
|
||||||
.createQueryBuilder("memberQualifications")
|
.createQueryBuilder("memberQualifications")
|
||||||
.leftJoinAndSelect("memberQualifications.qualification", "qualification")
|
.leftJoinAndSelect("memberQualifications.qualification", "qualification")
|
||||||
.where("memberQualifications.memberId = :memberId", { memberId: memberId })
|
.where("memberQualifications.memberId = :memberId", { memberId: memberId })
|
||||||
|
.orderBy("qualification.qualification", "ASC")
|
||||||
.getMany()
|
.getMany()
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
return res;
|
return res;
|
||||||
|
|
|
@ -2,13 +2,26 @@ import { dataSource } from "../../../data-source";
|
||||||
import { member } from "../../../entity/club/member/member";
|
import { member } from "../../../entity/club/member/member";
|
||||||
import { membership } from "../../../entity/club/member/membership";
|
import { membership } from "../../../entity/club/member/membership";
|
||||||
import InternalException from "../../../exceptions/internalException";
|
import InternalException from "../../../exceptions/internalException";
|
||||||
|
import { memberView } from "../../../views/memberView";
|
||||||
|
|
||||||
export default abstract class MemberService {
|
export default abstract class MemberService {
|
||||||
/**
|
/**
|
||||||
* @description get all members
|
* @description get all members
|
||||||
* @returns {Promise<[Array<member>, number]>}
|
* @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
|
let query = dataSource
|
||||||
.getRepository(member)
|
.getRepository(member)
|
||||||
.createQueryBuilder("member")
|
.createQueryBuilder("member")
|
||||||
|
@ -54,9 +67,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
|
return await query
|
||||||
.offset(offset)
|
|
||||||
.limit(count)
|
|
||||||
.orderBy("member.lastname")
|
.orderBy("member.lastname")
|
||||||
.addOrderBy("member.firstname")
|
.addOrderBy("member.firstname")
|
||||||
.addOrderBy("member.nameaffix")
|
.addOrderBy("member.nameaffix")
|
||||||
|
@ -114,23 +133,45 @@ export default abstract class MemberService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description get member statistics by id
|
||||||
|
* @param {number} id
|
||||||
|
* @returns {Promise<memberView>}
|
||||||
|
*/
|
||||||
|
static async getStatisticsById(id: number): Promise<memberView> {
|
||||||
|
return await dataSource
|
||||||
|
.getRepository(memberView)
|
||||||
|
.createQueryBuilder("memberView")
|
||||||
|
.where("memberView.id = :id", { id: id })
|
||||||
|
.getOneOrFail()
|
||||||
|
.then((res) => {
|
||||||
|
return res;
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
throw new InternalException("memberView not found by id", err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description get members where membership is setz
|
* @description get members where membership is setz
|
||||||
* @returns {Promise<member>}
|
* @returns {Promise<member>}
|
||||||
*/
|
*/
|
||||||
static async getByRunningMembership(): Promise<Array<member>> {
|
static async getByRunningMembership(): Promise<Array<member>> {
|
||||||
return await dataSource
|
return await dataSource
|
||||||
.getRepository(member)
|
.getRepository(member)
|
||||||
.createQueryBuilder("member")
|
.createQueryBuilder("member")
|
||||||
.leftJoinAndSelect("member.memberships", "membership")
|
.leftJoinAndSelect("member.memberships", "membership")
|
||||||
.where("membership.end IS NULL")
|
.where("membership.end IS NULL")
|
||||||
.getMany()
|
.orderBy("member.lastname")
|
||||||
.then((res) => {
|
.addOrderBy("member.firstname")
|
||||||
return res;
|
.addOrderBy("member.nameaffix")
|
||||||
})
|
.getMany()
|
||||||
.catch((err) => {
|
.then((res) => {
|
||||||
throw new InternalException("member not found by id", err);
|
return res;
|
||||||
});
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
throw new InternalException("member not found by id", err);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { dataSource } from "../../../data-source";
|
import { dataSource } from "../../../data-source";
|
||||||
import { membership } from "../../../entity/club/member/membership";
|
import { membership } from "../../../entity/club/member/membership";
|
||||||
import InternalException from "../../../exceptions/internalException";
|
import InternalException from "../../../exceptions/internalException";
|
||||||
|
import { membershipView } from "../../../views/membershipsView";
|
||||||
|
|
||||||
export default abstract class MembershipService {
|
export default abstract class MembershipService {
|
||||||
/**
|
/**
|
||||||
|
@ -45,4 +46,23 @@ export default abstract class MembershipService {
|
||||||
throw new InternalException("member membership not found by id", err);
|
throw new InternalException("member membership not found by id", err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description get membership statistics by memberId
|
||||||
|
* @param {number} memberId
|
||||||
|
* @returns {Promise<Array<membershipView>>}
|
||||||
|
*/
|
||||||
|
static async getStatisticsById(memberId: number): Promise<Array<membershipView>> {
|
||||||
|
return await dataSource
|
||||||
|
.getRepository(membershipView)
|
||||||
|
.createQueryBuilder("membershipView")
|
||||||
|
.where("membershipView.memberId = :memberId", { memberId: memberId })
|
||||||
|
.getMany()
|
||||||
|
.then((res) => {
|
||||||
|
return res;
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
throw new InternalException("membershipView not found by id", err);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ export default abstract class AwardService {
|
||||||
return await dataSource
|
return await dataSource
|
||||||
.getRepository(award)
|
.getRepository(award)
|
||||||
.createQueryBuilder("award")
|
.createQueryBuilder("award")
|
||||||
|
.orderBy("award", "ASC")
|
||||||
.getMany()
|
.getMany()
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
return res;
|
return res;
|
||||||
|
|
|
@ -11,6 +11,7 @@ export default abstract class CalendarTypeService {
|
||||||
return await dataSource
|
return await dataSource
|
||||||
.getRepository(calendarType)
|
.getRepository(calendarType)
|
||||||
.createQueryBuilder("calendarType")
|
.createQueryBuilder("calendarType")
|
||||||
|
.orderBy("type", "ASC")
|
||||||
.getMany()
|
.getMany()
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
return res;
|
return res;
|
||||||
|
|
|
@ -11,6 +11,7 @@ export default abstract class CommunicationTypeService {
|
||||||
return await dataSource
|
return await dataSource
|
||||||
.getRepository(communicationType)
|
.getRepository(communicationType)
|
||||||
.createQueryBuilder("communicationType")
|
.createQueryBuilder("communicationType")
|
||||||
|
.orderBy("type", "ASC")
|
||||||
.getMany()
|
.getMany()
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
return res;
|
return res;
|
||||||
|
|
|
@ -12,6 +12,7 @@ export default abstract class ExecutivePositionService {
|
||||||
return await dataSource
|
return await dataSource
|
||||||
.getRepository(executivePosition)
|
.getRepository(executivePosition)
|
||||||
.createQueryBuilder("executivePosition")
|
.createQueryBuilder("executivePosition")
|
||||||
|
.orderBy("position", "ASC")
|
||||||
.getMany()
|
.getMany()
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
return res;
|
return res;
|
||||||
|
|
|
@ -12,6 +12,7 @@ export default abstract class MembershipStatusService {
|
||||||
return await dataSource
|
return await dataSource
|
||||||
.getRepository(membershipStatus)
|
.getRepository(membershipStatus)
|
||||||
.createQueryBuilder("membershipStatus")
|
.createQueryBuilder("membershipStatus")
|
||||||
|
.orderBy("status", "ASC")
|
||||||
.getMany()
|
.getMany()
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
return res;
|
return res;
|
||||||
|
|
|
@ -13,6 +13,7 @@ export default abstract class NewsletterConfigService {
|
||||||
.getRepository(newsletterConfig)
|
.getRepository(newsletterConfig)
|
||||||
.createQueryBuilder("newsletterConfig")
|
.createQueryBuilder("newsletterConfig")
|
||||||
.leftJoinAndSelect("newsletterConfig.comType", "comType")
|
.leftJoinAndSelect("newsletterConfig.comType", "comType")
|
||||||
|
.orderBy("comType.type", "ASC")
|
||||||
.getMany()
|
.getMany()
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
return res;
|
return res;
|
||||||
|
|
|
@ -13,6 +13,7 @@ export default abstract class QualificationService {
|
||||||
return await dataSource
|
return await dataSource
|
||||||
.getRepository(qualification)
|
.getRepository(qualification)
|
||||||
.createQueryBuilder("qualification")
|
.createQueryBuilder("qualification")
|
||||||
|
.orderBy("qualification", "ASC")
|
||||||
.getMany()
|
.getMany()
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
return res;
|
return res;
|
||||||
|
|
|
@ -11,6 +11,7 @@ export default abstract class QueryStoreService {
|
||||||
return await dataSource
|
return await dataSource
|
||||||
.getRepository(query)
|
.getRepository(query)
|
||||||
.createQueryBuilder("queryStore")
|
.createQueryBuilder("queryStore")
|
||||||
|
.orderBy("title", "ASC")
|
||||||
.getMany()
|
.getMany()
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
return res;
|
return res;
|
||||||
|
|
|
@ -12,6 +12,7 @@ export default abstract class TemplateService {
|
||||||
return await dataSource
|
return await dataSource
|
||||||
.getRepository(template)
|
.getRepository(template)
|
||||||
.createQueryBuilder("template")
|
.createQueryBuilder("template")
|
||||||
|
.orderBy("template", "ASC")
|
||||||
.getMany()
|
.getMany()
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
return res;
|
return res;
|
||||||
|
|
|
@ -14,6 +14,7 @@ export default abstract class TemplateUsageService {
|
||||||
.leftJoinAndSelect("templateUsage.header", "headerTemplate")
|
.leftJoinAndSelect("templateUsage.header", "headerTemplate")
|
||||||
.leftJoinAndSelect("templateUsage.body", "bodyTemplate")
|
.leftJoinAndSelect("templateUsage.body", "bodyTemplate")
|
||||||
.leftJoinAndSelect("templateUsage.footer", "footerTemplate")
|
.leftJoinAndSelect("templateUsage.footer", "footerTemplate")
|
||||||
|
.orderBy("scope", "ASC")
|
||||||
.getMany()
|
.getMany()
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
return res;
|
return res;
|
||||||
|
|
|
@ -12,6 +12,7 @@ export default abstract class RoleService {
|
||||||
.getRepository(role)
|
.getRepository(role)
|
||||||
.createQueryBuilder("role")
|
.createQueryBuilder("role")
|
||||||
.leftJoinAndSelect("role.permissions", "role_permissions")
|
.leftJoinAndSelect("role.permissions", "role_permissions")
|
||||||
|
.orderBy("role", "ASC")
|
||||||
.getMany()
|
.getMany()
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
return res;
|
return res;
|
||||||
|
|
|
@ -15,6 +15,8 @@ export default abstract class UserService {
|
||||||
.leftJoinAndSelect("user.roles", "roles")
|
.leftJoinAndSelect("user.roles", "roles")
|
||||||
.leftJoinAndSelect("user.permissions", "permissions")
|
.leftJoinAndSelect("user.permissions", "permissions")
|
||||||
.leftJoinAndSelect("roles.permissions", "role_permissions")
|
.leftJoinAndSelect("roles.permissions", "role_permissions")
|
||||||
|
.orderBy("firstname", "ASC")
|
||||||
|
.addOrderBy("lastname", "ASC")
|
||||||
.getMany()
|
.getMany()
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
return res;
|
return res;
|
||||||
|
|
24
src/service/user/webapiPermissionService.ts
Normal file
24
src/service/user/webapiPermissionService.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import { dataSource } from "../../data-source";
|
||||||
|
import { webapiPermission } from "../../entity/user/webapi_permission";
|
||||||
|
import InternalException from "../../exceptions/internalException";
|
||||||
|
|
||||||
|
export default abstract class WebapiPermissionService {
|
||||||
|
/**
|
||||||
|
* @description get permission by api
|
||||||
|
* @param webapiId number
|
||||||
|
* @returns {Promise<Array<webapiPermission>>}
|
||||||
|
*/
|
||||||
|
static async getByApi(webapiId: number): Promise<Array<webapiPermission>> {
|
||||||
|
return await dataSource
|
||||||
|
.getRepository(webapiPermission)
|
||||||
|
.createQueryBuilder("webapi_permission")
|
||||||
|
.where("webapi_permission.webapiId = :webapiId", { webapiId: webapiId })
|
||||||
|
.getMany()
|
||||||
|
.then((res) => {
|
||||||
|
return res;
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
throw new InternalException("webapi permissions not found by api", err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
83
src/service/user/webapiService.ts
Normal file
83
src/service/user/webapiService.ts
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
import { dataSource } from "../../data-source";
|
||||||
|
import { webapi } from "../../entity/user/webapi";
|
||||||
|
import InternalException from "../../exceptions/internalException";
|
||||||
|
|
||||||
|
export default abstract class WebapiService {
|
||||||
|
/**
|
||||||
|
* @description get apis
|
||||||
|
* @returns {Promise<Array<webapi>>}
|
||||||
|
*/
|
||||||
|
static async getAll(): Promise<Array<webapi>> {
|
||||||
|
return await dataSource
|
||||||
|
.getRepository(webapi)
|
||||||
|
.createQueryBuilder("webapi")
|
||||||
|
.leftJoinAndSelect("webapi.permissions", "permissions")
|
||||||
|
.getMany()
|
||||||
|
.then((res) => {
|
||||||
|
return res;
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
throw new InternalException("webapis not found", err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description get api by id
|
||||||
|
* @param id number
|
||||||
|
* @returns {Promise<webapi>}
|
||||||
|
*/
|
||||||
|
static async getById(id: number): Promise<webapi> {
|
||||||
|
return await dataSource
|
||||||
|
.getRepository(webapi)
|
||||||
|
.createQueryBuilder("webapi")
|
||||||
|
.leftJoinAndSelect("webapi.permissions", "permissions")
|
||||||
|
.where("webapi.id = :id", { id: id })
|
||||||
|
.getOneOrFail()
|
||||||
|
.then((res) => {
|
||||||
|
return res;
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
throw new InternalException("webapi not found by id", err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description get api by token
|
||||||
|
* @param token string
|
||||||
|
* @returns {Promise<webapi>}
|
||||||
|
*/
|
||||||
|
static async getByToken(token: string): Promise<webapi> {
|
||||||
|
return await dataSource
|
||||||
|
.getRepository(webapi)
|
||||||
|
.createQueryBuilder("webapi")
|
||||||
|
.leftJoinAndSelect("webapi.permissions", "permissions")
|
||||||
|
.where("webapi.token = :token", { token: token })
|
||||||
|
.getOneOrFail()
|
||||||
|
.then((res) => {
|
||||||
|
return res;
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
throw new InternalException("webapi not found by token", err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description get api by id
|
||||||
|
* @param id number
|
||||||
|
* @returns {Promise<webapi>}
|
||||||
|
*/
|
||||||
|
static async getTokenById(id: number): Promise<webapi> {
|
||||||
|
return await dataSource
|
||||||
|
.getRepository(webapi)
|
||||||
|
.createQueryBuilder("webapi")
|
||||||
|
.select("webapi.token")
|
||||||
|
.where("webapi.id = :id", { id: id })
|
||||||
|
.getOneOrFail()
|
||||||
|
.then((res) => {
|
||||||
|
return res;
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
throw new InternalException("webapi token not found by id", err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,9 +14,19 @@
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<h2>Anwesenheit ({{presence.length}})</h2>
|
<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>
|
<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 />
|
<br />
|
||||||
<h2>Agenda</h2>
|
<h2>Agenda</h2>
|
||||||
{{#each agenda}}
|
{{#each agenda}}
|
||||||
|
|
|
@ -14,6 +14,7 @@ export type PermissionModule =
|
||||||
| "calendar_type"
|
| "calendar_type"
|
||||||
| "user"
|
| "user"
|
||||||
| "role"
|
| "role"
|
||||||
|
| "webapi"
|
||||||
| "query"
|
| "query"
|
||||||
| "query_store"
|
| "query_store"
|
||||||
| "template"
|
| "template"
|
||||||
|
@ -55,6 +56,7 @@ export const permissionModules: Array<PermissionModule> = [
|
||||||
"calendar_type",
|
"calendar_type",
|
||||||
"user",
|
"user",
|
||||||
"role",
|
"role",
|
||||||
|
"webapi",
|
||||||
"query",
|
"query",
|
||||||
"query_store",
|
"query_store",
|
||||||
"template",
|
"template",
|
||||||
|
@ -75,5 +77,5 @@ export const sectionsAndModules: SectionsAndModulesObject = {
|
||||||
"template_usage",
|
"template_usage",
|
||||||
"newsletter_config",
|
"newsletter_config",
|
||||||
],
|
],
|
||||||
user: ["user", "role"],
|
user: ["user", "role", "webapi"],
|
||||||
};
|
};
|
||||||
|
|
|
@ -16,3 +16,15 @@ export interface MemberViewModel {
|
||||||
smsAlarming?: Array<CommunicationViewModel>;
|
smsAlarming?: Array<CommunicationViewModel>;
|
||||||
preferredCommunication?: Array<CommunicationViewModel>;
|
preferredCommunication?: Array<CommunicationViewModel>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface MemberStatisticsViewModel {
|
||||||
|
id: number;
|
||||||
|
salutation: Salutation;
|
||||||
|
firstname: string;
|
||||||
|
lastname: string;
|
||||||
|
nameaffix: string;
|
||||||
|
birthdate: Date;
|
||||||
|
todayAge: number;
|
||||||
|
ageThisYear: number;
|
||||||
|
exactAge: string;
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { Salutation } from "../../../../enums/salutation";
|
||||||
|
|
||||||
export interface MembershipViewModel {
|
export interface MembershipViewModel {
|
||||||
id: number;
|
id: number;
|
||||||
start: Date;
|
start: Date;
|
||||||
|
@ -6,3 +8,16 @@ export interface MembershipViewModel {
|
||||||
status: string;
|
status: string;
|
||||||
statusId: number;
|
statusId: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface MembershipStatisticsViewModel {
|
||||||
|
durationInDays: number;
|
||||||
|
durationInYears: string;
|
||||||
|
status: string;
|
||||||
|
statusId: number;
|
||||||
|
memberId: number;
|
||||||
|
memberSalutation: Salutation;
|
||||||
|
memberFirstname: string;
|
||||||
|
memberLastname: string;
|
||||||
|
memberNameaffix: string;
|
||||||
|
memberBirthdate: Date;
|
||||||
|
}
|
||||||
|
|
|
@ -3,5 +3,6 @@ import { MemberViewModel } from "../member/member.models";
|
||||||
export interface ProtocolPresenceViewModel {
|
export interface ProtocolPresenceViewModel {
|
||||||
memberId: number;
|
memberId: number;
|
||||||
absent: boolean;
|
absent: boolean;
|
||||||
|
excused: boolean;
|
||||||
protocolId: number;
|
protocolId: number;
|
||||||
}
|
}
|
||||||
|
|
10
src/viewmodel/admin/user/webapi.models.ts
Normal file
10
src/viewmodel/admin/user/webapi.models.ts
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import { PermissionObject } from "../../../type/permissionTypes";
|
||||||
|
|
||||||
|
export interface ApiViewModel {
|
||||||
|
id: number;
|
||||||
|
permissions: PermissionObject;
|
||||||
|
title: string;
|
||||||
|
createdAt: Date;
|
||||||
|
lastUsage?: Date;
|
||||||
|
expiry?: Date;
|
||||||
|
}
|
|
@ -43,5 +43,5 @@ export class memberView {
|
||||||
ageThisYear: number;
|
ageThisYear: number;
|
||||||
|
|
||||||
@ViewColumn()
|
@ViewColumn()
|
||||||
exactAge: Date;
|
exactAge: string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ export class membershipView {
|
||||||
durationInDays: number;
|
durationInDays: number;
|
||||||
|
|
||||||
@ViewColumn()
|
@ViewColumn()
|
||||||
durationInYears: Date;
|
durationInYears: string;
|
||||||
|
|
||||||
@ViewColumn()
|
@ViewColumn()
|
||||||
status: string;
|
status: string;
|
||||||
|
|
Loading…
Reference in a new issue