provide ics link

This commit is contained in:
Julian Krauser 2024-11-07 10:49:34 +01:00
parent 8c597fd68d
commit 91c3fde688
8 changed files with 155 additions and 4 deletions

88
package-lock.json generated
View file

@ -12,7 +12,9 @@
"cors": "^2.8.5", "cors": "^2.8.5",
"dotenv": "^16.4.5", "dotenv": "^16.4.5",
"express": "^5.0.0-beta.3", "express": "^5.0.0-beta.3",
"ics": "^3.8.1",
"jsonwebtoken": "^9.0.2", "jsonwebtoken": "^9.0.2",
"moment": "^2.30.1",
"ms": "^2.1.3", "ms": "^2.1.3",
"mysql": "^2.18.1", "mysql": "^2.18.1",
"node-schedule": "^2.1.1", "node-schedule": "^2.1.1",
@ -1440,6 +1442,17 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/ics": {
"version": "3.8.1",
"resolved": "https://registry.npmjs.org/ics/-/ics-3.8.1.tgz",
"integrity": "sha512-UqQlfkajfhrS4pUGQfGIJMYz/Jsl/ob3LqcfEhUmLbwumg+ZNkU0/6S734Vsjq3/FYNpEcZVKodLBoe+zBM69g==",
"license": "ISC",
"dependencies": {
"nanoid": "^3.1.23",
"runes2": "^1.1.2",
"yup": "^1.2.0"
}
},
"node_modules/ieee754": { "node_modules/ieee754": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
@ -1742,6 +1755,15 @@
"optional": true, "optional": true,
"peer": true "peer": true
}, },
"node_modules/moment": {
"version": "2.30.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
"license": "MIT",
"engines": {
"node": "*"
}
},
"node_modules/ms": { "node_modules/ms": {
"version": "2.1.3", "version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@ -1777,6 +1799,24 @@
"thenify-all": "^1.0.0" "thenify-all": "^1.0.0"
} }
}, },
"node_modules/nanoid": {
"version": "3.3.7",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"bin": {
"nanoid": "bin/nanoid.cjs"
},
"engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/napi-build-utils": { "node_modules/napi-build-utils": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz",
@ -2014,6 +2054,12 @@
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
}, },
"node_modules/property-expr": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz",
"integrity": "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==",
"license": "MIT"
},
"node_modules/proxy-addr": { "node_modules/proxy-addr": {
"version": "2.0.7", "version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
@ -2159,6 +2205,12 @@
"node": ">= 0.10" "node": ">= 0.10"
} }
}, },
"node_modules/runes2": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/runes2/-/runes2-1.1.4.tgz",
"integrity": "sha512-LNPnEDPOOU4ehF71m5JoQyzT2yxwD6ZreFJ7MxZUAoMKNMY1XrAo60H1CUoX5ncSm0rIuKlqn9JZNRrRkNou2g==",
"license": "MIT"
},
"node_modules/safe-buffer": { "node_modules/safe-buffer": {
"version": "5.2.1", "version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
@ -2657,6 +2709,12 @@
"node": ">=0.8" "node": ">=0.8"
} }
}, },
"node_modules/tiny-case": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/tiny-case/-/tiny-case-1.0.3.tgz",
"integrity": "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==",
"license": "MIT"
},
"node_modules/toidentifier": { "node_modules/toidentifier": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
@ -2665,6 +2723,12 @@
"node": ">=0.6" "node": ">=0.6"
} }
}, },
"node_modules/toposort": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz",
"integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==",
"license": "MIT"
},
"node_modules/ts-node": { "node_modules/ts-node": {
"version": "10.7.0", "version": "10.7.0",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.7.0.tgz", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.7.0.tgz",
@ -2726,6 +2790,18 @@
"node": "*" "node": "*"
} }
}, },
"node_modules/type-fest": {
"version": "2.19.0",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz",
"integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==",
"license": "(MIT OR CC0-1.0)",
"engines": {
"node": ">=12.20"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/type-is": { "node_modules/type-is": {
"version": "1.6.18", "version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
@ -3120,6 +3196,18 @@
"engines": { "engines": {
"node": ">=6" "node": ">=6"
} }
},
"node_modules/yup": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/yup/-/yup-1.4.0.tgz",
"integrity": "sha512-wPbgkJRCqIf+OHyiTBQoJiP5PFuAXaWiJK6AmYkzQAh5/c2K9hzSApBZG5wV9KoKSePF7sAxmNSvh/13YHkFDg==",
"license": "MIT",
"dependencies": {
"property-expr": "^2.0.5",
"tiny-case": "^1.0.3",
"toposort": "^2.0.2",
"type-fest": "^2.19.0"
}
} }
} }
} }

View file

@ -27,7 +27,9 @@
"cors": "^2.8.5", "cors": "^2.8.5",
"dotenv": "^16.4.5", "dotenv": "^16.4.5",
"express": "^5.0.0-beta.3", "express": "^5.0.0-beta.3",
"ics": "^3.8.1",
"jsonwebtoken": "^9.0.2", "jsonwebtoken": "^9.0.2",
"moment": "^2.30.1",
"ms": "^2.1.3", "ms": "^2.1.3",
"mysql": "^2.18.1", "mysql": "^2.18.1",
"node-schedule": "^2.1.1", "node-schedule": "^2.1.1",

View file

@ -43,6 +43,14 @@ export default abstract class CalendarCommandHandler {
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
static async update(updateCalendar: UpdateCalendarCommand): Promise<void> { static async update(updateCalendar: UpdateCalendarCommand): Promise<void> {
let sequence = await dataSource
.getRepository(calendar)
.createQueryBuilder("calendar")
.where("id = :id", { id: updateCalendar.id })
.getOneOrFail()
.then((res) => {
return res.sequence;
});
return await dataSource return await dataSource
.createQueryBuilder() .createQueryBuilder()
.update(calendar) .update(calendar)
@ -58,6 +66,7 @@ export default abstract class CalendarCommandHandler {
.createQueryBuilder("type") .createQueryBuilder("type")
.where("id = :id", { id: updateCalendar.typeId }) .where("id = :id", { id: updateCalendar.typeId })
.getOneOrFail(), .getOneOrFail(),
sequence: sequence + 1,
}) })
.where("id = :id", { id: updateCalendar.id }) .where("id = :id", { id: updateCalendar.id })
.execute() .execute()

View file

@ -31,7 +31,7 @@ export async function getAllCalendarItems(req: Request, res: Response): Promise<
* @returns {Promise<*>} * @returns {Promise<*>}
*/ */
export async function getCalendarItemById(req: Request, res: Response): Promise<any> { export async function getCalendarItemById(req: Request, res: Response): Promise<any> {
const id = parseInt(req.params.id); const id = req.params.id;
let item = await CalendarService.getById(id); let item = await CalendarService.getById(id);
res.json(CalendarFactory.mapToSingle(item)); res.json(CalendarFactory.mapToSingle(item));

View file

@ -3,6 +3,8 @@ import CalendarFactory from "../factory/admin/calendar";
import CalendarService from "../service/calendarService"; import CalendarService from "../service/calendarService";
import CalendarTypeService from "../service/calendarTypeService"; import CalendarTypeService from "../service/calendarTypeService";
import { calendar } from "../entity/calendar"; import { calendar } from "../entity/calendar";
import { createEvents } from "ics";
import moment from "moment";
/** /**
* @description get all calendar items by types or nscdr * @description get all calendar items by types or nscdr
@ -21,5 +23,49 @@ export async function getCalendarItemsByTypes(req: Request, res: Response): Prom
items = await CalendarService.getByTypeNSCDR(); items = await CalendarService.getByTypeNSCDR();
} }
res.json(CalendarFactory.mapToBase(items)); moment()
.format("YYYY-M-D-H-m")
.split("-")
.map((a) => parseInt(a));
let events = createEvents(
items.map((i) => ({
calName: process.env.CLUB_NAME,
uid: i.id,
sequence: 1,
start: moment(i.starttime)
.format("YYYY-M-D-H-m")
.split("-")
.map((a) => parseInt(a)) as [number, number, number, number, number],
end: moment(i.endtime)
.format("YYYY-M-D-H-m")
.split("-")
.map((a) => parseInt(a)) as [number, number, number, number, number],
title: i.title,
description: i.content,
location: i.location,
categories: [i.type.type],
created: moment(i.createdAt)
.format("YYYY-M-D-H-m")
.split("-")
.map((a) => parseInt(a)) as [number, number, number, number, number],
lastModified: moment(i.updatedAt)
.format("YYYY-M-D-H-m")
.split("-")
.map((a) => parseInt(a)) as [number, number, number, number, number],
transp: "OPAQUE" as "OPAQUE",
url: "https://www.ff-merching.de",
alarms: [
{
action: "display",
description: "Erinnerung",
trigger: {
minutes: 30,
before: true,
},
},
],
}))
);
res.type("ics").send(events.value);
} }

View file

@ -6,6 +6,8 @@ import {
PrimaryGeneratedColumn, PrimaryGeneratedColumn,
CreateDateColumn, CreateDateColumn,
UpdateDateColumn, UpdateDateColumn,
AfterUpdate,
BeforeUpdate,
} from "typeorm"; } from "typeorm";
import { calendarType } from "./calendarType"; import { calendarType } from "./calendarType";
@ -32,6 +34,9 @@ export class calendar {
@Column({ type: "boolean", default: false }) @Column({ type: "boolean", default: false })
allDay: boolean; allDay: boolean;
@Column({ type: "int", default: 1 })
sequence: number;
@CreateDateColumn() @CreateDateColumn()
createdAt: Date; createdAt: Date;

View file

@ -30,6 +30,7 @@ export class Calendar1729947763295 implements MigrationInterface {
{ name: "content", type: "text", isNullable: true }, { name: "content", type: "text", isNullable: true },
{ name: "allDay", type: "tinyint", isNullable: false, default: 0 }, { name: "allDay", type: "tinyint", isNullable: false, default: 0 },
{ name: "location", type: "text", isNullable: true }, { name: "location", type: "text", isNullable: true },
{ name: "sequence", type: variableType_int, default: 1 },
{ name: "createdAt", type: "datetime", precision: 6, isNullable: false, default: "CURRENT_TIMESTAMP(6)" }, { name: "createdAt", type: "datetime", precision: 6, isNullable: false, default: "CURRENT_TIMESTAMP(6)" },
{ {
name: "updatedAt", name: "updatedAt",

View file

@ -25,7 +25,7 @@ export default abstract class CalendarService {
* @description get calendar by id * @description get calendar by id
* @returns {Promise<calendar>} * @returns {Promise<calendar>}
*/ */
static async getById(id: number): Promise<calendar> { static async getById(id: string): Promise<calendar> {
return await dataSource return await dataSource
.getRepository(calendar) .getRepository(calendar)
.createQueryBuilder("calendar") .createQueryBuilder("calendar")
@ -68,7 +68,7 @@ export default abstract class CalendarService {
.getRepository(calendar) .getRepository(calendar)
.createQueryBuilder("calendar") .createQueryBuilder("calendar")
.leftJoinAndSelect("calendar.type", "type") .leftJoinAndSelect("calendar.type", "type")
.where("type.nscdr = :nscdr)", { nscdr: true }) .where("type.nscdr = :nscdr", { nscdr: true })
.getMany() .getMany()
.then((res) => { .then((res) => {
return res; return res;