diff --git a/app.ts b/app.ts index c2acceb..d0a5498 100644 --- a/app.ts +++ b/app.ts @@ -7,6 +7,7 @@ import {highlightMarkerRouter} from './src/models/twitch-highlight-marker/Highli import {dhbwServiceRouter} from './src/models/dhbw-service/DHBWService.router'; import logger from './src/middleware/logger'; import {dhbwRaPlaChangesRouter} from './src/models/dhbw-rapla-changes/DHBWRaPlaChanges.router'; +import {raPlaMiddlewareRouter} from './src/models/rapla-middleware/RaPlaMiddleware.router'; let cors = require('cors'); @@ -33,6 +34,7 @@ app.use('/dhbw-service', dhbwServiceRouter); app.use('/twitch-highlight-marker', highlightMarkerRouter); app.use('/partyplaner', partyPlanerRouter); app.use('/raplachanges', dhbwRaPlaChangesRouter); +app.use('/rapla-middleware', raPlaMiddlewareRouter); // this is a simple route to make sure everything is working properly app.get('/', (req: express.Request, res: express.Response) => { diff --git a/src/models/rapla-middleware/RaPlaMiddleware.router.ts b/src/models/rapla-middleware/RaPlaMiddleware.router.ts new file mode 100644 index 0000000..f9e47b2 --- /dev/null +++ b/src/models/rapla-middleware/RaPlaMiddleware.router.ts @@ -0,0 +1,63 @@ +/** + * Required External Modules and Interfaces + */ +import express, {Request, Response} from 'express'; +import logger from '../../middleware/logger'; +import {Guid} from 'guid-typescript'; +import * as icalgenerator from './icalgenerator/icalgenerator.service'; + +/** + * Router Definition + */ +export const raPlaMiddlewareRouter = express.Router(); + +raPlaMiddlewareRouter.get('/', async (req: Request, res: Response) => { + try { + let user = (req.query.user ?? '').toString(); + let file = (req.query.file ?? '').toString(); + let blockers = (req.query.blockers ?? '').toString() === '1'; + let wahl = (req.query.wahl ?? '').toString(); + let pflicht = (req.query.pflicht ?? '').toString(); + + const allowedCharsRegex: RegExp = /^[a-zA-Z0-9]+$/; + + if (user === '' || file === '') { + res.status(400).send('Please at least provide user and file for RaPla!'); + return; + } else if (!allowedCharsRegex.test(user) || !allowedCharsRegex.test(file)) { + res.status(400).send('Please provide a valid user and file!'); + return; + } + + let resultingFile = await icalgenerator.getiCalFile(user, file, blockers, wahl, pflicht); + + res.set({'Content-Disposition': 'attachment; filename=' + file + '.ics'}); + res.status(200).send(resultingFile); + } catch (e) { + let errorGuid = Guid.create().toString(); + logger.error('Error handling a request: ' + e.message, {reference: errorGuid}); + res.status(500).send({ + 'status': 'PROCESSING_ERROR', + 'message': 'Internal Server Error. Try again later.', + 'reference': errorGuid + }); + } +}); + +let electiveModules = { + 1: {name: ''}, + 2: {name: ''}, + 3: {name: ''}, + 4: {name: ''}, + 5: {name: ''}, + 6: {name: ''}, + 7: {name: ''} +}; + +let profileModules = { + 1: {name: ''}, + 2: {name: ''}, + 3: {name: ''}, + 4: {name: ''}, + 5: {name: ''} +}; diff --git a/src/models/rapla-middleware/icalgenerator/icalgenerator.service.ts b/src/models/rapla-middleware/icalgenerator/icalgenerator.service.ts new file mode 100644 index 0000000..2326480 --- /dev/null +++ b/src/models/rapla-middleware/icalgenerator/icalgenerator.service.ts @@ -0,0 +1,86 @@ +const fetch = require('node-fetch'); + +export const getiCalFile = async (user: string, file: string, showBlockers: boolean, electiveModule: string, profileModule: string): Promise => { + try { + let iCalFileContents = ''; + + let baseUrl = 'https://rapla.dhbw-karlsruhe.de/rapla?page=ical'; + baseUrl += '&user=' + user; + baseUrl += '&file=' + file; + + const response = await fetch(baseUrl); + + let body = await response.text(); + + // Parse the ical file + let iCalFile = parseIcal(body); + + // Now remove unwanted shit from the iCal file + if (!showBlockers) { + iCalFile = removeBlockers(iCalFile); + } + + // Turn into one file again + let resultingFile = serializeIcal(iCalFile); + + return resultingFile; + } catch (err) { + throw err; + } +}; + +export const parseIcal = function (icalContents: string): iCalFile { + const fileHeaderRegex: RegExp = /^BEGIN:VCALENDAR.*?BEGIN:VEVENT$/ms; + const eventRegex: RegExp = /^BEGIN:VEVENT.*?END:VEVENT$/msg; + + let headerMatch = fileHeaderRegex.exec(icalContents); + let header = ''; + + if (headerMatch) { + header = headerMatch[0]; + } + + header = header.substr(0, header.lastIndexOf('\n')); + + let events: string[] = []; + let match: any; + while (match = eventRegex.exec(icalContents)) { + if (match) { + events.push(match[0]); + } + } + + return { + head: header, + body: events + }; +}; + +export const serializeIcal = function (ical: iCalFile): string { + let resString = ical.head; + ical.body.forEach((event) => { + resString += event + '\n'; + }); + resString += 'END:VCALENDAR'; + + return resString; +}; + +export const removeBlockers = function (ical: iCalFile): iCalFile { + let remainingEvents: string[] = []; + + ical.body.forEach((event) => { + if (!event.includes('CATEGORIES:Sonstiger Termin') && !event.includes('SUMMARY:Präsenz')) { + remainingEvents.push(event); + } + }); + + ical.body = remainingEvents; + + return ical; +}; + +export interface iCalFile { + head: string; + body: string[]; +}