From 5d73f5f7c709bfc439d35f5341c0f05f04f1e2f0 Mon Sep 17 00:00:00 2001 From: Moritz Utcke Date: Tue, 21 Jan 2025 23:34:01 +0700 Subject: [PATCH] =?UTF-8?q?API=20gr=C3=B6=C3=9Ftenteils=20umgezogen=20und?= =?UTF-8?q?=20Funktionen=20angepasst?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- openapi.json | 2 +- package.json | 9 +- src/astro-typesafe-api-caller.ts | 15 +- src/client/lib/validateAccessToken.ts | 14 +- .../lib/verbrauchsausweisWohnenSpeichern.ts | 174 +++++++----- .../Ausweis/AusweisPreviewContainer.svelte | 4 +- src/components/Ausweis/Ausweisart.svelte | 27 +- .../Ausweis/BilderZusatzsysteme.svelte | 56 ++-- .../Ausweis/ButtonWeiterHilfe.svelte | 4 +- ...ButtonZurueckSpeichernKaufabschluss.svelte | 2 +- src/components/Ausweis/GebaeudeDaten.svelte | 18 +- .../Ausweis/LueftungundLeerstand.svelte | 10 +- .../Ausweis/PerformanceScore.svelte | 16 +- .../SanierungszustandFensterTueren.svelte | 32 +-- .../SanierungszustandHeizungsanlage.svelte | 38 +-- .../SanierungszustandWaermedammung.svelte | 14 +- src/components/Ausweis/StromVerbrauch.svelte | 58 ++-- .../Ausweis/ThermischeKuehlung.svelte | 4 +- src/components/Ausweis/Verbrauch.svelte | 36 +-- src/components/Ausweis/types.ts | 82 +++--- src/components/AusweisPruefenBox.svelte | 76 +++--- .../Dashboard/DashboardAusweis.svelte | 22 +- src/components/PlzSuche.svelte | 10 +- .../Verbrauchsausweis/audits/EndEnergie.ts | 8 +- .../audits/PlzNichtErkannt.ts | 8 +- .../Verbrauchsausweis/audits/hidden.ts | 3 +- .../labels/StromVerbrauchsHelpLabel.svelte | 2 +- .../labels/VerbrauchsHelpLabel.svelte | 10 +- .../verbrauchsausweis-wohnen-2016.json | 2 +- src/lib/APIResponse.ts | 38 --- src/lib/AusweisData.ts | 2 +- .../BedarfsausweisWohnen_18599.ts | 4 +- .../BerechnungRechnerischeLaufzeitHeizung.ts | 2 +- .../VerbrauchsausweisWohnen_2016.ts | 26 +- .../VerbrauchsausweisWohnen_2023.ts | 16 +- src/lib/Berechnungen/endEnergieVerbrauch.ts | 2 +- src/lib/Klimafaktoren.ts | 3 +- .../calculateFormProgress.ts | 8 +- src/lib/XML/AusweisBerechnungen2016.ts | 4 +- .../xmlVerbrauchsausweisWohnen_2016.ts | 6 +- src/lib/XML/getEmpfehlungen.ts | 40 +-- src/lib/altes-system/import.ts | 4 +- src/lib/faker/verbrauchsausweis-wohnen.ts | 4 +- src/lib/middleware/authorization.ts | 8 + .../pdfDatenblattVerbrauchsausweisWohnen.ts | 72 ++--- src/lib/pdf/pdfVerbrauchsausweisWohnen.ts | 34 +-- src/lib/pdf/plugins/variables/index.ts | 6 +- .../templates/GEG24_Verbrauchsausweis.json | 2 +- src/lib/validators/index.ts | 255 ++++++++++++++++++ .../BedarfsausweisWohnenModule.svelte | 22 +- .../Dashboard/DashboardPDFViewerModule.svelte | 2 +- src/modules/KundendatenModule.svelte | 12 +- src/modules/KundendatenModule_org.svelte | 4 +- .../VerbrauchsausweisGewerbeModule.svelte | 34 +-- .../AusweisWeiter.svelte | 42 +-- .../ORG_VerbrauchsausweisWohnenModule.svelte | 110 ++++---- .../VerbrauchsausweisWohnenModule.svelte | 77 +++--- .../VerbrauchsausweisWohnenModule_V01.svelte | 96 +++---- src/pages/api/aufnahme/{[id].ts => [uid].ts} | 36 ++- src/pages/api/aufnahme/index.ts | 59 ++++ src/pages/api/auth/access-token.ts | 2 +- src/pages/api/klimafaktoren.ts | 77 ++++++ src/pages/api/objekt/[uid]/bilder.ts | 122 +++++++++ .../api/objekt/{[id].ts => [uid]/index.ts} | 44 ++- src/pages/api/objekt/index.ts | 28 +- src/pages/api/postleitzahlen.ts | 49 ++++ src/pages/api/ticket.ts | 73 +++++ .../api/verbrauchsausweis-wohnen/[id].ts | 71 ----- .../api/verbrauchsausweis-wohnen/[uid].ts | 117 ++++++++ .../api/verbrauchsausweis-wohnen/index.ts | 111 ++++++-- .../bedarfsausweis-wohngebaeude/index.astro | 2 +- .../verbrauchsausweis-gewerbe/index.astro | 2 +- .../index.astro | 47 +++- .../energieverbrauch.test.ts | 2 +- 74 files changed, 1715 insertions(+), 818 deletions(-) delete mode 100644 src/lib/APIResponse.ts create mode 100644 src/lib/validators/index.ts rename src/pages/api/aufnahme/{[id].ts => [uid].ts} (59%) create mode 100644 src/pages/api/klimafaktoren.ts create mode 100644 src/pages/api/objekt/[uid]/bilder.ts rename src/pages/api/objekt/{[id].ts => [uid]/index.ts} (55%) create mode 100644 src/pages/api/postleitzahlen.ts create mode 100644 src/pages/api/ticket.ts delete mode 100644 src/pages/api/verbrauchsausweis-wohnen/[id].ts create mode 100644 src/pages/api/verbrauchsausweis-wohnen/[uid].ts diff --git a/openapi.json b/openapi.json index 037f9db3..872d426b 100644 --- a/openapi.json +++ b/openapi.json @@ -1 +1 @@ -{"openapi":"3.0.3","info":{"title":"Title","version":"1.0.0","description":""},"paths":{"[id]":{"patch":{"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{}}}}}}},"index":{"post":{"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{}}}}}}}}} \ No newline at end of file +{"openapi":"3.0.3","info":{"title":"Title","version":"1.0.0","description":""},"paths":{"postleitzahlen":{"get":{"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"plz":{"type":"string","minLength":1,"maxLength":5},"limit":{"type":"integer","maximum":50,"minimum":1,"default":10}},"required":["plz"],"additionalProperties":false}}}},"parameters":[],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"plz":{"type":"string","minLength":4,"maxLength":5},"stadt":{"type":"string"},"bundesland":{"type":"string"},"landkreis":{"type":"string"},"lat":{"type":"number"},"lon":{"type":"number"}},"required":["plz","stadt","bundesland","landkreis","lat","lon"],"additionalProperties":false}}}}}}}},"index":{"post":{"parameters":[],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{}}}}}}},"[id]":{"patch":{"parameters":[],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{}}}}}}},"refresh-token":{"get":{"description":"Erstellt sowohl einen neuen Refresh Token als auch einen Access Token für den gegebenen Benutzer. Der Refresh Token kann später für die Erstellung neuer Access Token genutzt werden.","tags":["Benutzer"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email"},"passwort":{"type":"string","minLength":8,"maxLength":100}},"required":["email","passwort"],"additionalProperties":false}}}},"parameters":[],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object","properties":{"uid":{"type":"string","format":"uuid"},"accessToken":{"type":"string"},"refreshToken":{"type":"string"},"refreshTokenBase64":{"type":"string"},"accessTokenBase64":{"type":"string"},"exp":{"type":"number"}},"required":["uid","accessToken","refreshToken","refreshTokenBase64","accessTokenBase64","exp"],"additionalProperties":false}}}}}}},"self":{"get":{"description":"Gibt die Daten des momentan eingeloggten Benutzers zurück. Falls der Authorization Key invalid ist wird stattdessen null zurückgegeben.","tags":["Benutzer"],"requestBody":{"required":true,"content":{"application/json":{"schema":{}}}},"parameters":[],"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"anyOf":[{"type":"object","properties":{"uid":{"type":"string"},"name":{"type":"string","nullable":true},"vorname":{"type":"string","nullable":true},"email":{"type":"string"},"profilbild":{"type":"string","nullable":true},"plz":{"type":"string","nullable":true},"ort":{"type":"string","nullable":true},"adresse":{"type":"string","nullable":true},"telefon":{"type":"string","nullable":true},"anrede":{"type":"string","nullable":true},"rolle":{"type":"string","enum":["USER","ADMIN"]}},"required":["uid","email","rolle"],"additionalProperties":false},{"enum":["null"],"nullable":true}]}}}}}}}}} \ No newline at end of file diff --git a/package.json b/package.json index 2c133863..6c0d8c2a 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,8 @@ "@pdfme/ui": "^5.2.16", "@trpc/client": "^10.45.2", "@trpc/server": "^10.45.2", - "astro": "^4.16.18", + "astro": "^4.16.17", + "astro-typesafe-api": "link:astro-typesafe-api", "body-scroll-lock": "^4.0.0-beta.0", "buffer": "^6.0.3", "bun": "^1.1.45", @@ -38,6 +39,7 @@ "express": "^4.21.2", "flag-icons": "^6.15.0", "fontkit": "^2.0.4", + "is-base64": "^1.1.0", "js-cookie": "^3.0.5", "js-interpolate": "^1.3.2", "jsonwebtoken": "^9.0.2", @@ -63,7 +65,9 @@ "@types/body-scroll-lock": "^3.1.2", "@types/express": "^5.0.0", "@types/fontkit": "^2.0.7", + "@types/is-base64": "^1.1.3", "@types/js-cookie": "^3.0.6", + "@types/jsonwebtoken": "^9.0.7", "@types/uuid": "^9.0.8", "@typescript-eslint/eslint-plugin": "^5.62.0", "@typescript-eslint/parser": "^5.62.0", @@ -79,5 +83,8 @@ "postcss-nesting": "^13.0.1", "prettier": "^2.8.8", "typescript": "^4.9.5" + }, + "overrides": { + "zod": "^3.24.1" } } diff --git a/src/astro-typesafe-api-caller.ts b/src/astro-typesafe-api-caller.ts index fdea9c81..8c932255 100644 --- a/src/astro-typesafe-api-caller.ts +++ b/src/astro-typesafe-api-caller.ts @@ -1,16 +1,19 @@ import { createCallerFactory } from "astro-typesafe-api/server"; -import { type AstroGlobal } from "astro"; export const createCaller = createCallerFactory({ - "aufnahme/[id]": await import("../src/pages/api/aufnahme/[id].ts"), + "klimafaktoren": await import("../src/pages/api/klimafaktoren.ts"), + "postleitzahlen": await import("../src/pages/api/postleitzahlen.ts"), + "ticket": await import("../src/pages/api/ticket.ts"), + "bedarfsausweis-wohnen": await import("../src/pages/api/bedarfsausweis-wohnen/index.ts"), + "aufnahme/[uid]": await import("../src/pages/api/aufnahme/[uid].ts"), "aufnahme": await import("../src/pages/api/aufnahme/index.ts"), "auth/access-token": await import("../src/pages/api/auth/access-token.ts"), "auth/refresh-token": await import("../src/pages/api/auth/refresh-token.ts"), - "user/self": await import("../src/pages/api/user/self.ts"), - "bedarfsausweis-wohnen": await import("../src/pages/api/bedarfsausweis-wohnen/index.ts"), - "objekt/[id]": await import("../src/pages/api/objekt/[id].ts"), "objekt": await import("../src/pages/api/objekt/index.ts"), + "user/self": await import("../src/pages/api/user/self.ts"), "verbrauchsausweis-gewerbe": await import("../src/pages/api/verbrauchsausweis-gewerbe/index.ts"), - "verbrauchsausweis-wohnen/[id]": await import("../src/pages/api/verbrauchsausweis-wohnen/[id].ts"), + "verbrauchsausweis-wohnen/[uid]": await import("../src/pages/api/verbrauchsausweis-wohnen/[uid].ts"), "verbrauchsausweis-wohnen": await import("../src/pages/api/verbrauchsausweis-wohnen/index.ts"), + "objekt/[uid]/bilder": await import("../src/pages/api/objekt/[uid]/bilder.ts"), + "objekt/[uid]": await import("../src/pages/api/objekt/[uid]/index.ts"), }) \ No newline at end of file diff --git a/src/client/lib/validateAccessToken.ts b/src/client/lib/validateAccessToken.ts index 1e4f334d..02f0999d 100644 --- a/src/client/lib/validateAccessToken.ts +++ b/src/client/lib/validateAccessToken.ts @@ -1,7 +1,9 @@ import Cookies from "js-cookie"; import { API_ACCESS_TOKEN_COOKIE_NAME, API_REFRESH_TOKEN_COOKIE_NAME, API_UID_COOKIE_NAME } from "#lib/constants.js"; -import { client } from "../../trpc.js"; import moment from "moment"; +import jwt from "jsonwebtoken" +import { TokenData, TokenType } from "#lib/auth/token.js"; +import { api } from "astro-typesafe-api/client"; export async function validateAccessTokenClient() { @@ -9,10 +11,12 @@ export async function validateAccessTokenClient() { const refreshToken = Cookies.get(API_REFRESH_TOKEN_COOKIE_NAME); if (accessToken) { - const { valid } = await client.v1.benutzer.validateAccessToken.query({accessToken}) + const { exp, typ, uid } = jwt.decode(accessToken, { + json: true + }) as TokenData - if (valid) { - return valid; + if (exp > Date.now() && typ === TokenType.Access) { + return true; } else { Cookies.remove(API_ACCESS_TOKEN_COOKIE_NAME); } @@ -33,7 +37,7 @@ export async function validateAccessTokenClient() { // Wenn das klappt, dann haben wir auch einen neuen Access Token. // Wenn das nicht klappt, dann müssen wir uns neu anmelden. try { - const { accessToken: newAccessToken, accessTokenExpiry, refreshToken: newRefreshToken, refreshTokenExpiry } = await client.v1.benutzer.getAccessToken.query({ + const { accessToken: newAccessToken, accessTokenExpiry, refreshToken: newRefreshToken, refreshTokenExpiry } = await api.auth["access-token"].GET.fetch({ refreshToken }) diff --git a/src/client/lib/verbrauchsausweisWohnenSpeichern.ts b/src/client/lib/verbrauchsausweisWohnenSpeichern.ts index dcb1177a..ee7708bc 100644 --- a/src/client/lib/verbrauchsausweisWohnenSpeichern.ts +++ b/src/client/lib/verbrauchsausweisWohnenSpeichern.ts @@ -1,86 +1,116 @@ -import { - BenutzerClient, - GebaeudeAufnahmeClient, - GebaeudeClient, - UploadedGebaeudeBild, - VerbrauchsausweisWohnenClient, -} from "#components/Ausweis/types.js"; + +import { api } from "astro-typesafe-api/client" import { exclude } from "#lib/exclude.js"; -import { client } from "src/trpc.js"; import { bilderHochladen } from "./bilderHochladen.js"; -import { addNotification } from "@ibcornelsen/ui"; +import { prisma } from "@ibcornelsen/database/server"; +import Cookies from "js-cookie"; +import { API_ACCESS_TOKEN_COOKIE_NAME } from "#lib/constants.js"; +import { AufnahmeClient, BenutzerClient, ObjektClient, UploadedGebaeudeBild, VerbrauchsausweisWohnenClient, } from "#components/Ausweis/types.js"; +// import { addNotification } from "@ibcornelsen/ui"; export async function verbrauchsausweisWohnenSpeichern( ausweis: VerbrauchsausweisWohnenClient, - gebaeude: GebaeudeClient, - gebaeude_aufnahme_allgemein: GebaeudeAufnahmeClient, + objekt: ObjektClient, + aufnahme: AufnahmeClient, images: (UploadedGebaeudeBild & { base64?: string })[], user: BenutzerClient ) { - if (ausweis.uid) { - // Anscheinend wurde der Ausweis bereits erstellt und hat eine UID. - // Jetzt müssen wir ihn nun nur noch abspeichern. - try { - await client.v1.objekt.speichern.mutate() + if (objekt.uid) { + await api.objekt._uid.PATCH.fetch({ + ...exclude(objekt, ["uid"]) + }, { + params: { + uid: objekt.uid + }, + headers: { + "Authorization": `Bearer ${Cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)}` + } + }) +} else { + const { uid } = await api.objekt.PUT.fetch({ + ...exclude(objekt, ["uid"]) + }, { + headers: { + "Authorization": `Bearer ${Cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)}` + } + }) - await client.v1.verbrauchsausweisWohnen[2016].speichern.mutate({ - ...ausweis, - gebaeude_aufnahme_allgemein: { - ...exclude( - gebaeude_aufnahme_allgemein, - ["erstellungsdatum", "events", "ausstellungsdatum", "rechnungen"] - ), - gebaeude_stammdaten: { - ...exclude(gebaeude, [ - "gebaeude_bilder", - ]), - }, - }, - }); - - images = await bilderHochladen(images, gebaeude.uid); - - return { uid: ausweis.uid, gebaeude_uid: gebaeude.uid, gebaeude_aufnahme_uid: gebaeude_aufnahme_allgemein.uid }; - } catch (e) { - } - } else { - // Wir speichern den Ausweis ab und leiten auf die "ausweis-gespeichert" Seite weiter. - try { - const response = - await client.v1.verbrauchsausweisWohnen[2016].erstellen.mutate({ - ...ausweis, - gebaeude_aufnahme_allgemein: { - ...gebaeude_aufnahme_allgemein, - gebaeude_stammdaten: { - ...gebaeude, - }, - }, - }); - - images = await bilderHochladen(images, response.gebaeude_uid); - - return response; - } catch (e: any) { - await client.v1.tickets.erstellen.mutate({ - titel: "Ausweis konnte nicht gespeichert werden", - beschreibung: e.stack, - email: user.email ?? "", - metadata: JSON.stringify({ - ausweis, - }), - }); - } + objekt.uid = uid; } - addNotification({ - dismissable: false, - message: - "Ausweis konnte nicht gespeichert werden, bitte versuchen sie es erneut.", - subtext: - "Sollte das Problem weiterhin bestehen, kontaktieren sie bitte den Support.", - timeout: 6000, - type: "error", - }); + + + if (aufnahme.uid) { + await api.aufnahme._uid.PATCH.fetch({ + ...exclude(aufnahme, ["uid"]) + }, { + params: { + uid: aufnahme.uid + }, + headers: { + "Authorization": `Bearer ${Cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)}` + } + }) + } else { + const { uid } = await api.aufnahme.PUT.fetch({ + aufnahme, + uid_objekt: objekt.uid + }, { + headers: { + "Authorization": `Bearer ${Cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)}` + } + }) + + aufnahme.uid = uid + } + + if (ausweis.uid) { + await api["verbrauchsausweis-wohnen"]._uid.PATCH.fetch({ + ...exclude(ausweis, ["uid"]) + }, { + params: { + uid: ausweis.uid + }, + headers: { + "Authorization": `Bearer ${Cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)}` + } + }) + } else { + await api["verbrauchsausweis-wohnen"].PUT.fetch({ + ausweis, + uid_aufnahme: aufnahme.uid + }, { + headers: { + "Authorization": `Bearer ${Cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)}` + } + }) + } + + return { + uid_ausweis: ausweis.uid, + uid_aufnahme: aufnahme.uid, + uid_objekt: objekt.uid + } + + + // await client.v1.tickets.erstellen.mutate({ + // titel: "Ausweis konnte nicht gespeichert werden", + // beschreibung: e.stack, + // email: user.email ?? "", + // metadata: JSON.stringify({ + // ausweis, + // }), + // }); + + // addNotification({ + // dismissable: false, + // message: + // "Ausweis konnte nicht gespeichert werden, bitte versuchen sie es erneut.", + // subtext: + // "Sollte das Problem weiterhin bestehen, kontaktieren sie bitte den Support.", + // timeout: 6000, + // type: "error", + // }); return null; } diff --git a/src/components/Ausweis/AusweisPreviewContainer.svelte b/src/components/Ausweis/AusweisPreviewContainer.svelte index 48e07047..4aecf14f 100644 --- a/src/components/Ausweis/AusweisPreviewContainer.svelte +++ b/src/components/Ausweis/AusweisPreviewContainer.svelte @@ -1,8 +1,8 @@
- {#if Energieausweis=="Verbrauchsausweis Wohngebäude"} + {#if ausweisart=="VerbrauchsausweisWohnen"} @@ -86,7 +83,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8 - {:else if Energieausweis=="Verbrauchsausweis Gewerbe"} + {:else if ausweisart=="VerbrauchsausweisGewerbe"} @@ -162,7 +159,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8 autocomplete="off" data-msg="Pflichtfeld" maxlength="3" - bind:value={gebaeude_aufnahme_allgemein.einheiten} + bind:value={aufnahme.einheiten} />
@@ -181,7 +178,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8 @@ -136,7 +136,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8 name="keller" data-test="keller" required - bind:value={gebaeude_aufnahme_allgemein.keller} + bind:value={aufnahme.keller} > @@ -168,7 +168,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8 autocomplete="off" data-rule-minlength="2" data-msg-minlength="min. 2 Zeichen" - bind:value={gebaeude_aufnahme_allgemein.nutzflaeche} + bind:value={aufnahme.nutzflaeche} />
diff --git a/src/components/Ausweis/LueftungundLeerstand.svelte b/src/components/Ausweis/LueftungundLeerstand.svelte index fd487809..cea31ba0 100644 --- a/src/components/Ausweis/LueftungundLeerstand.svelte +++ b/src/components/Ausweis/LueftungundLeerstand.svelte @@ -3,7 +3,7 @@ import HelpLabel from "#components/labels/HelpLabel.svelte"; import Inputlabel from "#components/labels/InputLabel.svelte"; - export let gebaeude_aufnahme_allgemein: GebaeudeAufnahmeClient; + export let aufnahme: GebaeudeAufnahmeClient; @@ -25,7 +25,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8 @@ -499,7 +499,7 @@