From 0a76427ba83670da8ac71d6f82c6d7c755f34c72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thea=20Sch=C3=B6bl?= Date: Tue, 25 Apr 2023 15:54:06 +0200 Subject: [PATCH] feat: migrate backend to cosmiconfig --- .../backend/config/b-tu/elasticsearchrc.js | 20 + backend/backend/config/default-b-tu.ts | 11 - backend/backend/config/default-f-u.ts | 610 -------------- backend/backend/config/default-fb-fh.ts | 11 - backend/backend/config/default-ks-ug.ts | 11 - backend/backend/config/default.ts | 741 ------------------ backend/backend/config/default/app/index.js | 27 + .../config/default/app/language-setting.js | 34 + backend/backend/config/default/app/menu.js | 195 +++++ .../config/default/app/user-group-setting.js | 40 + .../config/default/backend/aggregations.js | 51 ++ .../config/default/backend/boostings.js | 103 +++ .../backend/config/default/backend/index.js | 73 ++ backend/backend/config/default/backendrc.js | 26 + .../backend/config/default/elasticsearchrc.js | 33 + .../prometheusrc.json} | 0 .../backend/config/default/tools/markdown.js | 29 + .../config/default/tools/semester-acronym.js | 34 + backend/backend/config/elasticsearch-b-tu.ts | 21 - backend/backend/config/elasticsearch.ts | 35 - .../config/f-u/about-pages/about.de.md | 7 + .../config/f-u/about-pages/about.en.md | 6 + .../backend/config/f-u/about-pages/about.js | 126 +++ .../backend/config/f-u/about-pages/imprint.js | 27 + .../backend/config/f-u/about-pages/index.js | 13 + .../config/f-u/about-pages/privacy.de.md | 166 ++++ .../config/f-u/about-pages/privacy.en.md | 166 ++++ .../backend/config/f-u/about-pages/privacy.js | 15 + backend/backend/config/f-u/backendrc.js | 82 ++ backend/backend/package.json | 45 +- backend/backend/src/app.ts | 24 +- backend/backend/src/common.ts | 13 +- backend/backend/src/config.ts | 54 ++ backend/backend/src/environment.d.ts | 14 + backend/backend/src/routes/index-route.ts | 8 +- .../backend/src/routes/multi-search-route.ts | 5 +- .../src/routes/plugin-register-route.ts | 11 +- .../src/routes/virtual-plugin-route.ts | 5 +- .../storage/elasticsearch/elasticsearch.ts | 1 + backend/backend/test/app.spec.ts | 6 +- backend/backend/test/common.spec.ts | 2 +- backend/backend/test/common.ts | 4 +- .../notification/backend-transport.spec.ts | 5 +- .../backend/test/routes/bulk-route.spec.ts | 6 +- .../test/routes/plugin-register-route.spec.ts | 19 +- backend/backend/test/routes/route.spec.ts | 2 +- .../backend/test/routes/search-route.spec.ts | 4 +- .../test/routes/thing-update-route.spec.ts | 6 +- .../test/routes/virtual-plugin-route.spec.ts | 14 +- .../backend/test/storage/bulk-storage.spec.ts | 4 +- .../elasticsearch/elasticsearch.spec.ts | 46 +- .../test/storage/elasticsearch/query.spec.ts | 2 +- backend/backend/tsconfig.json | 6 +- configuration/backend-config/about-page.xsd | 53 ++ configuration/backend-config/menu.xsd | 32 + configuration/backend-config/package.json | 17 + .../backend-config/test/about-page.xml | 44 ++ configuration/backend-config/test/menu.de.xml | 18 + configuration/backend-config/test/menu.xml | 18 + configuration/backend-config/tsconfig.json | 3 + configuration/eslint-config/index.js | 4 +- configuration/eslint-config/package.json | 2 +- packages/api/test/bulk.spec.ts | 2 +- packages/api/test/connector-client.spec.ts | 29 +- packages/api/test/e2e.spec.ts | 18 +- packages/api/test/http-client.spec.ts | 2 +- packages/api/test/plugin.spec.ts | 10 +- packages/core-tools/src/validate.ts | 10 +- packages/core/test/schema.spec.ts | 5 +- pnpm-lock.yaml | 135 ++-- 70 files changed, 1786 insertions(+), 1635 deletions(-) create mode 100644 backend/backend/config/b-tu/elasticsearchrc.js delete mode 100644 backend/backend/config/default-b-tu.ts delete mode 100644 backend/backend/config/default-f-u.ts delete mode 100644 backend/backend/config/default-fb-fh.ts delete mode 100644 backend/backend/config/default-ks-ug.ts delete mode 100644 backend/backend/config/default.ts create mode 100644 backend/backend/config/default/app/index.js create mode 100644 backend/backend/config/default/app/language-setting.js create mode 100644 backend/backend/config/default/app/menu.js create mode 100644 backend/backend/config/default/app/user-group-setting.js create mode 100644 backend/backend/config/default/backend/aggregations.js create mode 100644 backend/backend/config/default/backend/boostings.js create mode 100644 backend/backend/config/default/backend/index.js create mode 100644 backend/backend/config/default/backendrc.js create mode 100644 backend/backend/config/default/elasticsearchrc.js rename backend/backend/config/{prometheus.json => default/prometheusrc.json} (100%) create mode 100644 backend/backend/config/default/tools/markdown.js create mode 100644 backend/backend/config/default/tools/semester-acronym.js delete mode 100644 backend/backend/config/elasticsearch-b-tu.ts delete mode 100644 backend/backend/config/elasticsearch.ts create mode 100644 backend/backend/config/f-u/about-pages/about.de.md create mode 100644 backend/backend/config/f-u/about-pages/about.en.md create mode 100644 backend/backend/config/f-u/about-pages/about.js create mode 100644 backend/backend/config/f-u/about-pages/imprint.js create mode 100644 backend/backend/config/f-u/about-pages/index.js create mode 100644 backend/backend/config/f-u/about-pages/privacy.de.md create mode 100644 backend/backend/config/f-u/about-pages/privacy.en.md create mode 100644 backend/backend/config/f-u/about-pages/privacy.js create mode 100644 backend/backend/config/f-u/backendrc.js create mode 100644 backend/backend/src/config.ts create mode 100644 backend/backend/src/environment.d.ts create mode 100644 configuration/backend-config/about-page.xsd create mode 100644 configuration/backend-config/menu.xsd create mode 100644 configuration/backend-config/package.json create mode 100644 configuration/backend-config/test/about-page.xml create mode 100644 configuration/backend-config/test/menu.de.xml create mode 100644 configuration/backend-config/test/menu.xml create mode 100644 configuration/backend-config/tsconfig.json diff --git a/backend/backend/config/b-tu/elasticsearchrc.js b/backend/backend/config/b-tu/elasticsearchrc.js new file mode 100644 index 00000000..2bcd7dc1 --- /dev/null +++ b/backend/backend/config/b-tu/elasticsearchrc.js @@ -0,0 +1,20 @@ +// @ts-check + +/** + * This is the database configuration for the technical university of berlin + * + * @type {import('@openstapps/logger').RecursivePartial} + */ +const config = { + internal: { + database: { + name: 'elasticsearch', + query: { + minMatch: '60%', + queryType: 'query_string', + }, + }, + }, +}; + +export default config; diff --git a/backend/backend/config/default-b-tu.ts b/backend/backend/config/default-b-tu.ts deleted file mode 100644 index be9e32b8..00000000 --- a/backend/backend/config/default-b-tu.ts +++ /dev/null @@ -1,11 +0,0 @@ -// tslint:disable:no-default-export -// tslint:disable:no-magic-numbers -import {SCConfigFile} from '@openstapps/core'; -import {RecursivePartial} from '@openstapps/logger/lib/common'; - -/** - * This is the default configuration for the technical university of Berlin - */ -const config: RecursivePartial = {}; - -export default config; diff --git a/backend/backend/config/default-f-u.ts b/backend/backend/config/default-f-u.ts deleted file mode 100644 index 0e8ae9e5..00000000 --- a/backend/backend/config/default-f-u.ts +++ /dev/null @@ -1,610 +0,0 @@ -// tslint:disable:no-default-export -// tslint:disable:no-magic-numbers -import {SCAboutPageContentType, SCConfigFile, SCLanguageCode} from '@openstapps/core'; -import {RecursivePartial} from '@openstapps/logger/lib/common'; - -const markdownSources: { - [key in SCLanguageCode]?: { - /** - * Privacy policy markdown - */ - privacyPolicy?: string; - /** - * Terms and conditions markdown - */ - termsAndConditions?: string; - }; -} = {en: {}, de: {}}; - -markdownSources.de!.privacyPolicy = ` -# Datenschutzerklärung - -## Kontaktdaten des Verantwortlichen - -Verantwortlich im Sinne der Datenschutz-Grundverordnung und weiterer Vorschriften zum Datenschutz ist die: - -Johann Wolfgang Goethe-Universität Frankfurt am Main vertreten durch ihren Präsidenten
-Theodor-W.-Adorno-Platz 1
-60323 Frankfurt am Main - -Postanschrift:
-Goethe-Universität Frankfurt am Main
-60629 Frankfurt - -Website: http://www.uni-frankfurt.de - -## Kontaktdaten der Datenschutzbeauftragten an der Goethe-Universität - -Sie erreichen die behördlichen Datenschutzbeauftragten der Johann Wolfgang Goethe-Universität Frankfurt am Main unter:
-Mail:
-Website: http://www.uni-frankfurt.de/47859992/datenschutzbeauftragte - -## Informationen zur Verarbeitung personenbezogener Daten - -### 1. Umfang der Verarbeitung personenbezogener Daten - -Personenbezogene Daten sind gemäß Artikel 4 DSGVO alle Informationen, die sich auf eine identifizierte oder identifizierbare natürliche Person beziehen. - -Wir verarbeiten personenbezogene Daten von Ihnen als Nutzer:innen der Goethe-Uni-App, soweit dies zur Bereitstellung einer **funktionsfähigen Applikation** technisch erforderlich ist. - -Weiterhin kann eine Datenverarbeitung auf Ihrer freiwilligen Einwilligung basieren, wenn Sie **spezifische Funktionen** nutzen möchten. - -Wir unterscheiden daher nachfolgend zwischen - -- Zugriffsdaten bei der Nutzung der App: Inhalt der Anfragen, IP-Adressen, Datum/Uhrzeit der Anfrage, Angefragte URL, Fehlermeldungen, Browser-Kennung, HTTP-Header - -- Standortbestimmung und Navigation: freiwillige Standortangaben - -- Nutzer:inneneinstellungen: freiwillige Angabe von a) Sprachpräferenzen (derzeit: deutsch/englisch), b) Status (z. B. Gast/Student) oder c) spezifischen Suchanfragen und Suchergebnissen (Notifications) - -- Kalenderfunktion: freiwillige Nutzung der Kalenderfunktion (optional mit freiwilliger Nutzung einer Synchronisationsfunktion: Opt-in) oder der integrierten Stundenplanfunktion, hierbei werden folgende Daten auf dem Endgerät verarbeitet und gespeichert: Termine und Veranstaltungen - -- Feedbackfunktion und Kontaktaufnahme: freiwillige Nutzung mit der Angabe von Kontaktdaten und ggf. freiwilliger Übermittlung von Protokolldaten - -- Campus Dienste: freiwillige Nutzung mit Verarbeitung von Notenansicht, Matrikelnummer, E-Mailadresse, Name - -- Funktionen der Bibliothek: freiwillige Nutzung mit Verarbeitung von Bibliothekskontodaten, wie z.B. Ausweisnummer mit Name, E-Mailadresse, postalischer Adresse, Nutzungsberechtigung, Bestelldaten, Gebühren, Vormerkung, Ausleihdaten. Die vollständigen Angaben zur Verarbeitung finden Sie in der Datenschutzerklärung der Bibliothek:
- https://www.ub.uni-frankfurt.de/benutzung/datenschutz.html - -Die App verlinkt an einigen Stellen auf die Website der Goethe-Universität sowie auf andere, externe Websites, die in einem In-App-Browser dargestellt werden. Wir bitten Sie bei Aufruf dieser Websites, die dort geltenden gesonderte Datenschutzhinweise und Erklärungen zu beachten. - -### 2. Zweck(e) der Datenverarbeitung - -**Zugriff auf Standortdaten** - -Für die Navigation benötigt die Goethe-Uni-App Zugriff auf den Standort des verwendeten Endgerätes (Location Based Services). Bei einer Anfrage erhebt die App den aktuellen Standort über GPS, Funkzellendaten und WLAN-Datenbanken, um Ihnen als Nutzer:in Informationen zu Ihrer unmittelbaren Umgebung geben zu können. Der Zugriff auf die Standortdaten erfolgt nur, wenn Sie den Zugriff auf die Standortdaten erlauben. Daten zu Ihrem Standort werden ausschließlich für die Bearbeitung von standortbezogenen Anfragen genutzt und um Ihren Standort auf der Karte anzuzeigen. - -**Zugriff auf Zugriffsdaten** - -Die Speicherung und Verarbeitung von Protokolldateien erfolgt, um die Funktionsfähigkeit der Goethe Uni-App für Sie sicherzustellen. Zudem benötigen wir die die Daten aus Gründen der Sicherheit unserer informationstechnischen Systeme. Eine anderweitige Auswertung oder Weitergabe findet in diesem Zusammenhang nicht statt. - -**Zugriff auf Spracheinstellungen** - -Der Zugriff auf die Spracheinstellung erfolgt um Ihnen die Oberfläche der App in der von Ihnen gewünschten Sprache anzuzeigen. - -**Zugriff auf die Einstellung der Statusgruppe** - -Der Zugriff auf die Einstellung der Statusgruppe erfolgt um Ihnen in der App die für Ihre Gruppe zutreffenden Informationen anzuzeigen, z.B. Mensapreise - -**Zugriff auf personenbezogene Daten bei der Nutzung der Feedbackfunktion** - -Die Verarbeitung der personenbezogenen Daten aus der Feedbackfunktion dient uns zur Kontaktaufnahme und Fehlerbehebung. - -**Zugriff auf personenbezogene Daten bei der Kalendersynchronisation** - -Der Zugriff auf die Termindaten erfolgt um sie bei aktivierter Kalenderfunktion in den Gerätekalender zu schreiben. - -**Zugriff auf Daten der Campus Dienste** - -Der Zugriff auf das Campus Management Systems erfolgt ausschließlich um persönliche Daten der Studierendenverwaltung in der App anzuzeigen (z.B. Prüfungsnoten). - -**Zugriff auf bibliotheksspezifische personenbezogene Daten** - -Der Zugriff auf die Daten (z.B. Ausweisnummer, Name, Postanschrift) erfolgt zur Durchführung von Bestell- und Ausleihverfahren von Büchern und sonstigen Materialien der Universitätsbibliothek. Die vollständigen Angaben zu den Verarbeitungszwecken finden Sie in der Datenschutzerklärung der Bibliothek: https://www.ub.uni-frankfurt.de/benutzung/datenschutz.html - -### 3. Rechtsgrundlage(n) für die Datenverarbeitung - -Die Nutzung der Nutzungs-/Zugriffsdaten („Protokolldateien") basiert auf Artikel 6 Absatz 1 lit. f) DSGVO. - -Für alle spezifischen Funktionen, bei denen die Datenverarbeitung auf Ihrer freiwilligen Einwilligung als Nutzer:innen basiert, werden explizit Einwilligungen bzw. aktive Zustimmungsakte („Opt-In") eingeholt. Die Bereitstellung personenbezogener Daten zu Ihrer Person gegenüber der Goethe-Universität erfolgen dabei auf freiwilliger Basis. Die Rechtsgrundlage ist in diesen Fällen jeweils Artikel 6 Absatz 1 lit. a) DSGVO. Sie können Ihre jeweilige Einwilligung jederzeit einzeln widerrufen bzw. Ihre Einstellungen ändern. - -### 4. Datenlöschung und Speicherdauer - -Die in den Protokolldateien der App erfassten Daten werden sieben Tage nach dem Ende des Zugriffs automatisch gelöscht oder anonymisiert. - -Die Löschfristen bzw. Speicherdauer der in den Bibliotheksystemen erfassten Daten finden Sie in der Datenschutzerklärung der Bibliothek: https://www.ub.uni-frankfurt.de/benutzung/datenschutz.html - -Für alle anderen Funktionen und Dienste gilt: Die Löschung erfolgt hier je nach Vorgabe des genutzten Dienstes. Die personenbezogenen Daten der betroffenen Person werden gelöscht oder gesperrt, sobald der Zweck der Speicherung entfällt. - -### 5. Datenweitergabe/Datenübermittlung - -Ihre personenbezogenen Daten werden von uns nicht an Dritte weitergegeben. - -Von Betreiberseite wird durch technische und organisatorische Maßnahmen sichergestellt, dass Dritte keinen Zugriff auf die verarbeiteten Daten, wie z. B. Nutzungsdaten, erhalten. Ein Auftragsverarbeitungsverhältnis nach Art. 28 DSGVO besteht nicht, da ausschließlich eigene Server verwendet werden. - -### 6. Automatisierte Entscheidungsfindung - -Eine automatisierte Entscheidungsfindung einschließlich Profiling erfolgt nicht. - -## Rechte der betroffenen Person - -Werden personenbezogene Daten von Ihnen verarbeitet, sind Sie Betroffener im Sinne der DSGVO. Die Geltendmachung Ihrer Betroffenenrechte ist kostenfrei. Sie können sich dafür selbstverständlich an uns wenden. Es stehen Ihnen folgende Betroffenenrechte gegenüber der Goethe-Universität zu: - -### 1. Auskunftsrecht - -Sie können von uns als verantwortlicher Stelle eine Bestätigung darüber verlangen, ob und welche Ihrer personenbezogenen Daten von uns verarbeitet werden. Sie haben das Recht, von uns Kopien Ihrer personenbezogenen Daten zu verlangen. Bitte beachten Sie die Ausnahmen, die sich durch spezifische Vorschriften ergeben können. - -### 2. Recht auf Berichtigung - -Sie haben das Recht von uns die Berichtigung und/oder Vervollständigung zu verlangen, sofern die verarbeiteten personenbezogenen Daten, die Sie betreffen, nicht (mehr) richtig oder nicht (mehr) vollständig sind. - -### 3. Recht auf Einschränkung der Verarbeitung - -Unter bestimmten Voraussetzungen können Sie die Einschränkung der Verarbeitung der Sie betreffenden personenbezogenen Daten verlangen, d. h. dass dann Ihre personenbezogenen Daten zwar nicht gelöscht, aber gekennzeichnet werden, so dass eine weitere Verarbeitung eingeschränkt ist. - -### 4. Recht auf Löschung - -Sie können unter bestimmten Voraussetzungen von uns verlangen, dass die Sie betreffenden personenbezogenen Daten unverzüglich gelöscht werden. Dies ist insbesondere der Fall, wenn die personenbezogenen Daten zu dem Zweck, zu dem sie ursprünglich erhoben oder verarbeitet wurden, nicht mehr erforderlich sind. - -### 5. Recht auf Unterrichtung - -Haben Sie das Recht auf Berichtigung, Löschung oder Einschränkung der Verarbeitung uns gegenüber geltend gemacht, sind wir verpflichtet, allen Empfänger/innen, denen die Sie betreffenden personenbezogenen Daten offengelegt wurden, diese Berichtigung oder Löschung der Daten oder Einschränkung der Verarbeitung mitzuteilen, es sei denn, dies erweist sich als unmöglich oder ist mit einem unverhältnismäßigen Aufwand verbunden. Sie sind berechtigt, über diese Empfänger unterrichtet zu werden. - -### 6. Recht auf Datenübertragbarkeit - -Sie haben unter bestimmten Voraussetzungen das Recht von uns zu verlangen, dass Ihre personenbezogenen Daten von uns direkt an einen anderen Verantwortlichen oder an eine andere Organisation übermittelt werden. Alternativ haben Sie unter bestimmten Voraussetzungen das Recht von uns zu verlangen, dass wir Ihnen selbst die Daten in einem maschinenlesbaren Format bereitstellen. - -### 7. Widerspruchsrecht - -Wenn wir Ihre personenbezogenen Daten verarbeiten, weil die Verarbeitung im öffentlichen Interesse, Teil unserer öffentlichen Aufgaben ist bzw. wenn wir Ihre Daten auf Basis eines berechtigten Interesses verarbeiten, haben Sie aus Gründen, die sich aus Ihrer besonderen Situation ergeben, das Recht, jederzeit der Verarbeitung der Sie betreffenden Daten zu widersprechen. - -### 8. Recht auf Widerruf der datenschutzrechtlichen Einwilligungserklärung - -Wenn wir Ihre personenbezogenen Daten verarbeiten, weil Sie uns Ihre Einwilligung gegeben haben, haben Sie jederzeit das Recht, Ihre Einwilligungserklärung zu widerrufen. - -### 9. Recht auf Beschwerde bei einer Aufsichtsbehörde - -Sie haben ferner das Recht auf Beschwerde bei einer Aufsichtsbehörde. Die zuständige Aufsichtsbehörde wird Ihre Beschwerde prüfen. - -## **Kontaktdaten der Aufsichtsbehörde im Bereich Datenschutz** - -Wenn Sie der Ansicht sind, dass eine Verarbeitung der Sie betreffenden personenbezogenen Daten gegen Datenschutzvorschriften verstößt, wenn Sie eine allgemeine Anfrage haben oder wenn Sie sich bei einer zuständigen Fachaufsichtsbehörde beschweren wollen, können Sie sich an den Hessischen Beauftragten für Datenschutz und Informationsfreiheit (HBDI) wenden. - -**Der Hessische Beauftragte für Datenschutz und Informationsfreiheit ist auf unterschiedlichen Wegen erreichbar:** - -**Der Hessische Beauftragte für Datenschutz und Informationsfreiheit**
-Postfach 3163
-65021 Wiesbaden - -Telefon: +49 611 1408 -- 0 - -Für allgemeine Anfragen können Sie ein Kontaktformular nutzen:
-
-
-Für Beschwerden steht Ihnen zudem ein Beschwerdeformular zur Verfügung:
- -`; - -markdownSources.en!.privacyPolicy = ` -# Privacy policy - -## Contact details of the person responsible - -Responsible in the sense of the General Data Protection Regulation and further regulations on data protection is the: - -Johann Wolfgang Goethe-Universität Frankfurt am Main represented by its president
-Theodor-W.-Adorno-Platz 1
-60323 Frankfurt am Main - -Postanschrift:
-Goethe-Universität Frankfurt am Main
-60629 Frankfurt - -Website: http://www.uni-frankfurt.de - -## Contact details of the data protection officer at Goethe University - -You can reach the data protection officers at Johann Wolfgang Goethe University Frankfurt am Main at:
-Mail:
-Website: http://www.uni-frankfurt.de/47859992/datenschutzbeauftragte - -## Information on the processing of personal data - -### 1. Scope of the processing of personal data. - -According to Article 4 DSGVO, personal data is any information relating to an identified or identifiable natural person. - -We process personal data of you as a user inside of the Goethe University App to the extent that this is technically necessary for the provision of a **functional application**. - -Furthermore, data processing may be based on your voluntary consent if you wish to use **specific functions**. - -We therefore distinguish below between - -- Access data when using the app: content of requests, IP addresses, date/time of request, requested URL, error codes, browser identifier, HTTP header. - -- Location and navigation: voluntary location information - -- User settings: voluntary specification of a) language preferences (currently: German/English), b) status (e.g. guest/student) or c) specific search queries and search results (notifications) - -- Calendar function: voluntary use of the calendar function (optional with voluntary use of a sync function: opt-in) or the integrated timetable function. The following data is processed and stored on the users device: appointments and events - -- Feedback function and contacting: voluntary use with the provision of contact data and, if applicable, voluntary transmission of log data - -- Campus services: voluntary use with processing of grade view, matriculation number, email address, name - -- Services of the library: voluntary use with processing of library account data, such as ID number with name, e-mail address, postal address, right of use, order data, fees, reservation, loan data. Full details of processing can be found in the library's privacy policy:
- https://www.ub.uni-frankfurt.de/benutzung/datenschutz.html - -In some places, the app links to the Goethe University website and to other external websites that are displayed in an in-app browser. When you visit these websites, we ask you to pay attention to the separate data protection notices and declarations that apply there. - -### 2. Purpose(s) of data processing - -**Access to location data**. - -For navigation, the Goethe University app requires access to the location of the end device used (location-based services). When a request is made, the app collects the current location via GPS, radio cell data and WLAN databases in order to be able to give you as a user:in information about your immediate surroundings. The location data is only accessed if you allow access to the location data. Data about your location is only used to process location-related requests and to display your location on the map. - -**Access to access data**. - -Log files are stored and processed to ensure that the Goethe Uni app functions properly for you. In addition, we need the data for reasons of security of our information technology systems. No other evaluation or disclosure takes place in this context. - -**Access to language settings**. - -Access to the language setting is made in order to display the interface of the app in the language of your choice. - -**Access to the status group setting**. - -Access to the status group setting is provided to show you the information in the app that applies to your group, e.g. canteen prices. - -**Access to personal data when using the feedback function**. - -We use the processing of personal data from the feedback function to contact you and troubleshoot problems. - -**Access to personal data when synchronizing calendars**. - -Appointment data is accessed in order to write it to the device calendar when the calendar function is enabled. - -**Access to Campus Services data**. - -Access to the Campus Management System is solely for the purpose of displaying personal student management data in the app (e.g., exam grades). - -**Access to library-specific personal data**. - -Access to data (e.g., ID number, name, mailing address) is for the purpose of carrying out ordering and borrowing procedures for books and other materials from the University Library. Full details of the purposes of processing can be found in the Library's Privacy Policy: https://www.ub.uni-frankfurt.de/benutzung/datenschutz.html - -### 3. Rechtsgrundlage(n) für die Datenverarbeitung - -The use of usage/access data ("log files") is based on Article 6(1)(f) DSGVO. - -For all specific functions where data processing is based on your voluntary consent as a user:in, explicit consent or active acts of consent ("opt-in") are obtained. The provision of personal data about you to Goethe University is done on a voluntary basis. The legal basis in each of these cases is Article 6 (1) a) DSGVO. You can individually revoke your respective consent or change your settings at any time. - -### 4. Data deletion and storage duration - -The data collected in the log files of the app are automatically deleted or anonymized seven days after the end of the access. - -The deletion periods or storage duration of the data collected in the library systems can be found in the library's privacy policy: https://www.ub.uni-frankfurt.de/benutzung/datenschutz.html - -For all other functions and services, the following applies: deletion takes place here depending on the specifications of the service used. The personal data of the data subject will be deleted or blocked as soon as the purpose of the storage no longer applies. - -### 5. Data disclosure/data transfer - -We will not pass on your personal data to third parties. - -On the part of the operator, technical and organizational measures are taken to ensure that third parties do not gain access to the processed data, such as usage data. An order processing relationship according to Art. 28 DSGVO does not exist, as only our own servers are used. - -### 6. Automated decision-making - -Automated decision-making, including profiling, does not take place. - -## Rights of the data subject - -If personal data is processed by you, you are a data subject within the meaning of the GDPR. The assertion of your data subject rights is free of charge. You can, of course, contact us for this purpose. You are entitled to the following data subject rights vis-à-vis Goethe University: - -### 1. Right of access - -You can request confirmation from us as the controller as to whether and which of your personal data is being processed by us. You have the right to request copies of your personal data from us. Please note the exceptions that may arise due to specific regulations. - -### 2. Right of rectification - -You have the right to request us to rectify and/or complete, if the processed personal data concerning you is not (anymore) accurate or not (anymore) complete. - -### 3. Right to restriction of processing - -Under certain conditions, you can request the restriction of the processing of personal data concerning you, i.e. that your personal data is then not deleted, but marked so that further processing is restricted. - -### 4. Right to erasure - -Under certain conditions, you can demand that we delete the personal data concerning you without delay. This is particularly the case if the personal data is no longer necessary for the purpose for which it was originally collected or processed. - -### 5. Right to information - -If you have asserted the right to rectification, erasure or restriction of processing against us, we are obliged to inform all recipients to whom the personal data concerning you have been disclosed of this rectification or erasure of the data or restriction of processing, unless this proves impossible or involves a disproportionate effort. You are entitled to be informed about these recipients. - -### 6. Right to data portability - -Under certain conditions, you have the right to request that we transfer your personal data directly to another controller or organization. Alternatively, under certain conditions, you have the right to request that we ourselves provide you with the data in a machine-readable format. - -### 7. Right to object - -If we process your personal data because the processing is in the public interest, part of our public duties, or if we process your data on the basis of a legitimate interest, you have the right to object at any time to the processing of data relating to you for reasons arising from your particular situation. - -### 8. Right to revoke the declaration of consent under data protection law - -If we process your personal data because you have given us your consent, you have the right to revoke your declaration of consent at any time. - -### 9. Right to lodge a complaint with a supervisory authority - -You also have the right to lodge a complaint with a supervisory authority. The competent supervisory authority will examine your complaint. - -## **Contact details of the supervisory authority in the area of data protection** - -If you believe that the processing of your personal data violates data protection regulations, if you have a general inquiry or if you want to complain to a competent supervisory authority, you can contact the Hessian Commissioner for Data Protection and Freedom of Information (HBDI). - -**The Hessian Commissioner for Data Protection and Freedom of Information can be reached in different ways:** - -**The Hessian Commissioner for Data Protection and Freedom of Information**
-PO Box 3163
-65021 Wiesbaden - -Telephone: +49 611 1408 -- 0 - -For general inquiries you can use a contact form:
-
-
-A complaint form is also available for complaints:
- -`; - -/** - * This is the default configuration for the Goethe university of Frankfurt - */ -const config: RecursivePartial = { - auth: { - default: { - client: { - clientId: '1cac3f99-33fa-4234-8438-979f07e0cdab', - scopes: '', - url: 'https://cas.rz.uni-frankfurt.de/cas/oauth2.0', - }, - endpoints: { - authorization: 'https://cas.rz.uni-frankfurt.de/cas/oauth2.0/authorize', - endSession: 'https://cas.rz.uni-frankfurt.de/cas/logout', - mapping: { - id: '$.id', - email: '$.attributes.mailPrimaryAddress', - familyName: '$.attributes.sn', - givenName: '$.attributes.givenName', - name: '$.attributes.givenName', - role: '$.attributes.eduPersonPrimaryAffiliation', - studentId: '$.attributes.employeeNumber', - }, - token: 'https://cas.rz.uni-frankfurt.de/cas/oauth2.0/accessToken', - userinfo: 'https://cas.rz.uni-frankfurt.de/cas/oauth2.0/profile', - }, - }, - paia: { - client: { - clientId: '', - scopes: '', - url: 'https://hds.hebis.de/Shibboleth.sso/UBFFM?target=https://hds.hebis.de/ubffm/paia_login_stub.php', - }, - endpoints: { - authorization: - 'https://hds.hebis.de/Shibboleth.sso/UBFFM?target=https://hds.hebis.de/ubffm/paia_login_stub.php', - endSession: 'https://ubffm.hds.hebis.de/Shibboleth.sso/Logout', - mapping: { - id: '$.email', - name: '$.name', - role: '$.type', - }, - token: 'https://hds.hebis.de/paia/auth/login', - userinfo: 'https://hds.hebis.de/paia/core', - }, - }, - }, - app: { - features: { - extern: { - hisometry: { - authProvider: 'default', - url: 'https://his-self-service.rz.uni-frankfurt.de', - }, - daia: { - url: 'https://daia.hebis.de/DAIA2/UB_Frankfurt', - }, - hebisProxy: { - url: 'https://proxy.ub.uni-frankfurt.de/login?qurl=', - }, - paia: { - authProvider: 'paia', - url: 'https://hds.hebis.de/paia/core', - }, - }, - }, - aboutPages: { - 'about': { - title: 'Über Open StApps', - content: [ - { - title: 'Verbundprojekt mehrerer Hochschulen für eine generische Studierenden-App', - content: { - type: SCAboutPageContentType.MARKDOWN, - value: ` - Open StApps bietet Studierenden aller beteiligten Hochschulen eine qualitativ - hochwertige App für den Studienalltag. Open StApps-Verbundpartner integrieren - generalisierbare Studierendenprozesse so in App-Module, dass diese auch - von anderen Hochschulen verwendet werden können. Die in der Open StApps App - verwendeten Daten einer Datenquelle sind in einem generalisierten Datenmodell - so aufbereitet, dass ein Austausch oder Abschaltung der Datenquelle problemlos möglich - ist und die Open StApps App problemlos weiterhin funktionsfähig bleibt. - `, - translations: { - en: { - value: `Open StApps provides students from all participating universities with a - high-quality app for everyday study. Open StApps partners integrate - generalizable student processes into app modules in such a way that they can be - can be used by other universities. The data of a data source used in the Open StApps app - is prepared in a generalized data model in a way that the data source can be easily - exchanged or switched off while the app continues to function without any problems. - `, - }, - }, - }, - translations: { - en: { - title: 'Collaborative project of multiple universities for a single generic study app', - }, - }, - type: SCAboutPageContentType.SECTION, - }, - { - title: 'Goethe-Uni Kontakt', - content: { - rows: [ - [ - { - value: 'Adresse', - translations: { - en: { - value: 'Address', - }, - }, - type: SCAboutPageContentType.MARKDOWN, - }, - { - value: ` - Goethe Universität
- Hochschulrechenzentrum (HRZ)
- Norbert-Wollheim-Platz 1
- 60629 Frankfurt - `, - translations: {}, - type: SCAboutPageContentType.MARKDOWN, - }, - ], - [ - { - value: 'Kontaktinformation', - translations: { - en: { - value: 'Contact information', - }, - }, - type: SCAboutPageContentType.MARKDOWN, - }, - { - value: - '[app@rz.uni-frankfurt.de](mailto:app@rz.uni-frankfurt.de)
' + - '[+49 69 798 32936](tel:+496979832936)
' + - '[https://app.rz.uni-frankfurt.de](https://app.rz.uni-frankfurt.de)', - translations: {}, - type: SCAboutPageContentType.MARKDOWN, - }, - ], - ], - type: SCAboutPageContentType.TABLE, - }, - translations: { - en: { - title: 'Goethe-Uni Contact', - }, - }, - type: SCAboutPageContentType.SECTION, - }, - { - icon: 'newspaper', - title: 'Neue Funktionen / Gelöste Probleme', - link: 'changelog', - translations: { - en: { - title: 'New features / Resolved issues', - }, - }, - type: SCAboutPageContentType.ROUTER_LINK, - }, - { - icon: 'description', - title: 'Impressum', - link: 'imprint', - translations: { - en: { - title: 'Imprint', - }, - }, - type: SCAboutPageContentType.ROUTER_LINK, - }, - { - icon: 'policy', - title: 'Datenschutz', - link: 'privacy', - translations: { - en: { - title: 'Privacy policy', - }, - }, - type: SCAboutPageContentType.ROUTER_LINK, - }, - { - icon: 'copyright', - title: 'Bibliotheken und Lizenzen', - link: 'licenses', - translations: { - en: { - title: 'Libraries and licenses', - }, - }, - type: SCAboutPageContentType.ROUTER_LINK, - }, - ], - translations: { - en: { - title: 'About Open StApps', - }, - }, - }, - 'about/imprint': { - title: 'Impressum', - content: [ - { - value: ` - [Impressum der Johann Wolfgang Goethe-Universität Frankfurt am Main](https://www.uni-frankfurt.de/impressum) - `, - translations: { - en: { - value: ` - [Imprint of the Goethe University Frankfurt](https://www.uni-frankfurt.de/impressum) - `, - }, - }, - type: SCAboutPageContentType.MARKDOWN, - }, - ], - translations: { - en: { - title: 'Imprint', - }, - }, - }, - 'about/privacy': { - title: 'Datenschutz', - content: [ - { - value: markdownSources.de!.privacyPolicy, - translations: { - en: { - value: markdownSources.en!.privacyPolicy, - }, - }, - type: SCAboutPageContentType.MARKDOWN, - }, - ], - translations: { - en: { - title: 'Privacy Policy', - }, - }, - }, - }, - }, -}; - -export default config; diff --git a/backend/backend/config/default-fb-fh.ts b/backend/backend/config/default-fb-fh.ts deleted file mode 100644 index 5de12b81..00000000 --- a/backend/backend/config/default-fb-fh.ts +++ /dev/null @@ -1,11 +0,0 @@ -// tslint:disable:no-default-export -// tslint:disable:no-magic-numbers -import {SCConfigFile} from '@openstapps/core'; -import {RecursivePartial} from '@openstapps/logger/lib/common'; - -/** - * This is the default configuration for the university of Kassel - */ -const config: RecursivePartial = {}; - -export default config; diff --git a/backend/backend/config/default-ks-ug.ts b/backend/backend/config/default-ks-ug.ts deleted file mode 100644 index 5de12b81..00000000 --- a/backend/backend/config/default-ks-ug.ts +++ /dev/null @@ -1,11 +0,0 @@ -// tslint:disable:no-default-export -// tslint:disable:no-magic-numbers -import {SCConfigFile} from '@openstapps/core'; -import {RecursivePartial} from '@openstapps/logger/lib/common'; - -/** - * This is the default configuration for the university of Kassel - */ -const config: RecursivePartial = {}; - -export default config; diff --git a/backend/backend/config/default.ts b/backend/backend/config/default.ts deleted file mode 100644 index 3b2a8243..00000000 --- a/backend/backend/config/default.ts +++ /dev/null @@ -1,741 +0,0 @@ -import { - SCAboutPageContentType, - SCConfigFile, - SCLanguageSetting, - SCSettingInputType, - SCThingOriginType, - SCThingType, - SCUserGroupSetting, -} from '@openstapps/core'; -import {readFileSync} from 'fs'; -import path from 'path'; - -/** - * Generates a range of numbers that represent consecutive calendric months - * - * @param startMonth The month to start with (inclusive) - * @param endMonth The month to end with (inclusive) - */ -export function yearSlice(startMonth: number, endMonth: number) { - let months = [...Array.from({length: 13}).keys()].slice(1); - months = [...months, ...months]; - if (!months.includes(startMonth) || !months.includes(endMonth)) { - throw new Error(`Given months not part of a year! Check ${startMonth} or ${endMonth}!`); - } - - const startIndex = months.indexOf(startMonth); - const endIndex = - months.indexOf(endMonth) <= startIndex ? months.lastIndexOf(endMonth) : months.indexOf(endMonth); - - return months.slice(startIndex, endIndex + 1); -} - -const sommerRange = yearSlice(3, 8); -const winterRange = yearSlice(9, 2); -const month = new Date().getMonth(); -const year = new Date().getFullYear(); -const winterYearOffset = month < winterRange[0] ? -1 : 0; -const sommerYear = year + (month <= winterRange[winterRange.length] ? -1 : 0); -const winterYear = `${year + winterYearOffset}/${(year + 1 + winterYearOffset).toString().slice(-2)}`; - -const wsAcronymShort = `WS ${winterYear}`; -const ssAcronymShort = `SS ${sommerYear}`; -const wsAcronymLong = `WiSe ${winterYear}`; -const ssAcronymLong = `SoSe ${sommerYear}`; - -const userGroupSetting: SCUserGroupSetting = { - categories: ['profile'], - defaultValue: 'students', - description: - 'The user group the app is going to be used.' + - 'This settings for example is getting used for the predefined price category of mensa meals.', - inputType: SCSettingInputType.SingleChoice, - name: 'group', - order: 1, - origin: { - indexed: '2018-09-11T12:30:00Z', - name: 'SCConfigFile Default Values', - type: SCThingOriginType.Remote, - }, - translations: { - de: { - description: - 'Mit welcher Benutzergruppe soll die App verwendet werden?' + - ' Die Einstellung wird beispielsweise für die Vorauswahl der Preiskategorie der Mensa verwendet.', - name: 'Gruppe', - values: ['Studierende', 'Angestellte', 'Gäste'], - }, - en: { - description: - 'The user group the app is going to be used.' + - ' This settings for example is getting used for the predefined price category of mensa meals.', - name: 'Group', - values: ['students', 'employees', 'guests'], - }, - }, - type: SCThingType.Setting, - uid: '2c97aa36-4aa2-43de-bc5d-a2b2cb3a530e', - values: ['students', 'employees', 'guests'], -}; - -const languageSetting: SCLanguageSetting = { - categories: ['profile'], - defaultValue: 'en', - description: 'The language this app is going to use.', - inputType: SCSettingInputType.SingleChoice, - name: 'language', - order: 0, - origin: { - indexed: '2018-09-11T12:30:00Z', - name: 'SCConfigFile Default Values', - type: SCThingOriginType.Remote, - }, - translations: { - de: { - description: 'Die Sprache in der die App angezeigt wird.', - name: 'Sprache', - values: ['Deutsch', 'English'], - }, - en: { - description: 'The language this app is going to use.', - name: 'Language', - values: ['Deutsch', 'English'], - }, - }, - type: SCThingType.Setting, - uid: 'dc9d6dec-6576-45ef-9e35-3598c0d6a662', - values: ['de', 'en'], -}; - -/** - * This is the default configuration for app and backend - * - * University specific files can be created with following naming scheme: default-.ts - * - * To select your university specific configuration which is merged from this default file and your university specific - * file, you have to supply the `NODE_APP_INSTANCE` environment variable with your license plate - * - * To get more information about the meaning of specific fields please have a look at `@openstapps/core` or use your - * IDE to read the TSDoc documentation. - */ - -const config: SCConfigFile = { - app: { - aboutPages: { - 'about': { - title: 'Über das Open StApps Projekt', - content: [ - { - title: 'Verbundprojekt mehrerer Hochschulen für eine generische Studierenden-App', - content: { - type: SCAboutPageContentType.MARKDOWN, - value: ` - Open StApps bietet Studierenden aller beteiligten Hochschulen eine qualitativ - hochwertige App für den Studienalltag. Open StApps-Verbundpartner integrieren - generalisierbare Studierendenprozesse so in App-Module, dass diese auch - von anderen Hochschulen verwendet werden können. Die in der Open StApps App - verwendeten Daten einer Datenquelle sind in einem generalisierten Datenmodell - so aufbereitet, dass ein Austausch oder Abschaltung der Datenquelle problemlos möglich - ist und die Open StApps App problemlos weiterhin funktionsfähig bleibt. - `, - translations: { - en: { - value: ` - Open StApps provides students from all participating universities with a - high-quality app for everyday study. Open StApps partners integrate - generalizable student processes into app modules in such a way that they can be - can be used by other universities. The data of a data source used in the Open StApps app - is prepared in a generalized data model in a way that the data source can be easily - exchanged or switched off while the app continues to function without any problems. - `, - }, - }, - }, - translations: { - en: { - title: 'Collaborative project of multiple universities for a single generic study app', - }, - }, - type: SCAboutPageContentType.SECTION, - }, - { - title: 'Universitätskontakt', - content: { - rows: [ - [ - { - value: 'Adresse', - translations: { - en: { - value: 'address', - }, - }, - type: SCAboutPageContentType.MARKDOWN, - }, - { - value: ` - Nirmasi Universität
- Abteilung für digitale Angelegenheiten (AbtDigi)
- Null Island 1
- 999999 Atlantic Ocean - `, - translations: { - en: { - value: 'This would be the english address', - }, - }, - type: SCAboutPageContentType.MARKDOWN, - }, - ], - [ - { - value: 'Kontaktinformationen', - translations: { - en: { - value: 'Contact information', - }, - }, - type: SCAboutPageContentType.MARKDOWN, - }, - { - value: '[mail]()
' + '[+49 12 345 67890]()
' + '[https://localhost/]()', - translations: { - en: { - value: 'This would be the english contact information', - }, - }, - type: SCAboutPageContentType.MARKDOWN, - }, - ], - ], - type: SCAboutPageContentType.TABLE, - }, - translations: { - en: { - title: 'Nimrasi-Uni Kontakt', - }, - }, - type: SCAboutPageContentType.SECTION, - }, - { - icon: 'campaign', - title: 'Neue Funktionen / Gelöste Probleme', - link: 'changelog', - translations: { - en: { - title: 'Changelog / Resolved issues', - }, - }, - type: SCAboutPageContentType.ROUTER_LINK, - }, - { - icon: 'description', - title: 'Impressum', - link: 'imprint', - translations: { - en: { - title: 'Imprint', - }, - }, - type: SCAboutPageContentType.ROUTER_LINK, - }, - { - icon: 'policy', - title: 'Datenschutz', - link: 'privacy', - translations: { - en: { - title: 'Privacy policy', - }, - }, - type: SCAboutPageContentType.ROUTER_LINK, - }, - { - icon: 'copyright', - title: 'Bibliotheken und Lizenzen', - link: 'licenses', - translations: { - en: { - title: 'Libraries and licenses', - }, - }, - type: SCAboutPageContentType.ROUTER_LINK, - }, - ], - translations: { - en: { - title: 'About', - }, - }, - }, - 'about/imprint': { - title: 'Impressum', - content: [ - { - title: 'Beteiligte Universitäten', - card: true, - content: { - value: ` - [Johann Wolfgang Goethe-Universität Frankfurt am Main](https://uni-frankfurt.de)
- [Philipps-Universität Marburg](https://www.uni-marburg.de)
- [Technische Hochschule Mittelhessen](https://www.thm.de)
- [Universität Kassel](https://www.uni-kassel.de)
- weitere Hochschulen und Mitarbeitende - `, - translations: { - en: { - value: ` - [Goethe University Frankfurt](https://uni-frankfurt.de)
- [University of Marburg](https://www.uni-marburg.de)
- [University of Applied Sciences Mittelhessen](https://www.thm.de)
- [University of Kassel](https://www.uni-kassel.de)
- further universities and developers - `, - }, - }, - type: SCAboutPageContentType.MARKDOWN, - }, - translations: { - en: { - title: 'Collaborating Universities', - }, - }, - type: SCAboutPageContentType.SECTION, - }, - ], - translations: { - en: { - title: 'Imprint', - }, - }, - }, - 'about/privacy': { - title: 'Datenschutz', - content: [ - { - value: 'Hier wäre der Datenschutz', - translations: { - en: { - value: 'This would be the privacy policy', - }, - }, - type: SCAboutPageContentType.MARKDOWN, - }, - ], - translations: { - en: { - title: 'Privacy Policy', - }, - }, - }, - }, - campusPolygon: { - coordinates: [ - [ - [8.660_432_999_690_723, 50.123_027_017_044_436], - [8.675_496_285_518_358, 50.123_027_017_044_436], - [8.675_496_285_518_358, 50.130_661_764_486_42], - [8.660_432_999_690_723, 50.130_661_764_486_42], - [8.660_432_999_690_723, 50.123_027_017_044_436], - ], - ], - type: 'Polygon', - }, - features: {}, - menus: [ - { - icon: 'home', - items: [ - { - icon: 'newspaper', - route: '/news', - title: 'news', - translations: { - de: { - title: 'Aktuelles', - }, - en: { - title: 'news', - }, - }, - }, - { - icon: 'search', - route: '/search', - title: 'search', - translations: { - de: { - title: 'Suche', - }, - en: { - title: 'search', - }, - }, - }, - { - icon: 'local_library', - route: '/hebis-search', - title: 'library catalog', - translations: { - de: { - title: 'Bibliothekskatalog', - }, - en: { - title: 'library catalog', - }, - }, - }, - { - icon: 'inventory_2', - route: '/catalog', - title: 'course catalog', - translations: { - de: { - title: 'Vorlesungsverzeichnis', - }, - en: { - title: 'course catalog', - }, - }, - }, - ], - title: 'overview', - route: '/overview', - translations: { - de: { - title: 'Übersicht', - }, - en: { - title: 'overview', - }, - }, - }, - { - icon: 'local_cafe', - items: [], - route: '/canteen', - title: 'canteen', - translations: { - de: { - title: 'Mensa', - }, - en: { - title: 'canteen', - }, - }, - }, - { - icon: 'map', - items: [], - route: '/map', - title: 'campus map', - translations: { - de: { - title: 'Campus Karte', - }, - en: { - title: 'campus map', - }, - }, - }, - { - icon: 'school', - items: [ - { - icon: 'grade', - route: '/favorites', - title: 'favorites', - translations: { - de: { - title: 'Favoriten', - }, - en: { - title: 'favorites', - }, - }, - }, - { - icon: 'calendar_today', - route: '/schedule', - title: 'schedule', - translations: { - de: { - title: 'Stundenplan', - }, - en: { - title: 'schedule', - }, - }, - }, - { - authProvider: 'paia', - icon: 'badge', - route: '/library-account', - title: 'library account', - translations: { - de: { - title: 'Bibliothekskonto', - }, - en: { - title: 'library account', - }, - }, - }, - { - icon: 'settings', - route: '/settings', - title: 'settings', - translations: { - de: { - title: 'Einstellungen', - }, - en: { - title: 'settings', - }, - }, - }, - { - icon: 'rate_review', - route: '/feedback', - title: 'feedback', - translations: { - de: { - title: 'Feedback', - }, - en: { - title: 'feedback', - }, - }, - }, - { - icon: 'info', - route: '/about', - title: 'about', - translations: { - de: { - title: 'Über die App', - }, - en: { - title: 'About the App', - }, - }, - }, - ], - title: 'my app', - route: '/profile', - translations: { - de: { - title: 'Meine App', - }, - en: { - title: 'my app', - }, - }, - }, - ], - name: 'Goethe-Uni', - privacyPolicyUrl: 'https://mobile.server.uni-frankfurt.de/_static/privacy.md', - settings: [userGroupSetting, languageSetting], - }, - auth: {}, - backend: { - SCVersion: JSON.parse(readFileSync(path.resolve('.', '.', 'package.json'), 'utf8').toString()) - .dependencies['@openstapps/core'], - externalRequestTimeout: 5000, - hiddenTypes: [SCThingType.DateSeries, SCThingType.Diff, SCThingType.Floor], - mappingIgnoredTags: ['minlength', 'pattern', 'see', 'tjs-format'], - maxMultiSearchRouteQueries: 5, - maxRequestBodySize: 512 * 1024, - name: 'Goethe-Universität Frankfurt am Main', - namespace: '909a8cbc-8520-456c-b474-ef1525f14209', - sortableFields: [ - { - fieldName: 'name', - sortTypes: ['ducet'], - }, - { - fieldName: 'type', - sortTypes: ['ducet'], - }, - { - fieldName: 'categories', - onlyOnTypes: [ - SCThingType.AcademicEvent, - SCThingType.Building, - SCThingType.Catalog, - SCThingType.Dish, - SCThingType.PointOfInterest, - SCThingType.Room, - ], - sortTypes: ['ducet'], - }, - { - fieldName: 'geo', - onlyOnTypes: [SCThingType.Building, SCThingType.PointOfInterest, SCThingType.Room], - sortTypes: ['distance'], - }, - { - fieldName: 'geo', - onlyOnTypes: [SCThingType.Building, SCThingType.PointOfInterest, SCThingType.Room], - sortTypes: ['distance'], - }, - { - fieldName: 'inPlace.geo', - onlyOnTypes: [ - SCThingType.DateSeries, - SCThingType.Dish, - SCThingType.Floor, - SCThingType.Organization, - SCThingType.PointOfInterest, - SCThingType.Room, - SCThingType.Ticket, - ], - sortTypes: ['distance'], - }, - { - fieldName: 'offers', - onlyOnTypes: [SCThingType.Dish], - sortTypes: ['price'], - }, - ], - }, - internal: { - aggregations: [ - { - fieldName: 'categories', - onlyOnTypes: [ - SCThingType.AcademicEvent, - SCThingType.Article, - SCThingType.Building, - SCThingType.Catalog, - SCThingType.Dish, - SCThingType.PointOfInterest, - SCThingType.Room, - ], - }, - { - fieldName: 'inPlace.name', - onlyOnTypes: [ - SCThingType.DateSeries, - SCThingType.Dish, - SCThingType.Floor, - SCThingType.Organization, - SCThingType.PointOfInterest, - SCThingType.Room, - SCThingType.Ticket, - ], - }, - { - fieldName: 'academicTerms.acronym', - onlyOnTypes: [SCThingType.AcademicEvent, SCThingType.SportCourse], - }, - { - fieldName: 'academicTerm.acronym', - onlyOnTypes: [SCThingType.Catalog], - }, - { - fieldName: 'majors', - onlyOnTypes: [SCThingType.AcademicEvent], - }, - { - fieldName: 'keywords', - onlyOnTypes: [SCThingType.Article, SCThingType.Book, SCThingType.Message, SCThingType.Video], - }, - { - fieldName: 'type', - }, - ], - boostings: { - default: [ - { - factor: 1, - fields: { - 'academicTerms.acronym': { - [ssAcronymShort]: sommerRange.includes(month) ? 1.1 : 1.05, - [wsAcronymShort]: winterRange.includes(month) ? 1.1 : 1.05, - [ssAcronymLong]: sommerRange.includes(month) ? 1.1 : 1.05, - [wsAcronymLong]: winterRange.includes(month) ? 1.1 : 1.05, - }, - }, - type: SCThingType.AcademicEvent, - }, - { - factor: 1, - fields: { - categories: { - 'course': 1.08, - 'integrated course': 1.08, - 'introductory class': 1.05, - 'lecture': 1.1, - 'seminar': 1.01, - 'tutorial': 1.05, - }, - }, - type: SCThingType.AcademicEvent, - }, - { - factor: 1.6, - type: SCThingType.Building, - }, - { - factor: 1, - fields: { - categories: { - cafe: 1.1, - learn: 1.1, - library: 1.2, - restaurant: 1.1, - }, - }, - type: SCThingType.PointOfInterest, - }, - { - factor: 1, - fields: { - categories: { - 'main dish': 2, - }, - }, - type: SCThingType.Dish, - }, - ], - dining: [ - { - factor: 1, - fields: { - categories: { - 'cafe': 2, - 'canteen': 2, - 'restaurant': 2, - 'restroom': 1.2, - 'student canteen': 2, - }, - }, - type: SCThingType.Building, - }, - { - factor: 2, - type: SCThingType.Dish, - }, - ], - place: [ - { - factor: 2, - type: SCThingType.Building, - }, - { - factor: 2, - type: SCThingType.PointOfInterest, - }, - { - factor: 2, - type: SCThingType.Room, - }, - ], - }, - }, - uid: 'f-u', -}; - -// tslint:disable-next-line:no-default-export -export default config; diff --git a/backend/backend/config/default/app/index.js b/backend/backend/config/default/app/index.js new file mode 100644 index 00000000..4f8b3525 --- /dev/null +++ b/backend/backend/config/default/app/index.js @@ -0,0 +1,27 @@ +import userGroupSetting from './user-group-setting.js'; +import languageSetting from './language-setting.js'; +import menus from './menu.js'; + +/** @type {import('@openstapps/core').SCAppConfiguration} */ +const app = { + aboutPages: {}, + campusPolygon: { + coordinates: [ + [ + [8.660_432_999_690_723, 50.123_027_017_044_436], + [8.675_496_285_518_358, 50.123_027_017_044_436], + [8.675_496_285_518_358, 50.130_661_764_486_42], + [8.660_432_999_690_723, 50.130_661_764_486_42], + [8.660_432_999_690_723, 50.123_027_017_044_436], + ], + ], + type: 'Polygon', + }, + features: {}, + menus, + name: 'Goethe-Uni', + privacyPolicyUrl: 'https://mobile.server.uni-frankfurt.de/_static/privacy.md', + settings: [userGroupSetting, languageSetting], +}; + +export default app; diff --git a/backend/backend/config/default/app/language-setting.js b/backend/backend/config/default/app/language-setting.js new file mode 100644 index 00000000..7d82ea39 --- /dev/null +++ b/backend/backend/config/default/app/language-setting.js @@ -0,0 +1,34 @@ +// @ts-check +import {SCSettingInputType, SCThingOriginType, SCThingType} from '@openstapps/core'; + +/** @type {import('@openstapps/core').SCLanguageSetting} */ +const languageSetting = { + categories: ['profile'], + defaultValue: 'en', + description: 'The language this app is going to use.', + inputType: SCSettingInputType.SingleChoice, + name: 'language', + order: 0, + origin: { + indexed: '2018-09-11T12:30:00Z', + name: 'SCConfigFile Default Values', + type: SCThingOriginType.Remote, + }, + translations: { + de: { + description: 'Die Sprache in der die App angezeigt wird.', + name: 'Sprache', + values: ['Deutsch', 'English'], + }, + en: { + description: 'The language this app is going to use.', + name: 'Language', + values: ['Deutsch', 'English'], + }, + }, + type: SCThingType.Setting, + uid: 'dc9d6dec-6576-45ef-9e35-3598c0d6a662', + values: ['de', 'en'], +}; + +export default languageSetting; diff --git a/backend/backend/config/default/app/menu.js b/backend/backend/config/default/app/menu.js new file mode 100644 index 00000000..10bf654e --- /dev/null +++ b/backend/backend/config/default/app/menu.js @@ -0,0 +1,195 @@ +// @ts-check +/** @type {import('@openstapps/core').SCAppConfigurationMenuCategory[]} */ +const menus = [ + { + icon: 'home', + items: [ + { + icon: 'newspaper', + route: '/news', + title: 'news', + translations: { + de: { + title: 'Aktuelles', + }, + en: { + title: 'news', + }, + }, + }, + { + icon: 'search', + route: '/search', + title: 'search', + translations: { + de: { + title: 'Suche', + }, + en: { + title: 'search', + }, + }, + }, + { + icon: 'local_library', + route: '/hebis-search', + title: 'library catalog', + translations: { + de: { + title: 'Bibliothekskatalog', + }, + en: { + title: 'library catalog', + }, + }, + }, + { + icon: 'inventory_2', + route: '/catalog', + title: 'course catalog', + translations: { + de: { + title: 'Vorlesungsverzeichnis', + }, + en: { + title: 'course catalog', + }, + }, + }, + ], + title: 'overview', + route: '/overview', + translations: { + de: { + title: 'Übersicht', + }, + en: { + title: 'overview', + }, + }, + }, + { + icon: 'local_cafe', + items: [], + route: '/canteen', + title: 'canteen', + translations: { + de: { + title: 'Mensa', + }, + en: { + title: 'canteen', + }, + }, + }, + { + icon: 'map', + items: [], + route: '/map', + title: 'campus map', + translations: { + de: { + title: 'Campus Karte', + }, + en: { + title: 'campus map', + }, + }, + }, + { + icon: 'school', + items: [ + { + icon: 'grade', + route: '/favorites', + title: 'favorites', + translations: { + de: { + title: 'Favoriten', + }, + en: { + title: 'favorites', + }, + }, + }, + { + icon: 'calendar_today', + route: '/schedule', + title: 'schedule', + translations: { + de: { + title: 'Stundenplan', + }, + en: { + title: 'schedule', + }, + }, + }, + { + authProvider: 'paia', + icon: 'badge', + route: '/library-account', + title: 'library account', + translations: { + de: { + title: 'Bibliothekskonto', + }, + en: { + title: 'library account', + }, + }, + }, + { + icon: 'settings', + route: '/settings', + title: 'settings', + translations: { + de: { + title: 'Einstellungen', + }, + en: { + title: 'settings', + }, + }, + }, + { + icon: 'rate_review', + route: '/feedback', + title: 'feedback', + translations: { + de: { + title: 'Feedback', + }, + en: { + title: 'feedback', + }, + }, + }, + { + icon: 'info', + route: '/about', + title: 'about', + translations: { + de: { + title: 'Über die App', + }, + en: { + title: 'About the App', + }, + }, + }, + ], + title: 'my app', + route: '/profile', + translations: { + de: { + title: 'Meine App', + }, + en: { + title: 'my app', + }, + }, + }, +]; + +export default menus; diff --git a/backend/backend/config/default/app/user-group-setting.js b/backend/backend/config/default/app/user-group-setting.js new file mode 100644 index 00000000..281197ef --- /dev/null +++ b/backend/backend/config/default/app/user-group-setting.js @@ -0,0 +1,40 @@ +// @ts-check +import {SCSettingInputType, SCThingOriginType, SCThingType} from '@openstapps/core'; + +/** @type {import('@openstapps/core').SCUserGroupSetting} */ +const userGroupSetting = { + categories: ['profile'], + defaultValue: 'students', + description: + 'The user group the app is going to be used.' + + 'This settings for example is getting used for the predefined price category of mensa meals.', + inputType: SCSettingInputType.SingleChoice, + name: 'group', + order: 1, + origin: { + indexed: '2018-09-11T12:30:00Z', + name: 'SCConfigFile Default Values', + type: SCThingOriginType.Remote, + }, + translations: { + de: { + description: + 'Mit welcher Benutzergruppe soll die App verwendet werden?' + + ' Die Einstellung wird beispielsweise für die Vorauswahl der Preiskategorie der Mensa verwendet.', + name: 'Gruppe', + values: ['Studierende', 'Angestellte', 'Gäste'], + }, + en: { + description: + 'The user group the app is going to be used.' + + ' This settings for example is getting used for the predefined price category of mensa meals.', + name: 'Group', + values: ['students', 'employees', 'guests'], + }, + }, + type: SCThingType.Setting, + uid: '2c97aa36-4aa2-43de-bc5d-a2b2cb3a530e', + values: ['students', 'employees', 'guests'], +}; + +export default userGroupSetting; diff --git a/backend/backend/config/default/backend/aggregations.js b/backend/backend/config/default/backend/aggregations.js new file mode 100644 index 00000000..5b60dfb5 --- /dev/null +++ b/backend/backend/config/default/backend/aggregations.js @@ -0,0 +1,51 @@ +// @ts-check +import {SCThingType} from '@openstapps/core'; + +/** @type {import('@openstapps/core').SCBackendAggregationConfiguration[]} */ +const aggregations = [ + { + fieldName: 'categories', + onlyOnTypes: [ + SCThingType.AcademicEvent, + SCThingType.Article, + SCThingType.Building, + SCThingType.Catalog, + SCThingType.Dish, + SCThingType.PointOfInterest, + SCThingType.Room, + ], + }, + { + fieldName: 'inPlace.name', + onlyOnTypes: [ + SCThingType.DateSeries, + SCThingType.Dish, + SCThingType.Floor, + SCThingType.Organization, + SCThingType.PointOfInterest, + SCThingType.Room, + SCThingType.Ticket, + ], + }, + { + fieldName: 'academicTerms.acronym', + onlyOnTypes: [SCThingType.AcademicEvent, SCThingType.SportCourse], + }, + { + fieldName: 'academicTerm.acronym', + onlyOnTypes: [SCThingType.Catalog], + }, + { + fieldName: 'majors', + onlyOnTypes: [SCThingType.AcademicEvent], + }, + { + fieldName: 'keywords', + onlyOnTypes: [SCThingType.Article, SCThingType.Book, SCThingType.Message, SCThingType.Video], + }, + { + fieldName: 'type', + }, +]; + +export default aggregations; diff --git a/backend/backend/config/default/backend/boostings.js b/backend/backend/config/default/backend/boostings.js new file mode 100644 index 00000000..abfae65d --- /dev/null +++ b/backend/backend/config/default/backend/boostings.js @@ -0,0 +1,103 @@ +// @ts-check +import { + month, + sommerRange, + ssAcronymLong, + ssAcronymShort, + winterRange, + wsAcronymLong, + wsAcronymShort, +} from '../tools/semester-acronym.js'; +import {SCThingType} from '@openstapps/core'; + +/** @type {import('@openstapps/core').SCBackendConfigurationSearchBoostingContext} */ +const boostings = { + default: [ + { + factor: 1, + fields: { + 'academicTerms.acronym': { + [ssAcronymShort]: sommerRange.includes(month) ? 1.1 : 1.05, + [wsAcronymShort]: winterRange.includes(month) ? 1.1 : 1.05, + [ssAcronymLong]: sommerRange.includes(month) ? 1.1 : 1.05, + [wsAcronymLong]: winterRange.includes(month) ? 1.1 : 1.05, + }, + }, + type: SCThingType.AcademicEvent, + }, + { + factor: 1, + fields: { + categories: { + 'course': 1.08, + 'integrated course': 1.08, + 'introductory class': 1.05, + 'lecture': 1.1, + 'seminar': 1.01, + 'tutorial': 1.05, + }, + }, + type: SCThingType.AcademicEvent, + }, + { + factor: 1.6, + type: SCThingType.Building, + }, + { + factor: 1, + fields: { + categories: { + cafe: 1.1, + learn: 1.1, + library: 1.2, + restaurant: 1.1, + }, + }, + type: SCThingType.PointOfInterest, + }, + { + factor: 1, + fields: { + categories: { + 'main dish': 2, + }, + }, + type: SCThingType.Dish, + }, + ], + dining: [ + { + factor: 1, + fields: { + categories: { + 'cafe': 2, + 'canteen': 2, + 'restaurant': 2, + 'restroom': 1.2, + 'student canteen': 2, + }, + }, + type: SCThingType.Building, + }, + { + factor: 2, + type: SCThingType.Dish, + }, + ], + place: [ + { + factor: 2, + type: SCThingType.Building, + }, + { + factor: 2, + type: SCThingType.PointOfInterest, + }, + { + factor: 2, + type: SCThingType.Room, + }, + ], +}; + +export default boostings; diff --git a/backend/backend/config/default/backend/index.js b/backend/backend/config/default/backend/index.js new file mode 100644 index 00000000..bf5b72d6 --- /dev/null +++ b/backend/backend/config/default/backend/index.js @@ -0,0 +1,73 @@ +// @ts-check +import {SCThingType} from '@openstapps/core'; +import aggregations from './aggregations.js'; +import boostings from './boostings.js'; +import {readFile} from 'fs/promises'; + +/** @type {import('@openstapps/core').SCBackendInternalConfiguration} */ +export const internal = { + aggregations, + boostings, +}; + +/** @type {import('@openstapps/core').SCBackendConfiguration} */ +export const backend = { + SCVersion: JSON.parse(await readFile('package.json', 'utf8')).version, + externalRequestTimeout: 5000, + hiddenTypes: [SCThingType.DateSeries, SCThingType.Diff, SCThingType.Floor], + mappingIgnoredTags: ['minlength', 'pattern', 'see', 'tjs-format'], + maxMultiSearchRouteQueries: 5, + maxRequestBodySize: 512 * 1024, + name: 'Goethe-Universität Frankfurt am Main', + namespace: '909a8cbc-8520-456c-b474-ef1525f14209', + sortableFields: [ + { + fieldName: 'name', + sortTypes: ['ducet'], + }, + { + fieldName: 'type', + sortTypes: ['ducet'], + }, + { + fieldName: 'categories', + onlyOnTypes: [ + SCThingType.AcademicEvent, + SCThingType.Building, + SCThingType.Catalog, + SCThingType.Dish, + SCThingType.PointOfInterest, + SCThingType.Room, + ], + sortTypes: ['ducet'], + }, + { + fieldName: 'geo', + onlyOnTypes: [SCThingType.Building, SCThingType.PointOfInterest, SCThingType.Room], + sortTypes: ['distance'], + }, + { + fieldName: 'geo', + onlyOnTypes: [SCThingType.Building, SCThingType.PointOfInterest, SCThingType.Room], + sortTypes: ['distance'], + }, + { + fieldName: 'inPlace.geo', + onlyOnTypes: [ + SCThingType.DateSeries, + SCThingType.Dish, + SCThingType.Floor, + SCThingType.Organization, + SCThingType.PointOfInterest, + SCThingType.Room, + SCThingType.Ticket, + ], + sortTypes: ['distance'], + }, + { + fieldName: 'offers', + onlyOnTypes: [SCThingType.Dish], + sortTypes: ['price'], + }, + ], +}; diff --git a/backend/backend/config/default/backendrc.js b/backend/backend/config/default/backendrc.js new file mode 100644 index 00000000..9d2d20e5 --- /dev/null +++ b/backend/backend/config/default/backendrc.js @@ -0,0 +1,26 @@ +// @ts-check +import app from './app/index.js'; +import {backend, internal} from './backend/index.js'; + +/** + * This is the default configuration for app and backend + * + * University-specific files can be created with the following naming scheme: default-.ts + * + * To select your university-specific configuration which is merged from this default file and your university-specific + * file, you have to supply the `NODE_APP_INSTANCE` environment variable with your license plate + * + * To get more information about the meaning of specific fields, please have a look at `@openstapps/core` or use your + * IDE to read the TSDoc documentation. + * + * @type {import('@openstapps/core').SCConfigFile} + */ +const config = { + app, + auth: {}, + backend, + internal, + uid: 'f-u', +}; + +export default config; diff --git a/backend/backend/config/default/elasticsearchrc.js b/backend/backend/config/default/elasticsearchrc.js new file mode 100644 index 00000000..0b6feade --- /dev/null +++ b/backend/backend/config/default/elasticsearchrc.js @@ -0,0 +1,33 @@ +// @ts-check + +/** + * This is the default configuration for elasticsearch (a database) + * + * To select your university-specific configuration which is merged from this default file and your university-specific + * file, you have to supply the `NODE_APP_INSTANCE` environment variable with your license plate + * + * To select a different database, you have to supply the `NODE_CONFIG_ENV` environment variable with a database name + * that is implemented in the backend + * + * To get more information about the meaning of specific fields, please use your IDE to read the TSDoc documentation. + * + * @type {import('../../src/storage/elasticsearch/types/elasticsearch.js')} + */ +const config = { + internal: { + database: { + name: 'elasticsearch', + version: '5.6', + query: { + minMatch: '75%', + queryType: 'dis_max', + matchBoosting: 1.3, + fuzziness: 'AUTO', + cutoffFrequency: 0, + tieBreaker: 0, + }, + }, + }, +}; + +export default config; diff --git a/backend/backend/config/prometheus.json b/backend/backend/config/default/prometheusrc.json similarity index 100% rename from backend/backend/config/prometheus.json rename to backend/backend/config/default/prometheusrc.json diff --git a/backend/backend/config/default/tools/markdown.js b/backend/backend/config/default/tools/markdown.js new file mode 100644 index 00000000..b5060cab --- /dev/null +++ b/backend/backend/config/default/tools/markdown.js @@ -0,0 +1,29 @@ +// @ts-check +import {readFile} from 'fs/promises'; +import {SCAboutPageContentType} from '@openstapps/core'; + +/** + * Usage: + * + * ```js + * await markdown('./page.md', import.meta.url) + * ``` + * + * @param {string} path relative path to the file, omitting the language marker + * @param {string | URL} base base path, usually import.meta.url + * @returns {Promise} + */ +export async function markdown(path, base) { + const de = await readFile(new URL(path.replace(/\.md$/, '.de.md'), base), 'utf8'); + const en = await readFile(new URL(path.replace(/\.md$/, '.en.md'), base), 'utf8'); + + return { + value: de, + translations: { + en: { + value: en, + }, + }, + type: SCAboutPageContentType.MARKDOWN, + }; +} diff --git a/backend/backend/config/default/tools/semester-acronym.js b/backend/backend/config/default/tools/semester-acronym.js new file mode 100644 index 00000000..2d4d6c35 --- /dev/null +++ b/backend/backend/config/default/tools/semester-acronym.js @@ -0,0 +1,34 @@ +// @ts-check +/** + * Generates a range of numbers that represent consecutive calendar months + * + * @param {number} startMonth The month to start with (inclusive) + * @param {number} endMonth The month to end with (inclusive) + * @returns {number[]} + */ +export function yearSlice(startMonth, endMonth) { + let months = [...Array.from({length: 13}).keys()].slice(1); + months = [...months, ...months]; + if (!months.includes(startMonth) || !months.includes(endMonth)) { + throw new Error(`Given months not part of a year! Check ${startMonth} or ${endMonth}!`); + } + + const startIndex = months.indexOf(startMonth); + const endIndex = + months.indexOf(endMonth) <= startIndex ? months.lastIndexOf(endMonth) : months.indexOf(endMonth); + + return months.slice(startIndex, endIndex + 1); +} + +export const sommerRange = yearSlice(3, 8); +export const winterRange = yearSlice(9, 2); +export const month = new Date().getMonth(); +export const year = new Date().getFullYear(); +export const winterYearOffset = month < winterRange[0] ? -1 : 0; +export const sommerYear = year + (month <= winterRange[winterRange.length] ? -1 : 0); +export const winterYear = `${year + winterYearOffset}/${(year + 1 + winterYearOffset).toString().slice(-2)}`; + +export const wsAcronymShort = `WS ${winterYear}`; +export const ssAcronymShort = `SS ${sommerYear}`; +export const wsAcronymLong = `WiSe ${winterYear}`; +export const ssAcronymLong = `SoSe ${sommerYear}`; diff --git a/backend/backend/config/elasticsearch-b-tu.ts b/backend/backend/config/elasticsearch-b-tu.ts deleted file mode 100644 index 257b54fa..00000000 --- a/backend/backend/config/elasticsearch-b-tu.ts +++ /dev/null @@ -1,21 +0,0 @@ -// tslint:disable:no-default-export -// tslint:disable:no-magic-numbers -import {RecursivePartial} from '@openstapps/logger/lib/common'; -import {ElasticsearchConfigFile} from '../src/storage/elasticsearch/types/elasticsearch.js'; - -/** - * This is the database configuration for the technical university of berlin - */ -const config: RecursivePartial = { - internal: { - database: { - name: 'elasticsearch', - query: { - minMatch: '60%', - queryType: 'query_string', - }, - }, - }, -}; - -export default config; diff --git a/backend/backend/config/elasticsearch.ts b/backend/backend/config/elasticsearch.ts deleted file mode 100644 index c318f165..00000000 --- a/backend/backend/config/elasticsearch.ts +++ /dev/null @@ -1,35 +0,0 @@ -// tslint:disable:no-default-export -// tslint:disable:no-magic-numbers -import {ElasticsearchConfigFile} from '../src/storage/elasticsearch/types/elasticsearch-config.js'; - -/** - * This is the default configuration for elasticsearch (a database) - * - * University specific files can be created with following naming scheme: elasticsearch-.ts - * - * To select your university specific configuration which is merged from this default file and your university specific - * file, you have to supply the `NODE_APP_INSTANCE` environment variable with your license plate - * - * To select a different database you have to supply the `NODE_CONFIG_ENV` environment variable with a database name - * that is implemented in the backend - * - * To get more information about the meaning of specific fields please use your IDE to read the TSDoc documentation. - */ -const config: ElasticsearchConfigFile = { - internal: { - database: { - name: 'elasticsearch', - version: '8.4', - query: { - minMatch: '75%', - queryType: 'dis_max', - matchBoosting: 1.3, - fuzziness: 'AUTO', - cutoffFrequency: 0, - tieBreaker: 0, - }, - }, - }, -}; - -export default config; diff --git a/backend/backend/config/f-u/about-pages/about.de.md b/backend/backend/config/f-u/about-pages/about.de.md new file mode 100644 index 00000000..12b55192 --- /dev/null +++ b/backend/backend/config/f-u/about-pages/about.de.md @@ -0,0 +1,7 @@ +Open StApps bietet Studierenden aller beteiligten Hochschulen eine qualitativ +hochwertige App für den Studienalltag. Open StApps-Verbundpartner integrieren +generalisierbare Studierendenprozesse so in App-Module, dass diese auch +von anderen Hochschulen verwendet werden können. Die in der Open StApps App +verwendeten Daten einer Datenquelle sind in einem generalisierten Datenmodell +so aufbereitet, dass ein Austausch oder Abschaltung der Datenquelle problemlos möglich +ist und die Open StApps App problemlos weiterhin funktionsfähig bleibt. diff --git a/backend/backend/config/f-u/about-pages/about.en.md b/backend/backend/config/f-u/about-pages/about.en.md new file mode 100644 index 00000000..516a4834 --- /dev/null +++ b/backend/backend/config/f-u/about-pages/about.en.md @@ -0,0 +1,6 @@ +Open StApps provides students from all participating universities with a +high-quality app for everyday study. Open StApps partners integrate +generalizable student processes into app modules in such a way that they can be +used by other universities. The data of a data source used in the Open StApps app +is prepared in a generalized data model in a way that the data source can be easily +exchanged or switched off while the app continues to function without any problems. diff --git a/backend/backend/config/f-u/about-pages/about.js b/backend/backend/config/f-u/about-pages/about.js new file mode 100644 index 00000000..b79f5514 --- /dev/null +++ b/backend/backend/config/f-u/about-pages/about.js @@ -0,0 +1,126 @@ +// @ts-check +import {SCAboutPageContentType} from '@openstapps/core'; +import {markdown} from '../../default/tools/markdown.js'; + +/** @type {import('@openstapps/core').SCAboutPage} */ +export const about = { + title: 'Über Open StApps', + content: [ + { + title: 'Verbundprojekt mehrerer Hochschulen für eine generische Studierenden-App', + content: await markdown('./about.md', import.meta.url), + translations: { + en: { + title: 'Collaborative project of multiple universities for a single generic study app', + }, + }, + type: SCAboutPageContentType.SECTION, + }, + { + title: 'Goethe-Uni Kontakt', + content: { + rows: [ + [ + { + value: 'Adresse', + translations: { + en: { + value: 'Address', + }, + }, + type: SCAboutPageContentType.MARKDOWN, + }, + { + // language=Markdown + value: + 'Goethe Universität
' + + 'Hochschulrechenzentrum (HRZ)
' + + 'Norbert-Wollheim-Platz 1
' + + '60629 Frankfurt', + translations: {}, + type: SCAboutPageContentType.MARKDOWN, + }, + ], + [ + { + value: 'Kontaktinformation', + translations: { + en: { + value: 'Contact information', + }, + }, + type: SCAboutPageContentType.MARKDOWN, + }, + { + // language=Markdown + value: + '[app@rz.uni-frankfurt.de](mailto:app@rz.uni-frankfurt.de)
' + + '[+49 69 798 32936](tel:+496979832936)
' + + '[https://app.rz.uni-frankfurt.de](https://app.rz.uni-frankfurt.de)', + translations: {}, + type: SCAboutPageContentType.MARKDOWN, + }, + ], + ], + type: SCAboutPageContentType.TABLE, + }, + translations: { + en: { + title: 'Goethe-Uni Contact', + }, + }, + type: SCAboutPageContentType.SECTION, + }, + { + icon: 'newspaper', + title: 'Neue Funktionen / Gelöste Probleme', + link: 'changelog', + translations: { + en: { + title: 'New features / Resolved issues', + }, + }, + type: SCAboutPageContentType.ROUTER_LINK, + }, + { + icon: 'description', + title: 'Impressum', + link: 'imprint', + translations: { + en: { + title: 'Imprint', + }, + }, + type: SCAboutPageContentType.ROUTER_LINK, + }, + { + icon: 'policy', + title: 'Datenschutz', + link: 'privacy', + translations: { + en: { + title: 'Privacy policy', + }, + }, + type: SCAboutPageContentType.ROUTER_LINK, + }, + { + icon: 'copyright', + title: 'Bibliotheken und Lizenzen', + link: 'licenses', + translations: { + en: { + title: 'Libraries and licenses', + }, + }, + type: SCAboutPageContentType.ROUTER_LINK, + }, + ], + translations: { + en: { + title: 'About Open StApps', + }, + }, +}; + +export default about; diff --git a/backend/backend/config/f-u/about-pages/imprint.js b/backend/backend/config/f-u/about-pages/imprint.js new file mode 100644 index 00000000..761f7717 --- /dev/null +++ b/backend/backend/config/f-u/about-pages/imprint.js @@ -0,0 +1,27 @@ +// @ts-check +import {SCAboutPageContentType} from '@openstapps/core'; + +/** @type {import('@openstapps/core').SCAboutPage} */ +export const imprint = { + title: 'Impressum', + content: [ + { + // language=Markdown + value: `[Impressum der Johann Wolfgang Goethe-Universität Frankfurt am Main](https://www.uni-frankfurt.de/impressum)`, + translations: { + en: { + // language=Markdown + value: `[Imprint of the Goethe University Frankfurt](https://www.uni-frankfurt.de/impressum)`, + }, + }, + type: SCAboutPageContentType.MARKDOWN, + }, + ], + translations: { + en: { + title: 'Imprint', + }, + }, +}; + +export default imprint; diff --git a/backend/backend/config/f-u/about-pages/index.js b/backend/backend/config/f-u/about-pages/index.js new file mode 100644 index 00000000..311a70a3 --- /dev/null +++ b/backend/backend/config/f-u/about-pages/index.js @@ -0,0 +1,13 @@ +// @ts-check +import about from './about.js'; +import imprint from './imprint.js'; +import privacy from './privacy.js'; + +/** @type {import('@openstapps/core').SCMap} */ +const aboutPages = { + 'about': about, + 'about/imprint': imprint, + 'about/privacy': privacy, +}; + +export default aboutPages; diff --git a/backend/backend/config/f-u/about-pages/privacy.de.md b/backend/backend/config/f-u/about-pages/privacy.de.md new file mode 100644 index 00000000..b2cfd667 --- /dev/null +++ b/backend/backend/config/f-u/about-pages/privacy.de.md @@ -0,0 +1,166 @@ +# Datenschutzerklärung + +## Kontaktdaten des Verantwortlichen + +Verantwortlich im Sinne der Datenschutz-Grundverordnung und weiterer Vorschriften zum Datenschutz ist die: + +Johann Wolfgang Goethe-Universität Frankfurt am Main vertreten durch ihren Präsidenten
+Theodor-W.-Adorno-Platz 1
+60323 Frankfurt am Main + +Postanschrift:
+Goethe-Universität Frankfurt am Main
+60629 Frankfurt + +Website: http://www.uni-frankfurt.de + +## Kontaktdaten der Datenschutzbeauftragten an der Goethe-Universität + +Sie erreichen die behördlichen Datenschutzbeauftragten der Johann Wolfgang Goethe-Universität Frankfurt am Main unter:
+Mail:
+Website: http://www.uni-frankfurt.de/47859992/datenschutzbeauftragte + +## Informationen zur Verarbeitung personenbezogener Daten + +### 1. Umfang der Verarbeitung personenbezogener Daten + +Personenbezogene Daten sind gemäß Artikel 4 DSGVO alle Informationen, die sich auf eine identifizierte oder identifizierbare natürliche Person beziehen. + +Wir verarbeiten personenbezogene Daten von Ihnen als Nutzer:innen der Goethe-Uni-App, soweit dies zur Bereitstellung einer **funktionsfähigen Applikation** technisch erforderlich ist. + +Weiterhin kann eine Datenverarbeitung auf Ihrer freiwilligen Einwilligung basieren, wenn Sie **spezifische Funktionen** nutzen möchten. + +Wir unterscheiden daher nachfolgend zwischen + +- Zugriffsdaten bei der Nutzung der App: Inhalt der Anfragen, IP-Adressen, Datum/Uhrzeit der Anfrage, Angefragte URL, Fehlermeldungen, Browser-Kennung, HTTP-Header + +- Standortbestimmung und Navigation: freiwillige Standortangaben + +- Nutzer:inneneinstellungen: freiwillige Angabe von a) Sprachpräferenzen (derzeit: deutsch/englisch), b) Status (z. B. Gast/Student) oder c) spezifischen Suchanfragen und Suchergebnissen (Notifications) + +- Kalenderfunktion: freiwillige Nutzung der Kalenderfunktion (optional mit freiwilliger Nutzung einer Synchronisationsfunktion: Opt-in) oder der integrierten Stundenplanfunktion, hierbei werden folgende Daten auf dem Endgerät verarbeitet und gespeichert: Termine und Veranstaltungen + +- Feedbackfunktion und Kontaktaufnahme: freiwillige Nutzung mit der Angabe von Kontaktdaten und ggf. freiwilliger Übermittlung von Protokolldaten + +- Campus Dienste: freiwillige Nutzung mit Verarbeitung von Notenansicht, Matrikelnummer, E-Mailadresse, Name + +- Funktionen der Bibliothek: freiwillige Nutzung mit Verarbeitung von Bibliothekskontodaten, wie z.B. Ausweisnummer mit Name, E-Mailadresse, postalischer Adresse, Nutzungsberechtigung, Bestelldaten, Gebühren, Vormerkung, Ausleihdaten. Die vollständigen Angaben zur Verarbeitung finden Sie in der Datenschutzerklärung der Bibliothek:
+ https://www.ub.uni-frankfurt.de/benutzung/datenschutz.html + +Die App verlinkt an einigen Stellen auf die Website der Goethe-Universität sowie auf andere, externe Websites, die in einem In-App-Browser dargestellt werden. Wir bitten Sie bei Aufruf dieser Websites, die dort geltenden gesonderte Datenschutzhinweise und Erklärungen zu beachten. + +### 2. Zweck(e) der Datenverarbeitung + +**Zugriff auf Standortdaten** + +Für die Navigation benötigt die Goethe-Uni-App Zugriff auf den Standort des verwendeten Endgerätes (Location Based Services). Bei einer Anfrage erhebt die App den aktuellen Standort über GPS, Funkzellendaten und WLAN-Datenbanken, um Ihnen als Nutzer:in Informationen zu Ihrer unmittelbaren Umgebung geben zu können. Der Zugriff auf die Standortdaten erfolgt nur, wenn Sie den Zugriff auf die Standortdaten erlauben. Daten zu Ihrem Standort werden ausschließlich für die Bearbeitung von standortbezogenen Anfragen genutzt und um Ihren Standort auf der Karte anzuzeigen. + +**Zugriff auf Zugriffsdaten** + +Die Speicherung und Verarbeitung von Protokolldateien erfolgt, um die Funktionsfähigkeit der Goethe Uni-App für Sie sicherzustellen. Zudem benötigen wir die die Daten aus Gründen der Sicherheit unserer informationstechnischen Systeme. Eine anderweitige Auswertung oder Weitergabe findet in diesem Zusammenhang nicht statt. + +**Zugriff auf Spracheinstellungen** + +Der Zugriff auf die Spracheinstellung erfolgt um Ihnen die Oberfläche der App in der von Ihnen gewünschten Sprache anzuzeigen. + +**Zugriff auf die Einstellung der Statusgruppe** + +Der Zugriff auf die Einstellung der Statusgruppe erfolgt um Ihnen in der App die für Ihre Gruppe zutreffenden Informationen anzuzeigen, z.B. Mensapreise + +**Zugriff auf personenbezogene Daten bei der Nutzung der Feedbackfunktion** + +Die Verarbeitung der personenbezogenen Daten aus der Feedbackfunktion dient uns zur Kontaktaufnahme und Fehlerbehebung. + +**Zugriff auf personenbezogene Daten bei der Kalendersynchronisation** + +Der Zugriff auf die Termindaten erfolgt um sie bei aktivierter Kalenderfunktion in den Gerätekalender zu schreiben. + +**Zugriff auf Daten der Campus Dienste** + +Der Zugriff auf das Campus Management Systems erfolgt ausschließlich um persönliche Daten der Studierendenverwaltung in der App anzuzeigen (z.B. Prüfungsnoten). + +**Zugriff auf bibliotheksspezifische personenbezogene Daten** + +Der Zugriff auf die Daten (z.B. Ausweisnummer, Name, Postanschrift) erfolgt zur Durchführung von Bestell- und Ausleihverfahren von Büchern und sonstigen Materialien der Universitätsbibliothek. Die vollständigen Angaben zu den Verarbeitungszwecken finden Sie in der Datenschutzerklärung der Bibliothek: https://www.ub.uni-frankfurt.de/benutzung/datenschutz.html + +### 3. Rechtsgrundlage(n) für die Datenverarbeitung + +Die Nutzung der Nutzungs-/Zugriffsdaten („Protokolldateien") basiert auf Artikel 6 Absatz 1 lit. f) DSGVO. + +Für alle spezifischen Funktionen, bei denen die Datenverarbeitung auf Ihrer freiwilligen Einwilligung als Nutzer:innen basiert, werden explizit Einwilligungen bzw. aktive Zustimmungsakte („Opt-In") eingeholt. Die Bereitstellung personenbezogener Daten zu Ihrer Person gegenüber der Goethe-Universität erfolgen dabei auf freiwilliger Basis. Die Rechtsgrundlage ist in diesen Fällen jeweils Artikel 6 Absatz 1 lit. a) DSGVO. Sie können Ihre jeweilige Einwilligung jederzeit einzeln widerrufen bzw. Ihre Einstellungen ändern. + +### 4. Datenlöschung und Speicherdauer + +Die in den Protokolldateien der App erfassten Daten werden sieben Tage nach dem Ende des Zugriffs automatisch gelöscht oder anonymisiert. + +Die Löschfristen bzw. Speicherdauer der in den Bibliotheksystemen erfassten Daten finden Sie in der Datenschutzerklärung der Bibliothek: https://www.ub.uni-frankfurt.de/benutzung/datenschutz.html + +Für alle anderen Funktionen und Dienste gilt: Die Löschung erfolgt hier je nach Vorgabe des genutzten Dienstes. Die personenbezogenen Daten der betroffenen Person werden gelöscht oder gesperrt, sobald der Zweck der Speicherung entfällt. + +### 5. Datenweitergabe/Datenübermittlung + +Ihre personenbezogenen Daten werden von uns nicht an Dritte weitergegeben. + +Von Betreiberseite wird durch technische und organisatorische Maßnahmen sichergestellt, dass Dritte keinen Zugriff auf die verarbeiteten Daten, wie z. B. Nutzungsdaten, erhalten. Ein Auftragsverarbeitungsverhältnis nach Art. 28 DSGVO besteht nicht, da ausschließlich eigene Server verwendet werden. + +### 6. Automatisierte Entscheidungsfindung + +Eine automatisierte Entscheidungsfindung einschließlich Profiling erfolgt nicht. + +## Rechte der betroffenen Person + +Werden personenbezogene Daten von Ihnen verarbeitet, sind Sie Betroffener im Sinne der DSGVO. Die Geltendmachung Ihrer Betroffenenrechte ist kostenfrei. Sie können sich dafür selbstverständlich an uns wenden. Es stehen Ihnen folgende Betroffenenrechte gegenüber der Goethe-Universität zu: + +### 1. Auskunftsrecht + +Sie können von uns als verantwortlicher Stelle eine Bestätigung darüber verlangen, ob und welche Ihrer personenbezogenen Daten von uns verarbeitet werden. Sie haben das Recht, von uns Kopien Ihrer personenbezogenen Daten zu verlangen. Bitte beachten Sie die Ausnahmen, die sich durch spezifische Vorschriften ergeben können. + +### 2. Recht auf Berichtigung + +Sie haben das Recht von uns die Berichtigung und/oder Vervollständigung zu verlangen, sofern die verarbeiteten personenbezogenen Daten, die Sie betreffen, nicht (mehr) richtig oder nicht (mehr) vollständig sind. + +### 3. Recht auf Einschränkung der Verarbeitung + +Unter bestimmten Voraussetzungen können Sie die Einschränkung der Verarbeitung der Sie betreffenden personenbezogenen Daten verlangen, d. h. dass dann Ihre personenbezogenen Daten zwar nicht gelöscht, aber gekennzeichnet werden, so dass eine weitere Verarbeitung eingeschränkt ist. + +### 4. Recht auf Löschung + +Sie können unter bestimmten Voraussetzungen von uns verlangen, dass die Sie betreffenden personenbezogenen Daten unverzüglich gelöscht werden. Dies ist insbesondere der Fall, wenn die personenbezogenen Daten zu dem Zweck, zu dem sie ursprünglich erhoben oder verarbeitet wurden, nicht mehr erforderlich sind. + +### 5. Recht auf Unterrichtung + +Haben Sie das Recht auf Berichtigung, Löschung oder Einschränkung der Verarbeitung uns gegenüber geltend gemacht, sind wir verpflichtet, allen Empfänger/innen, denen die Sie betreffenden personenbezogenen Daten offengelegt wurden, diese Berichtigung oder Löschung der Daten oder Einschränkung der Verarbeitung mitzuteilen, es sei denn, dies erweist sich als unmöglich oder ist mit einem unverhältnismäßigen Aufwand verbunden. Sie sind berechtigt, über diese Empfänger unterrichtet zu werden. + +### 6. Recht auf Datenübertragbarkeit + +Sie haben unter bestimmten Voraussetzungen das Recht von uns zu verlangen, dass Ihre personenbezogenen Daten von uns direkt an einen anderen Verantwortlichen oder an eine andere Organisation übermittelt werden. Alternativ haben Sie unter bestimmten Voraussetzungen das Recht von uns zu verlangen, dass wir Ihnen selbst die Daten in einem maschinenlesbaren Format bereitstellen. + +### 7. Widerspruchsrecht + +Wenn wir Ihre personenbezogenen Daten verarbeiten, weil die Verarbeitung im öffentlichen Interesse, Teil unserer öffentlichen Aufgaben ist bzw. wenn wir Ihre Daten auf Basis eines berechtigten Interesses verarbeiten, haben Sie aus Gründen, die sich aus Ihrer besonderen Situation ergeben, das Recht, jederzeit der Verarbeitung der Sie betreffenden Daten zu widersprechen. + +### 8. Recht auf Widerruf der datenschutzrechtlichen Einwilligungserklärung + +Wenn wir Ihre personenbezogenen Daten verarbeiten, weil Sie uns Ihre Einwilligung gegeben haben, haben Sie jederzeit das Recht, Ihre Einwilligungserklärung zu widerrufen. + +### 9. Recht auf Beschwerde bei einer Aufsichtsbehörde + +Sie haben ferner das Recht auf Beschwerde bei einer Aufsichtsbehörde. Die zuständige Aufsichtsbehörde wird Ihre Beschwerde prüfen. + +## **Kontaktdaten der Aufsichtsbehörde im Bereich Datenschutz** + +Wenn Sie der Ansicht sind, dass eine Verarbeitung der Sie betreffenden personenbezogenen Daten gegen Datenschutzvorschriften verstößt, wenn Sie eine allgemeine Anfrage haben oder wenn Sie sich bei einer zuständigen Fachaufsichtsbehörde beschweren wollen, können Sie sich an den Hessischen Beauftragten für Datenschutz und Informationsfreiheit (HBDI) wenden. + +**Der Hessische Beauftragte für Datenschutz und Informationsfreiheit ist auf unterschiedlichen Wegen erreichbar:** + +**Der Hessische Beauftragte für Datenschutz und Informationsfreiheit**
+Postfach 3163
+65021 Wiesbaden + +Telefon: +49 611 1408 -- 0 + +Für allgemeine Anfragen können Sie ein Kontaktformular nutzen:
+
+
+Für Beschwerden steht Ihnen zudem ein Beschwerdeformular zur Verfügung:
+ diff --git a/backend/backend/config/f-u/about-pages/privacy.en.md b/backend/backend/config/f-u/about-pages/privacy.en.md new file mode 100644 index 00000000..39f06c95 --- /dev/null +++ b/backend/backend/config/f-u/about-pages/privacy.en.md @@ -0,0 +1,166 @@ +# Privacy policy + +## Contact details of the person responsible + +Responsible in the sense of the General Data Protection Regulation and further regulations on data protection is the: + +Johann Wolfgang Goethe-Universität Frankfurt am Main represented by its president
+Theodor-W.-Adorno-Platz 1
+60323 Frankfurt am Main + +Postanschrift:
+Goethe-Universität Frankfurt am Main
+60629 Frankfurt + +Website: http://www.uni-frankfurt.de + +## Contact details of the data protection officer at Goethe University + +You can reach the data protection officers at Johann Wolfgang Goethe University Frankfurt am Main at:
+Mail:
+Website: http://www.uni-frankfurt.de/47859992/datenschutzbeauftragte + +## Information on the processing of personal data + +### 1. Scope of the processing of personal data. + +According to Article 4 DSGVO, personal data is any information relating to an identified or identifiable natural person. + +We process personal data of you as a user inside of the Goethe University App to the extent that this is technically necessary for the provision of a **functional application**. + +Furthermore, data processing may be based on your voluntary consent if you wish to use **specific functions**. + +We therefore distinguish below between + +- Access data when using the app: content of requests, IP addresses, date/time of request, requested URL, error codes, browser identifier, HTTP header. + +- Location and navigation: voluntary location information + +- User settings: voluntary specification of a) language preferences (currently: German/English), b) status (e.g. guest/student) or c) specific search queries and search results (notifications) + +- Calendar function: voluntary use of the calendar function (optional with voluntary use of a sync function: opt-in) or the integrated timetable function. The following data is processed and stored on the users device: appointments and events + +- Feedback function and contacting: voluntary use with the provision of contact data and, if applicable, voluntary transmission of log data + +- Campus services: voluntary use with processing of grade view, matriculation number, email address, name + +- Services of the library: voluntary use with processing of library account data, such as ID number with name, e-mail address, postal address, right of use, order data, fees, reservation, loan data. Full details of processing can be found in the library's privacy policy:
+ https://www.ub.uni-frankfurt.de/benutzung/datenschutz.html + +In some places, the app links to the Goethe University website and to other external websites that are displayed in an in-app browser. When you visit these websites, we ask you to pay attention to the separate data protection notices and declarations that apply there. + +### 2. Purpose(s) of data processing + +**Access to location data**. + +For navigation, the Goethe University app requires access to the location of the end device used (location-based services). When a request is made, the app collects the current location via GPS, radio cell data and WLAN databases in order to be able to give you as a user:in information about your immediate surroundings. The location data is only accessed if you allow access to the location data. Data about your location is only used to process location-related requests and to display your location on the map. + +**Access to access data**. + +Log files are stored and processed to ensure that the Goethe Uni app functions properly for you. In addition, we need the data for reasons of security of our information technology systems. No other evaluation or disclosure takes place in this context. + +**Access to language settings**. + +Access to the language setting is made in order to display the interface of the app in the language of your choice. + +**Access to the status group setting**. + +Access to the status group setting is provided to show you the information in the app that applies to your group, e.g. canteen prices. + +**Access to personal data when using the feedback function**. + +We use the processing of personal data from the feedback function to contact you and troubleshoot problems. + +**Access to personal data when synchronizing calendars**. + +Appointment data is accessed in order to write it to the device calendar when the calendar function is enabled. + +**Access to Campus Services data**. + +Access to the Campus Management System is solely for the purpose of displaying personal student management data in the app (e.g., exam grades). + +**Access to library-specific personal data**. + +Access to data (e.g., ID number, name, mailing address) is for the purpose of carrying out ordering and borrowing procedures for books and other materials from the University Library. Full details of the purposes of processing can be found in the Library's Privacy Policy: https://www.ub.uni-frankfurt.de/benutzung/datenschutz.html + +### 3. Rechtsgrundlage(n) für die Datenverarbeitung + +The use of usage/access data ("log files") is based on Article 6(1)(f) DSGVO. + +For all specific functions where data processing is based on your voluntary consent as a user:in, explicit consent or active acts of consent ("opt-in") are obtained. The provision of personal data about you to Goethe University is done on a voluntary basis. The legal basis in each of these cases is Article 6 (1) a) DSGVO. You can individually revoke your respective consent or change your settings at any time. + +### 4. Data deletion and storage duration + +The data collected in the log files of the app are automatically deleted or anonymized seven days after the end of the access. + +The deletion periods or storage duration of the data collected in the library systems can be found in the library's privacy policy: https://www.ub.uni-frankfurt.de/benutzung/datenschutz.html + +For all other functions and services, the following applies: deletion takes place here depending on the specifications of the service used. The personal data of the data subject will be deleted or blocked as soon as the purpose of the storage no longer applies. + +### 5. Data disclosure/data transfer + +We will not pass on your personal data to third parties. + +On the part of the operator, technical and organizational measures are taken to ensure that third parties do not gain access to the processed data, such as usage data. An order processing relationship according to Art. 28 DSGVO does not exist, as only our own servers are used. + +### 6. Automated decision-making + +Automated decision-making, including profiling, does not take place. + +## Rights of the data subject + +If personal data is processed by you, you are a data subject within the meaning of the GDPR. The assertion of your data subject rights is free of charge. You can, of course, contact us for this purpose. You are entitled to the following data subject rights vis-à-vis Goethe University: + +### 1. Right of access + +You can request confirmation from us as the controller as to whether and which of your personal data is being processed by us. You have the right to request copies of your personal data from us. Please note the exceptions that may arise due to specific regulations. + +### 2. Right of rectification + +You have the right to request us to rectify and/or complete, if the processed personal data concerning you is not (anymore) accurate or not (anymore) complete. + +### 3. Right to restriction of processing + +Under certain conditions, you can request the restriction of the processing of personal data concerning you, i.e. that your personal data is then not deleted, but marked so that further processing is restricted. + +### 4. Right to erasure + +Under certain conditions, you can demand that we delete the personal data concerning you without delay. This is particularly the case if the personal data is no longer necessary for the purpose for which it was originally collected or processed. + +### 5. Right to information + +If you have asserted the right to rectification, erasure or restriction of processing against us, we are obliged to inform all recipients to whom the personal data concerning you have been disclosed of this rectification or erasure of the data or restriction of processing, unless this proves impossible or involves a disproportionate effort. You are entitled to be informed about these recipients. + +### 6. Right to data portability + +Under certain conditions, you have the right to request that we transfer your personal data directly to another controller or organization. Alternatively, under certain conditions, you have the right to request that we ourselves provide you with the data in a machine-readable format. + +### 7. Right to object + +If we process your personal data because the processing is in the public interest, part of our public duties, or if we process your data on the basis of a legitimate interest, you have the right to object at any time to the processing of data relating to you for reasons arising from your particular situation. + +### 8. Right to revoke the declaration of consent under data protection law + +If we process your personal data because you have given us your consent, you have the right to revoke your declaration of consent at any time. + +### 9. Right to lodge a complaint with a supervisory authority + +You also have the right to lodge a complaint with a supervisory authority. The competent supervisory authority will examine your complaint. + +## **Contact details of the supervisory authority in the area of data protection** + +If you believe that the processing of your personal data violates data protection regulations, if you have a general inquiry or if you want to complain to a competent supervisory authority, you can contact the Hessian Commissioner for Data Protection and Freedom of Information (HBDI). + +**The Hessian Commissioner for Data Protection and Freedom of Information can be reached in different ways:** + +**The Hessian Commissioner for Data Protection and Freedom of Information**
+PO Box 3163
+65021 Wiesbaden + +Telephone: +49 611 1408 -- 0 + +For general inquiries you can use a contact form:
+
+
+A complaint form is also available for complaints:
+ diff --git a/backend/backend/config/f-u/about-pages/privacy.js b/backend/backend/config/f-u/about-pages/privacy.js new file mode 100644 index 00000000..40ac0412 --- /dev/null +++ b/backend/backend/config/f-u/about-pages/privacy.js @@ -0,0 +1,15 @@ +// @ts-check +import {markdown} from '../../default/tools/markdown.js'; + +/** @type {import('@openstapps/core').SCAboutPage} */ +export const privacy = { + title: 'Datenschutz', + content: [await markdown('./privacy.md', import.meta.url)], + translations: { + en: { + title: 'Privacy Policy', + }, + }, +}; + +export default privacy; diff --git a/backend/backend/config/f-u/backendrc.js b/backend/backend/config/f-u/backendrc.js new file mode 100644 index 00000000..6afdc7f9 --- /dev/null +++ b/backend/backend/config/f-u/backendrc.js @@ -0,0 +1,82 @@ +// @ts-check +import aboutPages from './about-pages/index.js'; +import defaultApp from '../default/app/index.js'; +import {backend as defaultBackend, internal as defaultInternal} from '../default/backend/index.js'; + +/** + * This is the default configuration for the Goethe university of Frankfurt + * + * @type {import('@openstapps/core').SCConfigFile} + */ +const config = { + auth: { + default: { + client: { + clientId: '1cac3f99-33fa-4234-8438-979f07e0cdab', + scopes: '', + url: 'https://cas.rz.uni-frankfurt.de/cas/oauth2.0', + }, + endpoints: { + authorization: 'https://cas.rz.uni-frankfurt.de/cas/oauth2.0/authorize', + endSession: 'https://cas.rz.uni-frankfurt.de/cas/logout', + mapping: { + id: '$.id', + email: '$.attributes.mailPrimaryAddress', + familyName: '$.attributes.sn', + givenName: '$.attributes.givenName', + name: '$.attributes.givenName', + role: '$.attributes.eduPersonPrimaryAffiliation', + studentId: '$.attributes.employeeNumber', + }, + token: 'https://cas.rz.uni-frankfurt.de/cas/oauth2.0/accessToken', + userinfo: 'https://cas.rz.uni-frankfurt.de/cas/oauth2.0/profile', + }, + }, + paia: { + client: { + clientId: '', + scopes: '', + url: 'https://hds.hebis.de/Shibboleth.sso/UBFFM?target=https://hds.hebis.de/ubffm/paia_login_stub.php', + }, + endpoints: { + authorization: + 'https://hds.hebis.de/Shibboleth.sso/UBFFM?target=https://hds.hebis.de/ubffm/paia_login_stub.php', + endSession: 'https://ubffm.hds.hebis.de/Shibboleth.sso/Logout', + mapping: { + id: '$.email', + name: '$.name', + role: '$.type', + }, + token: 'https://hds.hebis.de/paia/auth/login', + userinfo: 'https://hds.hebis.de/paia/core', + }, + }, + }, + app: { + ...defaultApp, + features: { + extern: { + hisometry: { + authProvider: 'default', + url: 'https://his-self-service.rz.uni-frankfurt.de', + }, + daia: { + url: 'https://daia.hebis.de/DAIA2/UB_Frankfurt', + }, + hebisProxy: { + url: 'https://proxy.ub.uni-frankfurt.de/login?qurl=', + }, + paia: { + authProvider: 'paia', + url: 'https://hds.hebis.de/paia/core', + }, + }, + }, + aboutPages, + }, + backend: defaultBackend, + internal: defaultInternal, + uid: 'f-u', +}; + +export default config; diff --git a/backend/backend/package.json b/backend/backend/package.json index 7e24b74f..6fb2fdc7 100644 --- a/backend/backend/package.json +++ b/backend/backend/package.json @@ -18,26 +18,25 @@ "main": "lib/app.js", "bin": "app.js", "scripts": { - "build": "tsup --dts", - "dev": "tsup --watch", + "build": "tsup", + "dev": "tsup --watch --onSuccess \"pnpm run start\"", "format": "prettier . --ignore-path ../../.gitignore", "format:fix": "prettier --write . --ignore-path ../../.gitignore", - "lint": "eslint --ext .ts src/", + "lint": "tsc --noEmit && eslint --ext .ts src/", "lint:fix": "eslint --fix --ext .ts src/", - "start": "NODE_CONFIG_ENV=elasticsearch ALLOW_NO_TRANSPORT=true node ./lib/cli.js", - "start-debug": "STAPPS_LOG_LEVEL=31 NODE_CONFIG_ENV=elasticsearch ALLOW_NO_TRANSPORT=true node ./lib/cli.js --require ts-node/register", - "test": "pnpm run test-unit && pnpm run test-integration", - "test-integration": "docker-compose -f integration-test.yml pull && docker-compose -f integration-test.yml up --build --abort-on-container-exit --exit-code-from apicli", - "test-unit": "cross-env NODE_CONFIG_ENV=elasticsearch ALLOW_NO_TRANSPORT=true STAPPS_LOG_LEVEL=0 nyc mocha --require ts-node/register --exit 'test/**/*.spec.ts'" + "start": "cross-env NODE_CONFIG_ENV=elasticsearch ALLOW_NO_TRANSPORT=true node app.js", + "start:debug": "cross-env STAPPS_LOG_LEVEL=31 NODE_CONFIG_ENV=elasticsearch ALLOW_NO_TRANSPORT=true node app.js", + "test": "cross-env NODE_CONFIG_ENV=elasticsearch ALLOW_NO_TRANSPORT=true STAPPS_LOG_LEVEL=0 c8 mocha", + "test:integration": "docker-compose -f integration-test.yml pull && docker-compose -f integration-test.yml up --build --abort-on-container-exit --exit-code-from apicli", + "test:unit": "cross-env NODE_CONFIG_ENV=elasticsearch ALLOW_NO_TRANSPORT=true STAPPS_LOG_LEVEL=0 nyc mocha --require ts-node/register --exit 'test/**/*.spec.ts'" }, "dependencies": { "@elastic/elasticsearch": "8.4.0", "@openstapps/core": "workspace:*", "@openstapps/core-tools": "workspace:*", "@openstapps/logger": "workspace:*", - "@redocly/cli": "1.0.0-beta.125", - "@types/node": "14.18.43", - "config": "3.3.9", + "body-parser": "1.20.2", + "cosmiconfig": "8.1.3", "cors": "2.8.5", "express": "4.18.2", "express-prom-bundle": "6.6.0", @@ -45,11 +44,11 @@ "got": "12.6.0", "moment": "2.29.4", "morgan": "1.10.0", - "nock": "13.3.1", + "nock": "13.3.0", "node-cache": "5.1.2", "node-cron": "3.0.2", "nodemailer": "6.9.1", - "prom-client": "14.2.0", + "prom-client": "14.1.1", "promise-queue": "2.2.5", "ts-node": "10.9.1", "uuid": "8.3.2" @@ -61,6 +60,7 @@ "@openstapps/prettier-config": "workspace:*", "@openstapps/tsconfig": "workspace:*", "@testdeck/mocha": "0.3.3", + "@types/body-parser": "1.19.2", "@types/chai": "4.3.4", "@types/chai-as-promised": "7.1.5", "@types/config": "3.3.0", @@ -73,24 +73,20 @@ "@types/node-cron": "3.0.7", "@types/nodemailer": "6.4.7", "@types/promise-queue": "2.2.0", + "@types/sinon": "10.0.14", "@types/sinon-express-mock": "1.3.9", "@types/supertest": "2.0.12", "@types/uuid": "8.3.4", "chai": "4.3.7", "chai-as-promised": "7.1.1", - "conventional-changelog-cli": "2.2.2", "cross-env": "7.0.3", - "eslint": "8.39.0", - "eslint-config-prettier": "8.8.0", - "eslint-plugin-jsdoc": "39.9.1", - "eslint-plugin-prettier": "4.2.1", - "eslint-plugin-unicorn": "45.0.2", + "conventional-changelog-cli": "2.2.2", "get-port": "5.1.1", "mocha": "10.2.0", "mocked-env": "1.3.5", - "nyc": "15.1.0", + "c8": "7.13.0", "redoc-cli": "0.13.20", - "sinon": "14.0.2", + "sinon": "15.0.4", "sinon-express-mock": "2.2.1", "supertest": "6.3.3", "tsup": "6.7.0", @@ -99,11 +95,11 @@ }, "tsup": { "entry": [ - "src/app.ts", - "src/app.ts" + "src/cli.ts" ], "sourcemap": true, "clean": true, + "target": "es2022", "format": "esm", "outDir": "lib" }, @@ -113,9 +109,6 @@ "@openstapps" ] }, - "nyc": { - "extends": "@openstapps/nyc-config" - }, "openstapps-configuration": { "overrides": [ "test" diff --git a/backend/backend/src/app.ts b/backend/backend/src/app.ts index 53a733fc..123c32a7 100644 --- a/backend/backend/src/app.ts +++ b/backend/backend/src/app.ts @@ -20,12 +20,11 @@ import { SCUnsupportedMediaTypeErrorResponse, } from '@openstapps/core'; import {Logger} from '@openstapps/logger'; -import config from 'config'; import cors from 'cors'; import {Express} from 'express'; import morgan from 'morgan'; import path from 'path'; -import {configFile, DEFAULT_TIMEOUT, isTestEnvironment, mailer, plugins, validator} from './common.js'; +import {DEFAULT_TIMEOUT, isTestEnvironment, mailer, plugins, validator} from './common.js'; import {getPrometheusMiddleware} from './middleware/prometheus.js'; import {MailQueue} from './notification/mail-queue.js'; import {bulkAddRouter} from './routes/bulk-add-route.js'; @@ -39,6 +38,7 @@ import {thingUpdateRouter} from './routes/thing-update-route.js'; import {virtualPluginRoute} from './routes/virtual-plugin-route.js'; import {BulkStorage} from './storage/bulk-storage.js'; import {DatabaseConstructor} from './storage/database.js'; +import {backendConfig} from './config.js'; /** * Configure the backend @@ -93,9 +93,9 @@ export async function configureApp(app: Express, databases: {[name: string]: Dat // allow cors preflight requests on every route app.options('*', [cors(corsOptions)]); - // only accept json as content type for all requests + // only accept json as content-type for all requests app.use((request, response, next) => { - // Only accept json as content type + // Only accept json as content-type if (request.is('application/json') !== 'application/json') { // return an error in the response const error = new SCUnsupportedMediaTypeErrorResponse(isTestEnvironment); @@ -111,7 +111,7 @@ export async function configureApp(app: Express, databases: {[name: string]: Dat const chunkGatherer = (chunk: Buffer) => { bodySize += chunk.byteLength; // when adding each chunk size to the total size, check how large it now is. - if (bodySize > configFile.backend.maxRequestBodySize) { + if (bodySize > backendConfig.backend.maxRequestBodySize) { request.off('data', chunkGatherer); request.off('end', endCallback); // return an error in the response @@ -149,7 +149,7 @@ export async function configureApp(app: Express, databases: {[name: string]: Dat ); // validate the config file - const configValidation = validator.validate(configFile, 'SCConfigFile'); + const configValidation = validator.validate(backendConfig, 'SCConfigFile'); // validation failed if (configValidation.errors.length > 0) { @@ -159,14 +159,14 @@ export async function configureApp(app: Express, databases: {[name: string]: Dat } // check if a database name was given - if (!config.has('internal.database.name')) { + if (!backendConfig.internal.database?.name) { throw new Error('You have to configure a database'); } - const database = new databases[config.get('internal.database.name')]( - configFile, + const database = new databases[backendConfig.internal.database.name]( + backendConfig, // mailQueue - typeof mailer !== 'undefined' && config.has('internal.monitoring') ? new MailQueue(mailer) : undefined, + typeof mailer !== 'undefined' && backendConfig.internal.monitoring ? new MailQueue(mailer) : undefined, ); await database.init(); @@ -201,12 +201,12 @@ export async function configureApp(app: Express, databases: {[name: string]: Dat // for plugins, as Express doesn't really want you to unregister routes (and doesn't offer any method to do so at all) app.all('*', async (request, response, next) => { - // if the route exists then call virtual route on the plugin that registered that route + // if the route exists, call virtual route on the plugin that registered that route if (plugins.has(request.originalUrl)) { try { response.json(await virtualPluginRoute(request, plugins.get(request.originalUrl)!)); } catch (error) { - // in case of error send an error response + // in case of an error: send an error response response.status(error.statusCode); response.json(error); } diff --git a/backend/backend/src/common.ts b/backend/backend/src/common.ts index f63dd90b..da6b2a98 100644 --- a/backend/backend/src/common.ts +++ b/backend/backend/src/common.ts @@ -13,9 +13,8 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -import {SCConfigFile, SCPluginMetaData} from '@openstapps/core'; +import {SCPluginMetaData} from '@openstapps/core'; import {Validator} from '@openstapps/core-tools'; -import config from 'config'; import {BackendTransport} from './notification/backend-transport.js'; /** @@ -23,11 +22,6 @@ import {BackendTransport} from './notification/backend-transport.js'; */ export const mailer = BackendTransport.getTransportInstance(); -/** - * Config file content - */ -export const configFile: SCConfigFile = config.util.toObject(); - /** * A validator instance to check if something is a valid JSON object (e.g. a request or a thing) */ @@ -43,11 +37,6 @@ export const isTestEnvironment = process.env.NODE_ENV !== 'production'; */ export const plugins = new Map(); -/** - * The version of the installed core - */ -export const coreVersion: string = configFile.backend.SCVersion; - /** * The default timeout in milliseconds */ diff --git a/backend/backend/src/config.ts b/backend/backend/src/config.ts new file mode 100644 index 00000000..5933b3ad --- /dev/null +++ b/backend/backend/src/config.ts @@ -0,0 +1,54 @@ +import {cosmiconfig, PublicExplorer} from 'cosmiconfig'; +import {SCConfigFile} from '@openstapps/core'; +import path from 'path'; +import deepmerge from 'deepmerge'; + +const fallbackNamespace = 'default'; +const configPath = 'config'; + +/** + * Creates a config loader + * + * @param moduleName the name of the config file (module) + */ +function configLoader(moduleName: string): PublicExplorer { + return cosmiconfig(moduleName, { + searchPlaces: ['js', 'json', 'yml', 'yaml'].map(it => `${moduleName}rc.${it}`), + loaders: { + '.js': filepath => import(`file://${filepath}`).then(it => it.default), + }, + }); +} + +/** + * Find and load a config file + */ +async function findConfig(moduleName: string, namespace = fallbackNamespace): Promise { + return configLoader(moduleName) + .search(path.posix.join('.', configPath, namespace)) + .then(it => it!.config as T) + .catch(() => + configLoader(moduleName) + .search(path.posix.join('.', configPath, fallbackNamespace)) + .then(it => it!.config), + ); +} + +/** + * Loads a config file + */ +async function loadConfig(moduleName: string): Promise { + const namespace = process.env.NODE_APP_INSTANCE; + const database = process.env.NODE_CONFIG_ENV; + + const config = await findConfig(moduleName, namespace); + if (database) { + const databaseConfig = await findConfig(database, namespace); + return deepmerge(config, databaseConfig); + } + + return config; +} + +export const backendConfig = await loadConfig('backend'); +export const prometheusConfig = await loadConfig('prometheus'); diff --git a/backend/backend/src/environment.d.ts b/backend/backend/src/environment.d.ts new file mode 100644 index 00000000..bbc1ea2b --- /dev/null +++ b/backend/backend/src/environment.d.ts @@ -0,0 +1,14 @@ +declare global { + namespace NodeJS { + interface ProcessEnv { + NODE_APP_INSTANCE: 'default' | 'b-tu' | 'f-u' | 'fb-fh' | 'ks-ug' | string | undefined; + NODE_CONFIG_ENV: 'elasticsearch' | string | undefined; + STAPPS_LOG_LEVEL: `${number}`; + ALLOW_NO_TRANSPORT: `${boolean}`; + PORT: `${number}`; + ES_DEBUG: `${boolean}`; + ES_ADDR: string; + PROMETHEUS_MIDDLEWARE: `${boolean}`; + } + } +} diff --git a/backend/backend/src/routes/index-route.ts b/backend/backend/src/routes/index-route.ts index 24b537a4..5126aa7e 100644 --- a/backend/backend/src/routes/index-route.ts +++ b/backend/backend/src/routes/index-route.ts @@ -14,8 +14,8 @@ * along with this program. If not, see . */ import {SCIndexResponse, SCIndexRoute} from '@openstapps/core'; -import {configFile} from '../common.js'; import {createRoute} from './route.js'; +import {backendConfig} from '../config.js'; /** * Contains information for using the index route @@ -29,9 +29,9 @@ export const indexRouter = createRoute( indexRouteModel, async (): Promise => { return { - app: configFile.app, - auth: configFile.auth, - backend: configFile.backend, + app: backendConfig.app, + auth: backendConfig.auth, + backend: backendConfig.backend, }; }, ); diff --git a/backend/backend/src/routes/multi-search-route.ts b/backend/backend/src/routes/multi-search-route.ts index 8838d8e9..2dd3222e 100644 --- a/backend/backend/src/routes/multi-search-route.ts +++ b/backend/backend/src/routes/multi-search-route.ts @@ -20,9 +20,10 @@ import { SCSearchResponse, SCTooManyRequestsErrorResponse, } from '@openstapps/core'; -import {configFile, isTestEnvironment} from '../common.js'; +import {isTestEnvironment} from '../common.js'; import {BulkStorage} from '../storage/bulk-storage.js'; import {createRoute} from './route.js'; +import {backendConfig} from '../config.js'; /** * Contains information for using the multi search route */ @@ -38,7 +39,7 @@ export const multiSearchRouter = createRoute< const bulkMemory: BulkStorage = app.get('bulk'); const queryNames = Object.keys(request); - if (queryNames.length > configFile.backend.maxMultiSearchRouteQueries) { + if (queryNames.length > backendConfig.backend.maxMultiSearchRouteQueries) { throw new SCTooManyRequestsErrorResponse(isTestEnvironment); } diff --git a/backend/backend/src/routes/plugin-register-route.ts b/backend/backend/src/routes/plugin-register-route.ts index 17eda612..f6fc6ad0 100644 --- a/backend/backend/src/routes/plugin-register-route.ts +++ b/backend/backend/src/routes/plugin-register-route.ts @@ -23,8 +23,9 @@ import { } from '@openstapps/core'; import {Logger} from '@openstapps/logger'; import {deepStrictEqual} from 'assert'; -import {configFile, isTestEnvironment, plugins} from '../common.js'; +import {isTestEnvironment, plugins} from '../common.js'; import {createRoute} from './route.js'; +import {backendConfig} from '../config.js'; /** * Contains information for using the route for registering routes @@ -80,10 +81,10 @@ function addPlugin(plugin: SCPluginMetaData): SCPluginRegisterResponse { // it's a new plugin so it can be added to the map of plugins plugins.set(plugin.route, plugin); // add plugin info to app config - if (typeof configFile.app.features.plugins === 'undefined') { - configFile.app.features.plugins = {}; + if (typeof backendConfig.app.features.plugins === 'undefined') { + backendConfig.app.features.plugins = {}; } - configFile.app.features.plugins[plugin.name] = {urlPath: plugin.route}; + backendConfig.app.features.plugins[plugin.name] = {urlPath: plugin.route}; Logger.log( `Registered plugin (name: ${plugin.name}, address: ${plugin.address}) on the route "${plugin.route}".`, ); @@ -102,7 +103,7 @@ function removePlugin(route: string): SCPluginRegisterResponse { } if (plugins.has(route)) { const plugin = plugins.get(route)!; - delete configFile.app.features.plugins?.[plugin.name]; + delete backendConfig.app.features.plugins?.[plugin.name]; } // remove the plugin information using its route as a key plugins.delete(route); diff --git a/backend/backend/src/routes/virtual-plugin-route.ts b/backend/backend/src/routes/virtual-plugin-route.ts index 8561f407..7e8b0571 100644 --- a/backend/backend/src/routes/virtual-plugin-route.ts +++ b/backend/backend/src/routes/virtual-plugin-route.ts @@ -17,7 +17,8 @@ import {SCInternalServerErrorResponse, SCPluginMetaData, SCValidationErrorResponse} from '@openstapps/core'; import {Request} from 'express'; import got from 'got'; -import {configFile, isTestEnvironment, validator} from '../common.js'; +import {isTestEnvironment, validator} from '../common.js'; +import {backendConfig} from '../config.js'; /** * Generic route function used to proxy actual requests to plugins @@ -38,7 +39,7 @@ export async function virtualPluginRoute(request: Request, plugin: SCPluginMetaD prefixUrl: plugin.address, json: request.body, timeout: { - response: configFile.backend.externalRequestTimeout, + response: backendConfig.backend.externalRequestTimeout, }, responseType: 'json', }); diff --git a/backend/backend/src/storage/elasticsearch/elasticsearch.ts b/backend/backend/src/storage/elasticsearch/elasticsearch.ts index 2d35af68..7460d842 100644 --- a/backend/backend/src/storage/elasticsearch/elasticsearch.ts +++ b/backend/backend/src/storage/elasticsearch/elasticsearch.ts @@ -23,6 +23,7 @@ import { } from '@elastic/elasticsearch/lib/api/types'; import {SCConfigFile, SCSearchQuery, SCSearchResponse, SCThings, SCUuid} from '@openstapps/core'; import {Logger} from '@openstapps/logger'; +import {IndicesUpdateAliasesParamsAction, SearchResponse} from 'elasticsearch'; import moment from 'moment'; import {MailQueue} from '../../notification/mail-queue'; import {Bulk} from '../bulk-storage'; diff --git a/backend/backend/test/app.spec.ts b/backend/backend/test/app.spec.ts index 827aa831..e0bc27c2 100644 --- a/backend/backend/test/app.spec.ts +++ b/backend/backend/test/app.spec.ts @@ -20,11 +20,12 @@ import { SCUnsupportedMediaTypeErrorResponse, } from '@openstapps/core'; import {expect} from 'chai'; -import {configFile, DEFAULT_TIMEOUT} from '../src/common.js'; +import {DEFAULT_TIMEOUT} from '../src/common.js'; import {DEFAULT_TEST_TIMEOUT} from './common.js'; import {testApp} from './tests-setup.js'; import sinon from 'sinon'; import mockedEnv from 'mocked-env'; +import {backendConfig} from '../src/config.js'; describe('App', async function () { // increase timeout for the suite @@ -39,6 +40,7 @@ describe('App', async function () { const clock = sandbox.useFakeTimers(); const processExitStub = sandbox.stub(process, 'exit'); // fake NODE_ENV as integration test + // @ts-expect-error type definitions are not working for some reason const restore = mockedEnv({ NODE_ENV: 'integration-test', }); @@ -55,7 +57,7 @@ describe('App', async function () { }); it('should provide request body too large error in case of a body larger than the max size', async function () { - sandbox.stub(configFile.backend, 'maxRequestBodySize').value('3'); + sandbox.stub(backendConfig.backend, 'maxRequestBodySize').value('3'); const {status} = await testApp .post('/') diff --git a/backend/backend/test/common.spec.ts b/backend/backend/test/common.spec.ts index c0038f0d..b2541508 100644 --- a/backend/backend/test/common.spec.ts +++ b/backend/backend/test/common.spec.ts @@ -13,8 +13,8 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -import {yearSlice} from '../config/default.js'; import {expect} from 'chai'; +import {yearSlice} from '../config/default/tools/semester-acronym.js'; describe('Common', function () { describe('yearSlice', function () { diff --git a/backend/backend/test/common.ts b/backend/backend/test/common.ts index 0bd169d4..1c28901f 100644 --- a/backend/backend/test/common.ts +++ b/backend/backend/test/common.ts @@ -20,12 +20,12 @@ import {getIndexUID} from '../src/storage/elasticsearch/util.js'; import {configureApp} from '../src/app.js'; import express from 'express'; import http from 'http'; -import {configFile} from '../src/common.js'; import {MailQueue} from '../src/notification/mail-queue.js'; import {Bulk, BulkStorage} from '../src/storage/bulk-storage.js'; import getPort from 'get-port'; import {Database} from '../src/storage/database.js'; import {v4} from 'uuid'; +import {backendConfig} from '../src/config.js'; /** * Adds routers and configures an (express) app @@ -108,7 +108,7 @@ export class ElasticsearchMock implements Database { } } -export const bulkStorageMock = new BulkStorage(new ElasticsearchMock(configFile)); +export const bulkStorageMock = new BulkStorage(new ElasticsearchMock(backendConfig)); export const bulk: Bulk = { expiration: moment().add(3600, 'seconds').format(), diff --git a/backend/backend/test/notification/backend-transport.spec.ts b/backend/backend/test/notification/backend-transport.spec.ts index 22419913..5f9f918b 100644 --- a/backend/backend/test/notification/backend-transport.spec.ts +++ b/backend/backend/test/notification/backend-transport.spec.ts @@ -13,8 +13,7 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -import {SMTP} from '@openstapps/logger/lib/smtp'; -import {Transport} from '@openstapps/logger/lib/transport'; +import {SMTP, Transport} from '@openstapps/logger'; import {expect} from 'chai'; import mockedEnv from 'mocked-env'; import {BackendTransport, isTransportWithVerification} from '../../src/notification/backend-transport.js'; @@ -54,6 +53,7 @@ describe('Backend transport', function () { it('should not throw in case of error getting SMTP instance when transport not allowed', function () { sandbox.stub(SMTP, 'getInstance').throws('Foo Error'); + // @ts-expect-error wrong type defs for some reason const restore = mockedEnv({ ALLOW_NO_TRANSPORT: 'true', }); @@ -66,6 +66,7 @@ describe('Backend transport', function () { it('should throw in case of error getting SMTP instance when transport is allowed', function () { sandbox.stub(SMTP, 'getInstance').throws('Foo Error'); + // @ts-expect-error wrong type defs for some reason const restore = mockedEnv({ ALLOW_NO_TRANSPORT: undefined, }); diff --git a/backend/backend/test/routes/bulk-route.spec.ts b/backend/backend/test/routes/bulk-route.spec.ts index 37a231ac..bec035ec 100644 --- a/backend/backend/test/routes/bulk-route.spec.ts +++ b/backend/backend/test/routes/bulk-route.spec.ts @@ -21,9 +21,13 @@ import { SCNotFoundErrorResponse, } from '@openstapps/core'; import {expect} from 'chai'; -import {instance as book} from '@openstapps/core/test/resources/indexable/Book.1.json'; import {bulk, DEFAULT_TEST_TIMEOUT} from '../common.js'; import {testApp} from '../tests-setup.js'; +import {readFile} from 'fs/promises'; + +const book = JSON.parse( + await readFile('node_modules/@openstapps/core/test/resources/indexable/Book.1.json', 'utf8'), +); describe('Bulk routes', async function () { // increase timeout for the suite diff --git a/backend/backend/test/routes/plugin-register-route.spec.ts b/backend/backend/test/routes/plugin-register-route.spec.ts index b6097c1c..1bf0d76f 100644 --- a/backend/backend/test/routes/plugin-register-route.spec.ts +++ b/backend/backend/test/routes/plugin-register-route.spec.ts @@ -23,19 +23,24 @@ import { SCValidationErrorResponse, } from '@openstapps/core'; import nock from 'nock'; -import {configFile, plugins} from '../../src/common.js'; +import {plugins} from '../../src/common.js'; import {pluginRegisterHandler} from '../../src/routes/plugin-register-route.js'; import {expect, use} from 'chai'; import chaiAsPromised from 'chai-as-promised'; -import {instance as registerRequest} from '@openstapps/core/test/resources/PluginRegisterRequest.1.json'; import {DEFAULT_TEST_TIMEOUT} from '../common.js'; import {testApp} from '../tests-setup.js'; +import {backendConfig} from '../../src/config.js'; +import {readFile} from 'fs/promises'; // for using promises in expectations (to.eventually.be...) use(chaiAsPromised); +const registerRequest = JSON.parse( + await readFile('node_modules/@openstapps/core/test/resources/PluginRegisterRequest.1.json', 'utf8'), +); + // cast it because of "TS2322: Type 'string' is not assignable to type '"add"'" -export const registerAddRequest: SCPluginAdd = registerRequest as SCPluginAdd; +export const registerAddRequest: SCPluginAdd = registerRequest.instance as SCPluginAdd; export const registerRemoveRequest: SCPluginRemove = { action: 'remove', @@ -48,7 +53,7 @@ describe('Plugin registration', async function () { after(function () { // remove plugins plugins.clear(); - configFile.app.features = {}; + backendConfig.app.features = {}; }); it('should register a plugin', async function () { @@ -57,7 +62,7 @@ describe('Plugin registration', async function () { expect(response).to.deep.equal(bodySuccess) && expect(plugins.size).to.equal(1) && - expect(configFile.app.features.plugins!['Foo Plugin']).to.not.be.empty; + expect(backendConfig.app.features.plugins!['Foo Plugin']).to.not.be.empty; }); it('should allow re-registering the same plugin', async function () { @@ -70,7 +75,7 @@ describe('Plugin registration', async function () { return ( expect(response).to.deep.equal(bodySuccess) && expect(plugins.size).to.equal(1) && - expect(configFile.app.features.plugins!['Foo Plugin']).to.not.be.empty + expect(backendConfig.app.features.plugins!['Foo Plugin']).to.not.be.empty ); }); @@ -102,7 +107,7 @@ describe('Plugin registration', async function () { expect(response).to.deep.equal(bodySuccess) && expect(plugins.size).to.equal(0) && - expect(configFile.app.features.plugins).to.be.empty; + expect(backendConfig.app.features.plugins).to.be.empty; }); it('should throw a "not found" error when removing a plugin whose registered route does not exist', async function () { diff --git a/backend/backend/test/routes/route.spec.ts b/backend/backend/test/routes/route.spec.ts index 10c57b8e..35673d3f 100644 --- a/backend/backend/test/routes/route.spec.ts +++ b/backend/backend/test/routes/route.spec.ts @@ -21,7 +21,7 @@ import { SCRouteHttpVerbs, SCValidationErrorResponse, } from '@openstapps/core'; -import * as bodyParser from 'body-parser'; +import bodyParser from 'body-parser'; import sinon from 'sinon'; import {expect} from 'chai'; import {Application} from 'express'; diff --git a/backend/backend/test/routes/search-route.spec.ts b/backend/backend/test/routes/search-route.spec.ts index 91f91349..bd700ed2 100644 --- a/backend/backend/test/routes/search-route.spec.ts +++ b/backend/backend/test/routes/search-route.spec.ts @@ -21,10 +21,10 @@ import { SCTooManyRequestsErrorResponse, } from '@openstapps/core'; import {expect} from 'chai'; -import {configFile} from '../../src/common.js'; import {DEFAULT_TEST_TIMEOUT} from '../common.js'; import {testApp} from '../tests-setup.js'; import sinon from 'sinon'; +import {backendConfig} from '../../src/config.js'; describe('Search route', async function () { // increase timeout for the suite @@ -96,7 +96,7 @@ describe('Search route', async function () { it('should respond with too many requests error if the number of sub-queries exceed their max number', async function () { const sandbox = sinon.createSandbox(); - sandbox.stub(configFile.backend, 'maxMultiSearchRouteQueries').value(2); + sandbox.stub(backendConfig.backend, 'maxMultiSearchRouteQueries').value(2); const {status} = await testApp .post(multiSearchRoute.urlPath) diff --git a/backend/backend/test/routes/thing-update-route.spec.ts b/backend/backend/test/routes/thing-update-route.spec.ts index 319983a5..d9a69b08 100644 --- a/backend/backend/test/routes/thing-update-route.spec.ts +++ b/backend/backend/test/routes/thing-update-route.spec.ts @@ -17,11 +17,15 @@ import {SCThingUpdateRoute} from '@openstapps/core'; import chaiAsPromised from 'chai-as-promised'; import {bulkStorageMock, DEFAULT_TEST_TIMEOUT} from '../common.js'; import {expect, use} from 'chai'; -import {instance as book} from '@openstapps/core/test/resources/indexable/Book.1.json'; import {testApp} from '../tests-setup.js'; +import {readFile} from 'fs/promises'; use(chaiAsPromised); +const book = JSON.parse( + await readFile('node_modules/@openstapps/core/test/resources/indexable/Book.1.json', 'utf8'), +); + describe('Thing update route', async function () { // increase timeout for the suite this.timeout(DEFAULT_TEST_TIMEOUT); diff --git a/backend/backend/test/routes/virtual-plugin-route.spec.ts b/backend/backend/test/routes/virtual-plugin-route.spec.ts index 31fd6de4..02ecfb44 100644 --- a/backend/backend/test/routes/virtual-plugin-route.spec.ts +++ b/backend/backend/test/routes/virtual-plugin-route.spec.ts @@ -25,7 +25,7 @@ import {mockReq} from 'sinon-express-mock'; import {plugins, validator} from '../../src/common.js'; import {virtualPluginRoute} from '../../src/routes/virtual-plugin-route.js'; import {DEFAULT_TEST_TIMEOUT, FooError} from '../common.js'; -import {registerAddRequest} from './plugin-register-route.spec'; +import {registerAddRequest} from './plugin-register-route.spec.js'; import {testApp} from '../tests-setup.js'; use(chaiAsPromised); @@ -154,12 +154,12 @@ describe('Virtual plugin routes', async function () { plugins.clear(); // // restore everything to default methods (remove stubs) sandbox.restore(); - // clean up request mocks (fixes issue with receiving response from mock from previous test case) + // cleanup request mocks (fixes issue with receiving response from mock from previous test case) nock.cleanAll(); }); it('should properly provide the response of a plugin', async function () { - // lets simulate that the plugin is already registered + // let's simulate that the plugin is already registered plugins.set(registerAddRequest.plugin.route, registerAddRequest.plugin); // mock responses of the plugin, depending on the body sent nock('http://foo.com:1234') @@ -174,20 +174,20 @@ describe('Virtual plugin routes', async function () { .set('Content-Type', 'application/json') .set('Accept', 'application/json') .send({query: 'foo'}); + expect(fooResponse.status).to.be.equal(OK); + expect(fooResponse.body).to.be.deep.equal({result: [{foo: 'foo'}, {bar: 'foo'}]}); + const barResponse = await testApp .post('/foo') .set('Content-Type', 'application/json') .set('Accept', 'application/json') .send({query: 'bar'}); - - expect(fooResponse.status).to.be.equal(OK); - expect(fooResponse.body).to.be.deep.equal({result: [{foo: 'foo'}, {bar: 'foo'}]}); expect(barResponse.status).to.be.equal(OK); expect(barResponse.body).to.be.deep.equal({result: [{foo: 'bar'}, {bar: 'bar'}]}); }); it('should return error response if plugin address is not responding', async function () { - // lets simulate that the plugin is already registered + // let's simulate that the plugin is already registered plugins.set(registerAddRequest.plugin.route, registerAddRequest.plugin); class FooError extends Error {} diff --git a/backend/backend/test/storage/bulk-storage.spec.ts b/backend/backend/test/storage/bulk-storage.spec.ts index 050d9e71..493ee16b 100644 --- a/backend/backend/test/storage/bulk-storage.spec.ts +++ b/backend/backend/test/storage/bulk-storage.spec.ts @@ -17,12 +17,12 @@ import {SCBulkRequest, SCThingType} from '@openstapps/core'; import moment from 'moment'; // eslint-disable-next-line unicorn/import-style import util from 'util'; -import {configFile} from '../../src/common.js'; import {Bulk, BulkStorage} from '../../src/storage/bulk-storage.js'; import {expect} from 'chai'; import {ElasticsearchMock} from '../common.js'; import sinon from 'sinon'; import NodeCache from 'node-cache'; +import {backendConfig} from '../../src/config.js'; describe('Bulk Storage', function () { describe('Bulk', function () { @@ -58,7 +58,7 @@ describe('Bulk Storage', function () { let database: ElasticsearchMock; beforeEach(function () { - database = new ElasticsearchMock(configFile); + database = new ElasticsearchMock(backendConfig); esMock = sandbox.stub(database, 'bulkExpired'); }); diff --git a/backend/backend/test/storage/elasticsearch/elasticsearch.spec.ts b/backend/backend/test/storage/elasticsearch/elasticsearch.spec.ts index 44d3234a..dec68613 100644 --- a/backend/backend/test/storage/elasticsearch/elasticsearch.spec.ts +++ b/backend/backend/test/storage/elasticsearch/elasticsearch.spec.ts @@ -31,10 +31,7 @@ import { SCThings, SCThingType, } from '@openstapps/core'; -import {instance as book} from '@openstapps/core/test/resources/indexable/Book.1.json'; -import {instance as message} from '@openstapps/core/test/resources/indexable/Message.1.json'; -import {Logger} from '@openstapps/logger'; -import {SMTP} from '@openstapps/logger/lib/smtp'; +import {Logger, SMTP} from '@openstapps/logger'; import {expect, use} from 'chai'; import chaiAsPromised from 'chai-as-promised'; import {beforeEach} from 'mocha'; @@ -58,6 +55,8 @@ import * as Monitoring from '../../../src/storage/elasticsearch/monitoring.js'; import * as templating from '../../../src/storage/elasticsearch/templating.js'; import {bulk, DEFAULT_TEST_TIMEOUT, getTransport, getIndex} from '../../common.js'; import fs from 'fs'; +import {backendConfig} from '../../../src/config.js'; +import {readFile} from 'fs/promises'; use(chaiAsPromised); @@ -68,6 +67,13 @@ function searchResponse(...hits: SearchHit[]): SearchResponse { return {hits: {hits}, took: 0, timed_out: false, _shards: {total: 1, failed: 0, successful: 1}}; } +const message = JSON.parse( + await readFile('node_modules/@openstapps/core/test/resources/indexable/Message.1.json', 'utf8'), +); +const book = JSON.parse( + await readFile('node_modules/@openstapps/core/test/resources/indexable/Book.1.json', 'utf8'), +); + describe('Elasticsearch', function () { // increase timeout for the suite this.timeout(DEFAULT_TEST_TIMEOUT); @@ -85,6 +91,7 @@ describe('Elasticsearch', function () { describe('getElasticsearchUrl', function () { it('should provide custom elasticsearch URL if defined', function () { const customAddress = 'http://foo-address:9200'; + // @ts-expect-error wrong type defs for some reason const restore = mockedEnv({ ES_ADDR: customAddress, }); @@ -95,6 +102,7 @@ describe('Elasticsearch', function () { }); it('should provide local URL as fallback', function () { + // @ts-expect-error wrong type defs for some reason const restore = mockedEnv({ ES_ADDR: undefined, }); @@ -193,16 +201,19 @@ describe('Elasticsearch', function () { }); it('should complain (throw an error) if database in config is undefined', function () { - const config: SCConfigFile = {...configFile, internal: {...configFile.internal, database: undefined}}; + const config: SCConfigFile = { + ...backendConfig, + internal: {...backendConfig.internal, database: undefined}, + }; expect(() => new Elasticsearch(config)).to.throw(Error); }); it('should complain (throw an error) if database version is not a string', function () { const config: SCConfigFile = { - ...configFile, + ...backendConfig, internal: { - ...configFile.internal, + ...backendConfig.internal, database: { name: 'foo', version: 123, @@ -218,7 +229,7 @@ describe('Elasticsearch', function () { const loggerErrorStub = sandbox.stub(Logger, 'error').resolves('foo'); sandbox.stub(Diagnostic.prototype, 'on').yields(error); - new Elasticsearch(configFile); + new Elasticsearch(backendConfig); expect(loggerErrorStub.calledWith(error)).to.be.true; }); @@ -228,13 +239,14 @@ describe('Elasticsearch', function () { const loggerLogStub = sandbox.stub(Logger, 'log'); sandbox.stub(Diagnostic.prototype, 'on').yields(null, fakeResponse); - new Elasticsearch(configFile); + new Elasticsearch(backendConfig); expect(loggerLogStub.calledWith(fakeResponse)).to.be.false; + // @ts-expect-error wrong type defs for some reason const restore = mockedEnv({ ES_DEBUG: 'true', }); - new Elasticsearch(configFile); + new Elasticsearch(backendConfig); expect(loggerLogStub.calledWith(fakeResponse)).to.be.true; // restore env variables @@ -249,9 +261,9 @@ describe('Elasticsearch', function () { it('should complain (throw an error) if monitoring is set but mail queue is undefined', async function () { const config: SCConfigFile = { - ...configFile, + ...backendConfig, internal: { - ...configFile.internal, + ...backendConfig.internal, monitoring: { actions: [], watchers: [], @@ -266,9 +278,9 @@ describe('Elasticsearch', function () { it('should setup the monitoring if there is monitoring is set and mail queue is defined', function () { const config: SCConfigFile = { - ...configFile, + ...backendConfig, internal: { - ...configFile.internal, + ...backendConfig.internal, monitoring: { actions: [], watchers: [], @@ -420,7 +432,7 @@ describe('Elasticsearch', function () { const sandbox = sinon.createSandbox(); before(function () { - es = new Elasticsearch(configFile); + es = new Elasticsearch(backendConfig); }); afterEach(function () { @@ -451,7 +463,7 @@ describe('Elasticsearch', function () { const sandbox = sinon.createSandbox(); before(function () { - es = new Elasticsearch(configFile); + es = new Elasticsearch(backendConfig); }); beforeEach(function () { @@ -510,7 +522,7 @@ describe('Elasticsearch', function () { const sandbox = sinon.createSandbox(); before(function () { - es = new Elasticsearch(configFile); + es = new Elasticsearch(backendConfig); }); afterEach(function () { sandbox.restore(); diff --git a/backend/backend/test/storage/elasticsearch/query.spec.ts b/backend/backend/test/storage/elasticsearch/query.spec.ts index d3019fdc..837baada 100644 --- a/backend/backend/test/storage/elasticsearch/query.spec.ts +++ b/backend/backend/test/storage/elasticsearch/query.spec.ts @@ -146,7 +146,7 @@ describe('Query', function () { tieBreaker: 0, }; const config: SCConfigFile = { - ...configFile, + ...backendConfig, }; beforeEach(function () { esConfig = { diff --git a/backend/backend/tsconfig.json b/backend/backend/tsconfig.json index d4e77b68..b36e2eac 100644 --- a/backend/backend/tsconfig.json +++ b/backend/backend/tsconfig.json @@ -2,9 +2,7 @@ "extends": "@openstapps/tsconfig", "compilerOptions": { "resolveJsonModule": true, - "skipLibCheck": true, "useUnknownInCatchVariables": false, - "lib": ["ES2020"] - }, - "exclude": ["./config/", "./test"] + "allowJs": true + } } diff --git a/configuration/backend-config/about-page.xsd b/configuration/backend-config/about-page.xsd new file mode 100644 index 00000000..82d9dd13 --- /dev/null +++ b/configuration/backend-config/about-page.xsd @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/configuration/backend-config/menu.xsd b/configuration/backend-config/menu.xsd new file mode 100644 index 00000000..4db232a7 --- /dev/null +++ b/configuration/backend-config/menu.xsd @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/configuration/backend-config/package.json b/configuration/backend-config/package.json new file mode 100644 index 00000000..e1b57275 --- /dev/null +++ b/configuration/backend-config/package.json @@ -0,0 +1,17 @@ +{ + "name": "@openstapps/backend-config", + "description": "Backend Configuration for OpenStApps", + "version": "2.1.0", + "type": "module", + "license": "GPL-3.0-only", + "author": "Thea Schöbl ", + "devDependencies": { + "@openstapps/tsconfig": "workspace:*", + "@openstapps/prettier-config": "workspace:*", + "@openstapps/eslint-config": "workspace:*" + }, + "prettier": "@openstapps/prettier-config", + "eslintConfig": { + "extends": ["@openstapps/eslint-configt"] + } +} diff --git a/configuration/backend-config/test/about-page.xml b/configuration/backend-config/test/about-page.xml new file mode 100644 index 00000000..759858ad --- /dev/null +++ b/configuration/backend-config/test/about-page.xml @@ -0,0 +1,44 @@ + + +
+
+ + Open StApps bietet Studierenden aller beteiligten Hochschulen eine qualitativ + hochwertige App für den Studienalltag. Open StApps-Verbundpartner integrieren + generalisierbare Studierendenprozesse so in App-Module, dass diese auch + von anderen Hochschulen verwendet werden können. Die in der Open StApps App + verwendeten Daten einer Datenquelle sind in einem generalisierten Datenmodell + so aufbereitet, dass ein Austausch oder Abschaltung der Datenquelle problemlos möglich + ist und die Open StApps App problemlos weiterhin funktionsfähig bleibt. + +
+
+ + + Adresse + + Goethe Universität<br> + Hochschulrechenzentrum (HRZ)<br> + Norbert-Wollheim-Platz 1<br> + 60629 Frankfurt + + + + Kontaktinformationen + + [app@rz.uni-frankfurt.de](mailto:app@rz.uni-frankfurt.de)<br> + [+49 69 798 32936](tel:+496979832936)<br> + [https://app.rz.uni-frankfurt.de](https://app.rz.uni-frankfurt.de) + + + +
+ Neue Funktionen / Gelöste Probleme + Impressum + Datenschutz + Bibliotheken und Lizenzen +
+
diff --git a/configuration/backend-config/test/menu.de.xml b/configuration/backend-config/test/menu.de.xml new file mode 100644 index 00000000..14e12782 --- /dev/null +++ b/configuration/backend-config/test/menu.de.xml @@ -0,0 +1,18 @@ + + + + Aktuelles + + + + + + + + + + + + + + diff --git a/configuration/backend-config/test/menu.xml b/configuration/backend-config/test/menu.xml new file mode 100644 index 00000000..900381d0 --- /dev/null +++ b/configuration/backend-config/test/menu.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/configuration/backend-config/tsconfig.json b/configuration/backend-config/tsconfig.json new file mode 100644 index 00000000..579a39ff --- /dev/null +++ b/configuration/backend-config/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "@openstapps/tsconfig" +} diff --git a/configuration/eslint-config/index.js b/configuration/eslint-config/index.js index 911386d1..a1e38064 100644 --- a/configuration/eslint-config/index.js +++ b/configuration/eslint-config/index.js @@ -3,14 +3,14 @@ module.exports = { ignorePatterns: ['projects/**/*'], parserOptions: { sourceType: 'module', - ecmaVersion: 2021, + ecmaVersion: 2022, }, overrides: [ { files: ['*.ts'], parser: '@typescript-eslint/parser', parserOptions: { - ecmaVersion: 2021, + ecmaVersion: 2022, sourceType: 'module', project: ['tsconfig.json'], createDefaultProgram: true, diff --git a/configuration/eslint-config/package.json b/configuration/eslint-config/package.json index 034cad3e..97fa3004 100644 --- a/configuration/eslint-config/package.json +++ b/configuration/eslint-config/package.json @@ -1,6 +1,6 @@ { "name": "@openstapps/eslint-config", - "description": "A collection of configuration base files for StApps projects.", + "description": "A collection of configuration base files for StApps projects. Just an (unused) experiment for now.", "version": "2.1.0", "type": "commonjs", "license": "GPL-3.0-only", diff --git a/packages/api/test/bulk.spec.ts b/packages/api/test/bulk.spec.ts index 574d9ea1..49d3678d 100644 --- a/packages/api/test/bulk.spec.ts +++ b/packages/api/test/bulk.spec.ts @@ -42,7 +42,7 @@ describe('Bulk', function () { afterEach(function () { sandbox.restore(); - }) + }); it('should add', async function () { sandbox.on(client, 'invokeRoute', () => { diff --git a/packages/api/test/connector-client.spec.ts b/packages/api/test/connector-client.spec.ts index 00c952f9..9c79a805 100644 --- a/packages/api/test/connector-client.spec.ts +++ b/packages/api/test/connector-client.spec.ts @@ -35,10 +35,17 @@ import chaiSpies from 'chai-spies'; import clone = require('rfdc'); import moment from 'moment'; import traverse from 'traverse'; -import {ConnectorClient, EmptyBulkError, NamespaceNotDefinedError, HttpClient, HttpClientRequest, HttpClientResponse} from '../src/index.js'; -import path from "path"; -import {fileURLToPath} from "url"; -import {readdir, readFile} from "fs/promises"; +import { + ConnectorClient, + EmptyBulkError, + NamespaceNotDefinedError, + HttpClient, + HttpClientRequest, + HttpClientResponse, +} from '../src/index.js'; +import path from 'path'; +import {fileURLToPath} from 'url'; +import {readdir, readFile} from 'fs/promises'; chai.should(); chai.use(chaiSpies); @@ -333,7 +340,7 @@ describe('ConnectorClient', function () { expect(ConnectorClient.makeUUID('foo', 'f-u')).not.to.be.equal(uuid); }); - it('should fail making a uuid', async function (){ + it('should fail making a uuid', async function () { expect(() => { ConnectorClient.makeUUID('foo', 'b-u'); }).to.throw(NamespaceNotDefinedError); @@ -353,12 +360,14 @@ describe('ConnectorClient', function () { const testFiles = await readdir(pathToTestFiles); - const testInstances = await Promise.all(testFiles.map(async testFile => { - const buffer = await readFile(path.join(pathToTestFiles, testFile)); - const content = JSON.parse(buffer.toString()); + const testInstances = await Promise.all( + testFiles.map(async testFile => { + const buffer = await readFile(path.join(pathToTestFiles, testFile)); + const content = JSON.parse(buffer.toString()); - return content.instance; - })); + return content.instance; + }), + ); for (const testInstance of testInstances) { const checkInstance = clone()(testInstance); diff --git a/packages/api/test/e2e.spec.ts b/packages/api/test/e2e.spec.ts index 59a3e929..d704992d 100644 --- a/packages/api/test/e2e.spec.ts +++ b/packages/api/test/e2e.spec.ts @@ -33,9 +33,9 @@ import {createFileSync} from 'fs-extra'; // eslint-disable-next-line unicorn/prevent-abbreviations import {e2eRun, getItemsFromSamples, ApiError, HttpClient, RequestOptions, Response} from '../src/index.js'; import {RecursivePartial} from './client.spec.js'; -import {expect} from "chai"; -import path from "path"; -import {fileURLToPath} from "url"; +import {expect} from 'chai'; +import path from 'path'; +import {fileURLToPath} from 'url'; chai.should(); chai.use(chaiSpies); @@ -184,14 +184,18 @@ describe('e2e Connector', function () { if (!existsSync(emptyDirectoryPath)) { mkdirSync(emptyDirectoryPath); } - await e2eRun(httpClient, {to: 'http://localhost', samplesLocation: emptyDirectoryPath}).should.be.rejectedWith( - 'Could not index samples. None were retrieved from the file system.', - ); + await e2eRun(httpClient, { + to: 'http://localhost', + samplesLocation: emptyDirectoryPath, + }).should.be.rejectedWith('Could not index samples. None were retrieved from the file system.'); rmdirSync(emptyDirectoryPath); }); it('should fail to index directory without json data', async function () { - const somewhatFilledDirectoryPath = path.join(path.dirname(fileURLToPath(import.meta.url)), 'somewhatFilledDir'); + const somewhatFilledDirectoryPath = path.join( + path.dirname(fileURLToPath(import.meta.url)), + 'somewhatFilledDir', + ); if (!existsSync(somewhatFilledDirectoryPath)) { mkdirSync(somewhatFilledDirectoryPath); } diff --git a/packages/api/test/http-client.spec.ts b/packages/api/test/http-client.spec.ts index 0e00aa0e..00afe80e 100644 --- a/packages/api/test/http-client.spec.ts +++ b/packages/api/test/http-client.spec.ts @@ -21,7 +21,7 @@ import {HttpClient} from '../src/index.js'; describe('HttpClient', function () { afterEach(function () { nock.cleanAll(); - }) + }); it('should construct', function () { expect(() => { diff --git a/packages/api/test/plugin.spec.ts b/packages/api/test/plugin.spec.ts index 73317ccb..f6d04c29 100644 --- a/packages/api/test/plugin.spec.ts +++ b/packages/api/test/plugin.spec.ts @@ -19,9 +19,9 @@ import {expect} from 'chai'; import chaiSpies from 'chai-spies'; import {HttpClient} from '../src/index.js'; import {TestPlugin} from './plugin-resources/test-plugin.js'; -import path from "path"; -import {readFile} from "fs/promises"; -import {fileURLToPath} from "url"; +import path from 'path'; +import {readFile} from 'fs/promises'; +import {fileURLToPath} from 'url'; chai.use(chaiSpies); @@ -56,12 +56,12 @@ describe('Plugin', function () { '', '', ); - }) + }); afterEach(async function () { await testPlugin.close(); sandbox.restore(); - }) + }); it('should construct', async function () { const converter = new Converter( diff --git a/packages/core-tools/src/validate.ts b/packages/core-tools/src/validate.ts index c6bed361..cfd29ec7 100644 --- a/packages/core-tools/src/validate.ts +++ b/packages/core-tools/src/validate.ts @@ -36,13 +36,14 @@ export class Validator { */ private readonly ajv = new Ajv.default({ verbose: true, + allowUnionTypes: true, code: {regExp: re2 as never}, }); /** * Map of schema names to schemas */ - private readonly schemas: { [type: string]: Schema } = {}; + private readonly schemas: {[type: string]: Schema} = {}; /** * A wrapper function for Ajv that transforms the error into the compatible old error @@ -94,7 +95,7 @@ export class Validator { if (schema === undefined) { if (isThingWithType(instance)) { // schema name can be inferred from type string - const schemaSuffix = (instance as { type: string }).type + const schemaSuffix = (instance as {type: string}).type .split(' ') .map((part: string) => { return part.slice(0, 1).toUpperCase() + part.slice(1); @@ -178,7 +179,10 @@ export async function validateFiles( await v.addSchemas(schemaDirectory); // get a list of files to test - const testFiles = await glob(path.posix.join(resourcesDirectory.replaceAll(path.sep, path.posix.sep), '*.json'), {absolute: true}); + const testFiles = await glob( + path.posix.join(resourcesDirectory.replaceAll(path.sep, path.posix.sep), '*.json'), + {absolute: true}, + ); if (testFiles.length === 0) { throw new Error(`No test files in ${resourcesDirectory}!`); diff --git a/packages/core/test/schema.spec.ts b/packages/core/test/schema.spec.ts index 4ddfe23d..7f9e52d8 100644 --- a/packages/core/test/schema.spec.ts +++ b/packages/core/test/schema.spec.ts @@ -8,7 +8,10 @@ describe('Schema', function () { this.slow(10_000); it('should validate against test files', async function () { - const errorsPerFile = await validateFiles(path.resolve('lib', 'schema'), path.resolve('test', 'resources')); + const errorsPerFile = await validateFiles( + path.resolve('lib', 'schema'), + path.resolve('test', 'resources'), + ); await mkdir('report', {recursive: true}); await writeReport(path.join('report', 'index.html'), errorsPerFile); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 60039c42..b47cd6e9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -34,6 +34,7 @@ importers: '@openstapps/prettier-config': workspace:* '@openstapps/tsconfig': workspace:* '@testdeck/mocha': 0.3.3 + '@types/body-parser': 1.19.2 '@types/chai': 4.3.4 '@types/chai-as-promised': 7.1.5 '@types/config': 3.3.0 @@ -47,16 +48,20 @@ importers: '@types/node-cron': 3.0.7 '@types/nodemailer': 6.4.7 '@types/promise-queue': 2.2.0 + '@types/sinon': 10.0.14 '@types/sinon-express-mock': 1.3.9 '@types/supertest': 2.0.12 '@types/uuid': 8.3.4 '@typescript-eslint/eslint-plugin': 5.49.0 '@typescript-eslint/parser': 5.49.0 + body-parser: 1.20.2 + c8: 7.13.0 chai: 4.3.7 chai-as-promised: 7.1.1 - config: 3.3.9 conventional-changelog-cli: 2.2.2 cors: 2.8.5 + cosmiconfig: 8.1.3 + cross-env: 7.0.3 eslint: 8.33.0 eslint-config-prettier: 8.6.0 eslint-plugin-jsdoc: 39.7.4 @@ -75,12 +80,11 @@ importers: node-cache: 5.1.2 node-cron: 3.0.2 nodemailer: 6.9.1 - nyc: 15.1.0 prettier: 2.8.6 prom-client: 14.1.1 promise-queue: 2.2.5 redoc-cli: 0.13.20 - sinon: 14.0.2 + sinon: 15.0.4 sinon-express-mock: 2.2.1 supertest: 6.3.3 ts-node: 10.9.1 @@ -93,8 +97,9 @@ importers: '@openstapps/core': link:../../packages/core '@openstapps/core-tools': link:../../packages/core-tools '@openstapps/logger': link:../../packages/logger - config: 3.3.9 + body-parser: 1.20.2 cors: 2.8.5 + cosmiconfig: 8.1.3 express: 4.18.2 express-prom-bundle: 6.6.0_prom-client@14.1.1 express-promise-router: 4.1.1_fhep4uswmxibn3567pnkkiq3fa @@ -116,6 +121,7 @@ importers: '@openstapps/prettier-config': link:../../configuration/prettier-config '@openstapps/tsconfig': link:../../configuration/tsconfig '@testdeck/mocha': 0.3.3 + '@types/body-parser': 1.19.2 '@types/chai': 4.3.4 '@types/chai-as-promised': 7.1.5 '@types/config': 3.3.0 @@ -129,14 +135,17 @@ importers: '@types/node-cron': 3.0.7 '@types/nodemailer': 6.4.7 '@types/promise-queue': 2.2.0 + '@types/sinon': 10.0.14 '@types/sinon-express-mock': 1.3.9 '@types/supertest': 2.0.12 '@types/uuid': 8.3.4 '@typescript-eslint/eslint-plugin': 5.49.0_bhfhikta3fu7tua7ac2hrytgwe '@typescript-eslint/parser': 5.49.0_o2s6jvgtr2hafiobaqfgu6k2l4 + c8: 7.13.0 chai: 4.3.7 chai-as-promised: 7.1.1_chai@4.3.7 conventional-changelog-cli: 2.2.2 + cross-env: 7.0.3 eslint: 8.33.0 eslint-config-prettier: 8.6.0_eslint@8.33.0 eslint-plugin-jsdoc: 39.7.4_eslint@8.33.0 @@ -145,11 +154,10 @@ importers: get-port: 5.1.1 mocha: 10.2.0 mocked-env: 1.3.5 - nyc: 15.1.0 prettier: 2.8.6 redoc-cli: 0.13.20_ouq63s25oqg3e72zt7ebre7ohe - sinon: 14.0.2 - sinon-express-mock: 2.2.1_sinon@14.0.2 + sinon: 15.0.4 + sinon-express-mock: 2.2.1_sinon@15.0.4 supertest: 6.3.3 tsup: 6.7.0_mwhvu7sfp6vq5ryuwb6hlbjfka typedoc: 0.23.28_typescript@4.8.4 @@ -236,6 +244,32 @@ importers: tsup: 6.7.0_mwhvu7sfp6vq5ryuwb6hlbjfka typedoc: 0.23.28_typescript@4.8.4 + configuration/backend-config: + specifiers: + '@openstapps/eslint-config': workspace:* + '@openstapps/prettier-config': workspace:* + '@openstapps/tsconfig': workspace:* + '@typescript-eslint/eslint-plugin': 5.49.0 + '@typescript-eslint/parser': 5.49.0 + eslint: 8.33.0 + eslint-config-prettier: 8.6.0 + eslint-plugin-jsdoc: 39.7.4 + eslint-plugin-prettier: 4.2.1 + eslint-plugin-unicorn: 45.0.2 + prettier: 2.8.6 + devDependencies: + '@openstapps/eslint-config': link:../eslint-config + '@openstapps/prettier-config': link:../prettier-config + '@openstapps/tsconfig': link:../tsconfig + '@typescript-eslint/eslint-plugin': 5.49.0_bhfhikta3fu7tua7ac2hrytgwe + '@typescript-eslint/parser': 5.49.0_o2s6jvgtr2hafiobaqfgu6k2l4 + eslint: 8.33.0 + eslint-config-prettier: 8.6.0_eslint@8.33.0 + eslint-plugin-jsdoc: 39.7.4_eslint@8.33.0 + eslint-plugin-prettier: 4.2.1_2blprs6rvzrjlgi3xqpegp3hoe + eslint-plugin-unicorn: 45.0.2_eslint@8.33.0 + prettier: 2.8.6 + configuration/eslint-config: specifiers: '@typescript-eslint/eslint-plugin': 5.49.0 @@ -5588,12 +5622,6 @@ packages: engines: {node: '>=14.16'} dev: false - /@sinonjs/commons/1.8.6: - resolution: {integrity: sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==} - dependencies: - type-detect: 4.0.8 - dev: true - /@sinonjs/commons/2.0.0: resolution: {integrity: sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==} dependencies: @@ -5612,20 +5640,6 @@ packages: '@sinonjs/commons': 2.0.0 dev: true - /@sinonjs/fake-timers/9.1.2: - resolution: {integrity: sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==} - dependencies: - '@sinonjs/commons': 1.8.6 - dev: true - - /@sinonjs/samsam/7.0.1: - resolution: {integrity: sha512-zsAk2Jkiq89mhZovB2LLOdTCxJF4hqqTToGP0ASWlhp4I1hqOjcfmZGafXntCN7MDC6yySH0mFHrYtHceOeLmw==} - dependencies: - '@sinonjs/commons': 2.0.0 - lodash.get: 4.4.2 - type-detect: 4.0.8 - dev: true - /@sinonjs/samsam/8.0.0: resolution: {integrity: sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew==} dependencies: @@ -5763,7 +5777,7 @@ packages: resolution: {integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==} dependencies: '@types/connect': 3.4.35 - '@types/node': 18.15.5 + '@types/node': 18.15.3 /@types/bonjour/3.5.10: resolution: {integrity: sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==} @@ -5816,7 +5830,7 @@ packages: /@types/connect/3.4.35: resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==} dependencies: - '@types/node': 18.15.5 + '@types/node': 18.15.3 /@types/cookie/0.4.1: resolution: {integrity: sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==} @@ -6117,13 +6131,7 @@ packages: resolution: {integrity: sha512-wHtSYqZ/c1FytL4qEMVb3tp8UZk1EnUq6Qc4jQv5eJ9N2QtFdS4WU3Wijqzeu5r3/DfZqpvzkxbbZLa0+9F9sA==} dependencies: '@types/express': 4.17.16 - '@types/sinon': 10.0.13 - dev: true - - /@types/sinon/10.0.13: - resolution: {integrity: sha512-UVjDqJblVNQYvVNUsj0PuYYw0ELRmgt1Nt5Vk0pT5f16ROGfcKJY8o1HVuMOJOpD727RrGB9EGvoaTQE5tgxZQ==} - dependencies: - '@types/sinonjs__fake-timers': 8.1.2 + '@types/sinon': 10.0.14 dev: true /@types/sinon/10.0.14: @@ -7455,6 +7463,25 @@ packages: transitivePeerDependencies: - supports-color + /body-parser/1.20.2: + resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + on-finished: 2.4.1 + qs: 6.11.0 + raw-body: 2.5.2 + type-is: 1.6.18 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + /bonjour/3.5.0: resolution: {integrity: sha512-RaVTblr+OnEli0r/ud8InrU7D+G0y6aJhlxaLa6Pwty4+xoxboF1BsUI45tujvRpbj9dQVoglChqonGAsjEBYg==} dependencies: @@ -8684,6 +8711,16 @@ packages: path-type: 4.0.0 dev: true + /cosmiconfig/8.1.3: + resolution: {integrity: sha512-/UkO2JKI18b5jVMJUp0lvKFMpa/Gye+ZgZjKD+DGEN9y7NRcf/nK1A0sp67ONmKtnDCNMS44E6jrk0Yc3bDuUw==} + engines: {node: '>=14'} + dependencies: + import-fresh: 3.3.0 + js-yaml: 4.1.0 + parse-json: 5.2.0 + path-type: 4.0.0 + dev: false + /cpu-features/0.0.4: resolution: {integrity: sha512-fKiZ/zp1mUwQbnzb9IghXtHtDoTMtNeb8oYGx6kX2SYfhnG0HNdBEBIzB9b5KlXu5DQPhfy3mInbBxFcgwAr3A==} engines: {node: '>=10.0.0'} @@ -8736,6 +8773,14 @@ packages: pretty-bytes: 5.6.0 dev: true + /cross-env/7.0.3: + resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==} + engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'} + hasBin: true + dependencies: + cross-spawn: 7.0.3 + dev: true + /cross-spawn/5.1.0: resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==} dependencies: @@ -12712,7 +12757,7 @@ packages: hasBin: true dependencies: '@colors/colors': 1.5.0 - body-parser: 1.20.1 + body-parser: 1.20.2 braces: 3.0.2 chokidar: 3.5.3 connect: 3.7.0 @@ -15874,7 +15919,6 @@ packages: http-errors: 2.0.0 iconv-lite: 0.4.24 unpipe: 1.0.0 - dev: true /rc/1.2.8: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} @@ -16883,23 +16927,12 @@ packages: sinon: 15.0.4 dev: true - /sinon-express-mock/2.2.1_sinon@14.0.2: + /sinon-express-mock/2.2.1_sinon@15.0.4: resolution: {integrity: sha512-z1wqaPMwEnfn0SpigFhVYVS/ObX1tkqyRzRdccX99FgQaLkxGSo4684unr3NCqWeYZ1zchxPw7oFIDfzg1cAjg==} peerDependencies: sinon: '*' dependencies: - sinon: 14.0.2 - dev: true - - /sinon/14.0.2: - resolution: {integrity: sha512-PDpV0ZI3ZCS3pEqx0vpNp6kzPhHrLx72wA0G+ZLaaJjLIYeE0n8INlgaohKuGy7hP0as5tbUd23QWu5U233t+w==} - dependencies: - '@sinonjs/commons': 2.0.0 - '@sinonjs/fake-timers': 9.1.2 - '@sinonjs/samsam': 7.0.1 - diff: 5.1.0 - nise: 5.1.4 - supports-color: 7.2.0 + sinon: 15.0.4 dev: true /sinon/15.0.4: