From ee5133b3f8c8c0b5337e5f899088c457a8aff88c Mon Sep 17 00:00:00 2001 From: Moritz Utcke Date: Mon, 8 Jan 2024 14:38:28 +0700 Subject: [PATCH] API Verbessert - Verbrauchsausweis Funktioniert --- package.json | 2 +- src/components/Ausweis/Verbrauch.svelte | 4 +- src/components/AusweisCard.svelte | 297 ------------------ src/components/Header.astro | 6 +- .../VerbrauchsausweisWohnen_2016.ts | 4 +- .../VerbrauchsausweisWohnen_2023.ts | 2 +- src/lib/JsonWebToken.ts | 6 +- src/lib/Klimafaktoren.ts | 4 +- src/lib/User/index.ts | 29 -- .../xmlVerbrauchsausweisWohnen_2016.ts | 23 +- src/lib/server/Klimafaktoren.ts | 5 - src/lib/server/fetch.ts | 21 -- src/{pages/api => lib}/trpc/context.ts | 22 +- src/lib/trpc/middlewares/adminProcedure.ts | 0 src/lib/trpc/middlewares/loggedProcedure.ts | 26 ++ src/lib/trpc/middlewares/privateProcedure.ts | 44 +++ src/lib/trpc/middlewares/publicProcedure.ts | 3 + .../trpc/procedures/v1/benutzer/entfernen.ts | 0 .../trpc/procedures/v1/benutzer/erstellen.ts | 64 ++++ .../procedures/v1/benutzer/fromPrivateId.ts | 31 ++ .../procedures/v1/benutzer/fromPublicId.ts | 31 ++ .../procedures/v1/benutzer/tokenErneuern.ts | 50 +++ .../procedures/v1/benutzer/tokenValidieren.ts | 49 +++ src/lib/trpc/procedures/v1/index.ts | 45 +++ .../trpc/procedures/v1/klimafaktoren.ts | 4 +- .../2016/erstellen.ts | 38 ++- .../2016/zusatzdaten-erfassung.xml.ts | 0 .../VerbrauchsausweisWohnenModule.svelte | 10 +- src/modules/LoginModule.svelte | 41 +-- src/modules/RegisterModule.svelte | 46 ++- src/pages/api/[trpc].ts | 42 +++ src/pages/api/login.ts | 34 -- src/pages/api/token.ts | 34 -- src/pages/api/trpc/[trpc].ts | 31 -- src/pages/api/trpc/procedures/v1/index.ts | 42 --- src/pages/api/user.ts | 50 --- src/pages/api/zip.ts | 47 --- src/pages/user/index.astro | 40 ++- src/trpc.ts | 6 +- test.ts | 2 +- 40 files changed, 535 insertions(+), 700 deletions(-) delete mode 100644 src/components/AusweisCard.svelte delete mode 100644 src/lib/server/Klimafaktoren.ts delete mode 100644 src/lib/server/fetch.ts rename src/{pages/api => lib}/trpc/context.ts (52%) create mode 100644 src/lib/trpc/middlewares/adminProcedure.ts create mode 100644 src/lib/trpc/middlewares/loggedProcedure.ts create mode 100644 src/lib/trpc/middlewares/privateProcedure.ts create mode 100644 src/lib/trpc/middlewares/publicProcedure.ts create mode 100644 src/lib/trpc/procedures/v1/benutzer/entfernen.ts create mode 100644 src/lib/trpc/procedures/v1/benutzer/erstellen.ts create mode 100644 src/lib/trpc/procedures/v1/benutzer/fromPrivateId.ts create mode 100644 src/lib/trpc/procedures/v1/benutzer/fromPublicId.ts create mode 100644 src/lib/trpc/procedures/v1/benutzer/tokenErneuern.ts create mode 100644 src/lib/trpc/procedures/v1/benutzer/tokenValidieren.ts create mode 100644 src/lib/trpc/procedures/v1/index.ts rename src/{pages/api => lib}/trpc/procedures/v1/klimafaktoren.ts (92%) rename src/{pages/api => lib}/trpc/procedures/v1/verbrauchsausweis-wohnen/2016/erstellen.ts (86%) rename src/{pages/api => lib}/trpc/procedures/v1/verbrauchsausweis-wohnen/2016/zusatzdaten-erfassung.xml.ts (100%) create mode 100644 src/pages/api/[trpc].ts delete mode 100644 src/pages/api/login.ts delete mode 100644 src/pages/api/token.ts delete mode 100644 src/pages/api/trpc/[trpc].ts delete mode 100644 src/pages/api/trpc/procedures/v1/index.ts delete mode 100644 src/pages/api/user.ts delete mode 100644 src/pages/api/zip.ts diff --git a/package.json b/package.json index fe53573f..ad5922c0 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "trpc-openapi": "^1.2.0", "uuid": "^9.0.0", "vite-tsconfig-paths": "^4.2.0", - "zod": "^3.21.4" + "zod": "^3.22.4" }, "devDependencies": { "@types/js-cookie": "^3.0.6", diff --git a/src/components/Ausweis/Verbrauch.svelte b/src/components/Ausweis/Verbrauch.svelte index 3f9389f1..8ee7322a 100644 --- a/src/components/Ausweis/Verbrauch.svelte +++ b/src/components/Ausweis/Verbrauch.svelte @@ -107,7 +107,7 @@
- import HorizontalDots from "#components/Icons/HorizontalDots.svelte"; - import type { Verbrauchsausweis } from "#lib/Ausweis/Verbrauchsausweis"; - import moment from "moment"; - import Cross from "#components/Icons/Cross.svelte"; - import { Dachgeschoss } from "#lib/Ausweis/types"; - - interface Service { - name: string; - url: string; - } - - export let ausweis: Verbrauchsausweis; - export let services: Service[]; - export let hidden: boolean; - - export let i: number; - - let left: string = ""; - $: { - if (i == 0 || i % 3 == 0) { - left = "0"; - } else if (i % 2 == 0) { - left = "-50%"; - } else { - left = "-100%"; - } - } - - $: energieverbrauchDaten = [ - moment(ausweis.energieverbrauch_zeitraum), - moment(ausweis.energieverbrauch_zeitraum).add("1", "year"), - moment(ausweis.energieverbrauch_zeitraum).add("2", "years"), - moment(ausweis.energieverbrauch_zeitraum).add("3", "years"), - ]; - - let showDropdown: boolean = false; - - -
-
-

- {ausweis.objekt_strasse}, {ausweis.objekt_plz} - {ausweis.objekt_ort} -

- - - -
- -
- Gebäudeansicht -
-
-

- Datenprüfung Verbrauchsausweis - ID {ausweis.id} - {ausweis.objekt_strasse}, - {ausweis.objekt_plz} - {ausweis.objekt_ort} -

- (hidden = true)} - className="p-1 hover:bg-gray-100 cursor-pointer w-[25px] h-[25px] rounded-lg" - /> -
-
Abschnitt A,B,C,D und E
-
-
- - - - - - - - - - - - - - - - - - - - - -
AL:{ausweis.ausstellgrund}
BH:{ausweis.baujahr_anlage}
BG:{ausweis.baujahr_gebaeude}
AW:{ausweis.anzahl_einheiten}
ST:{ausweis.objekt_saniert}
-
-
- - - - - - - - - - - - - - - - - - - - - -
WF:{ausweis.wohnflaeche}
F:{ausweis.baujahr_anlage}
AN:{ausweis.baujahr_gebaeude}
KB:{ausweis.keller_beheizt}
DB:{ausweis.dachgeschoss == Dachgeschoss.BEHEIZT ? "Ja" : "Nein"}
-
-
- - - - - - - - - - - - - - - - - - - - -
WWE:{ausweis.warmwasser_enthalten}
WWA:{ausweis.anteil_warmwasser_1}
WWAZH:{ausweis.anteil_warmwasser_2}
AEVS: -
SSWW:{ausweis.dachgeschoss}
-
-
- - - - - - - - - - - - - - - - - - - -
GT:{ausweis.objekt_gebaeudeteil}
GTL: -
L:{ausweis.lueftungskonzept}
AK: -
LS:{ausweis.leerstand}
-
-
-
- - - - - - - - - - - - - - - - -
{energieverbrauchDaten[0].format("MMMM YYYY")}
{energieverbrauchDaten[0].format("MM.YYYY")} - {energieverbrauchDaten[1].format( - "MM.YYYY" - )}
{energieverbrauchDaten[1].format("MM.YYYY")} - {energieverbrauchDaten[2].format( - "MM.YYYY" - )}
{energieverbrauchDaten[2].format("MM.YYYY")} - {energieverbrauchDaten[3].format( - "MM.YYYY" - )}
Faktoren Hi / PF / COE
- - - - - - - - - - - - - - - - -
Primäre Heizquelle (1)
{ausweis.energieverbrauch_1_heizquelle_1} - {ausweis.energietraeger_einheit_heizquelle_1} - {ausweis.energietraeger_1}
{ausweis.energieverbrauch_2_heizquelle_1} - {ausweis.energietraeger_einheit_heizquelle_1} - {ausweis.energietraeger_1}
{ausweis.energieverbrauch_3_heizquelle_1} - {ausweis.energietraeger_einheit_heizquelle_1} - {ausweis.energietraeger_1}
10 / 1.1 / 2.5
- - - - - - - - - - - - - - - - -
Sekundäre Heizquelle (2)
{ausweis.energieverbrauch_1_heizquelle_2} - {ausweis.energietraeger_einheit_heizquelle_2} - {ausweis.energietraeger_2}
{ausweis.energieverbrauch_2_heizquelle_2} - {ausweis.energietraeger_einheit_heizquelle_2} - {ausweis.energietraeger_2}
{ausweis.energieverbrauch_3_heizquelle_2} - {ausweis.energietraeger_einheit_heizquelle_2} - {ausweis.energietraeger_2}
10 / 1.1 / 2.5
- - - - - - - - - - - - - - - - -
Klimafaktoren
1.12
1.15
1.12
-
-
-
-
diff --git a/src/components/Header.astro b/src/components/Header.astro index df2f0129..021d4faf 100644 --- a/src/components/Header.astro +++ b/src/components/Header.astro @@ -56,17 +56,17 @@ const loggedIn = isLoggedIn(Astro); >Energieausweis erstellen {t("header.kontakt")}Kontakt AGB { loggedIn ? ( - {t("header.profil")} + Profil ) : ( - {t("header.login")} + Login ) } diff --git a/src/lib/Berechnungen/VerbrauchsausweisWohnen/VerbrauchsausweisWohnen_2016.ts b/src/lib/Berechnungen/VerbrauchsausweisWohnen/VerbrauchsausweisWohnen_2016.ts index 28f814ce..818f0e43 100644 --- a/src/lib/Berechnungen/VerbrauchsausweisWohnen/VerbrauchsausweisWohnen_2016.ts +++ b/src/lib/Berechnungen/VerbrauchsausweisWohnen/VerbrauchsausweisWohnen_2016.ts @@ -4,7 +4,7 @@ import { VerbrauchsausweisWohnen, } from "@ibcornelsen/database"; import moment from "moment"; -import trpc from "src/trpc"; +import { client } from "src/trpc"; export function energetischeNutzflaecheVerbrauchsausweisWohnen_2016( ausweis: VerbrauchsausweisWohnen & { @@ -28,7 +28,7 @@ export async function endEnergieVerbrauchVerbrauchsausweis_2016( return null } - const klimafaktoren = await trpc.klimafaktoren.query({ + const klimafaktoren = await client.v1.klimafaktoren.query({ plz: ausweis.gebaeude_stammdaten.plz, genauigkeit: "years", startdatum: ausweis.startdatum, diff --git a/src/lib/Berechnungen/VerbrauchsausweisWohnen/VerbrauchsausweisWohnen_2023.ts b/src/lib/Berechnungen/VerbrauchsausweisWohnen/VerbrauchsausweisWohnen_2023.ts index 88648bc4..47c71552 100644 --- a/src/lib/Berechnungen/VerbrauchsausweisWohnen/VerbrauchsausweisWohnen_2023.ts +++ b/src/lib/Berechnungen/VerbrauchsausweisWohnen/VerbrauchsausweisWohnen_2023.ts @@ -1,4 +1,4 @@ -import { getKlimafaktoren } from "#lib/Klimafaktoren"; +import { getKlimafaktoren } from "#lib/getKlimafaktoren"; import { getHeizwertfaktor } from "#lib/server/Heizwertfaktor"; import { GebaeudeStammdaten, VerbrauchsausweisWohnen } from "@ibcornelsen/database"; diff --git a/src/lib/JsonWebToken.ts b/src/lib/JsonWebToken.ts index 9df0eacc..9dc3a7b5 100644 --- a/src/lib/JsonWebToken.ts +++ b/src/lib/JsonWebToken.ts @@ -1,10 +1,12 @@ import jwt from "jwt-simple"; -export function encodeToken(data: Record) { +type TokenData = { uid: string, exp: number } + +export function encodeToken(data: TokenData) { const token = jwt.encode(data, "yIvbgS$k7Bfc+mpV%TWDZAhje9#uJad4", "HS256"); return token; } -export function decodeToken(token: string): Partial { +export function decodeToken(token: string): Partial { return jwt.decode(token, "yIvbgS$k7Bfc+mpV%TWDZAhje9#uJad4"); } \ No newline at end of file diff --git a/src/lib/Klimafaktoren.ts b/src/lib/Klimafaktoren.ts index 73ea5d94..ebf05065 100644 --- a/src/lib/Klimafaktoren.ts +++ b/src/lib/Klimafaktoren.ts @@ -1,13 +1,13 @@ import moment from "moment"; import { memoize } from "./Memoization"; -import trpc from "src/trpc"; +import { client } from "src/trpc"; export const getKlimafaktoren = memoize(async (date: Date, plz: string) => { if (!plz || !date) { return null; } - const response = await trpc.klimafaktoren.query({ + const response = await client.v1.klimafaktoren.query({ plz, genauigkeit: "years", startdatum: date, diff --git a/src/lib/User/index.ts b/src/lib/User/index.ts index f8d4ec46..e13f0768 100644 --- a/src/lib/User/index.ts +++ b/src/lib/User/index.ts @@ -69,33 +69,4 @@ export class User { return user; } - - public static async create(user: UserType): Promise<{uid: string, id: number} | null> { - if (!user || UserRegisterValidator.safeParse(user).success == false) { - return null; - } - - const uid = uuid(); - const hashedPassword = hashPassword(user.passwort); - - const result = await prisma.benutzer.create({ - data: { - email: user.email, - passwort: hashedPassword, - uid: uid - }, - select: { - id: true - } - }) - - if (!result) { - return null; - } - - return { - uid, - id: result.id - } - } } \ No newline at end of file diff --git a/src/lib/XML/VerbrauchsausweisWohnen/xmlVerbrauchsausweisWohnen_2016.ts b/src/lib/XML/VerbrauchsausweisWohnen/xmlVerbrauchsausweisWohnen_2016.ts index 1ff5bd5f..1269dd65 100644 --- a/src/lib/XML/VerbrauchsausweisWohnen/xmlVerbrauchsausweisWohnen_2016.ts +++ b/src/lib/XML/VerbrauchsausweisWohnen/xmlVerbrauchsausweisWohnen_2016.ts @@ -2,7 +2,7 @@ import { endEnergieVerbrauchVerbrauchsausweis_2016, energetischeNutzflaecheVerbrauchsausweisWohnen_2016, } from "#lib/Berechnungen/VerbrauchsausweisWohnen/VerbrauchsausweisWohnen_2016"; -import { getKlimafaktoren } from "#lib/Klimafaktoren"; +import { getKlimafaktoren } from "#lib/getKlimafaktoren"; import { getHeizwertfaktor } from "#lib/server/Heizwertfaktor"; import { GebaeudeStammdaten, @@ -28,6 +28,11 @@ export async function xmlVerbrauchsausweisWohnen_2016( let postleitzahl = gebaeude.plz?.substring(0, 3) + "XX"; const result = await endEnergieVerbrauchVerbrauchsausweis_2016(ausweis); + + if (!result) { + throw new Error("Verbrauchsausweis konnte nicht zur Berechnung verarbeitet werden."); + } + const berechnungen = new AusweisBerechnungen2016( ausweis, ausweis.gebaeude_stammdaten, @@ -79,12 +84,12 @@ export async function xmlVerbrauchsausweisWohnen_2016( : "true"; let heizwertfaktor_1 = getHeizwertfaktor( - ausweis.brennstoff_1, - ausweis.einheit_1 + ausweis.brennstoff_1 as string, + ausweis.einheit_1 as string ); let heizwertfaktor_2 = getHeizwertfaktor( - ausweis.brennstoff_2, - ausweis.einheit_2 + ausweis.brennstoff_2 as string, + ausweis.einheit_2 as string ); let warmWasserErmittlung = @@ -158,12 +163,12 @@ export async function xmlVerbrauchsausweisWohnen_2016( ); let energieTraeger = berechnungen.getEnergietraegerBezeichnung( - ausweis.brennstoff_1, - ausweis.einheit_1 + ausweis.brennstoff_1 as string, + ausweis.einheit_1 as string ); let energieTraeger_2 = berechnungen.getEnergietraegerBezeichnung( - ausweis.brennstoff_2, - ausweis.einheit_2 + ausweis.brennstoff_2 as string, + ausweis.einheit_2 as string ); let warmwasserZuschlag = Math.round(result.energieVerbrauchWarmwasser_1); diff --git a/src/lib/server/Klimafaktoren.ts b/src/lib/server/Klimafaktoren.ts deleted file mode 100644 index f5d09407..00000000 --- a/src/lib/server/Klimafaktoren.ts +++ /dev/null @@ -1,5 +0,0 @@ -export async function getKlimafaktorenServer(date: Date, zip: string, accuracy: "months" | "years" = "months"): Promise { - - - return null; -} \ No newline at end of file diff --git a/src/lib/server/fetch.ts b/src/lib/server/fetch.ts deleted file mode 100644 index aa1f4fe2..00000000 --- a/src/lib/server/fetch.ts +++ /dev/null @@ -1,21 +0,0 @@ - -/** - * Dies ist die Server-Side Implementierung von fetch, die Daten werden direkt vom Server abgerufen. - * Dadurch können unnötige Requests vermieden werden. - * @date 9/20/2023 - 11:33:30 AM - * - * @export - * @async - * @param {string} resourceUri - * @param {?RequestInit} [options] - * @returns {Promise} - */ -export async function fetch(resourceUri: string, options?: RequestInit): Promise { - const response = await fetch(`http://localhost:3000/api/${resourceUri}`, options); - - if (!response.ok) { - throw new Error("Fehler beim Abrufen der Daten."); - } - - return response.json(); -} \ No newline at end of file diff --git a/src/pages/api/trpc/context.ts b/src/lib/trpc/context.ts similarity index 52% rename from src/pages/api/trpc/context.ts rename to src/lib/trpc/context.ts index a3fce6bf..92f1f3e0 100644 --- a/src/pages/api/trpc/context.ts +++ b/src/lib/trpc/context.ts @@ -1,10 +1,15 @@ -import { TRPCError, initTRPC } from "@trpc/server"; +import { initTRPC } from "@trpc/server"; import { ZodError } from "zod"; import { OpenApiMeta } from "trpc-openapi"; -type Context = { uid?: string }; +type Context = { + authorization: string | null; + ip: string; + req: Request; +} export const t = initTRPC.context().meta().create({ + errorFormatter(opts) { const { shape, error } = opts; return { @@ -18,17 +23,4 @@ export const t = initTRPC.context().meta().create({ }, }; } -}); - -export const publicProcedure = t.procedure - -export const privateProcedure = t.procedure.use((opts) => { - if (!opts.ctx.uid) { - throw new TRPCError({ - code: 'FORBIDDEN', - message: "Diese Ressource benötigt eine UID, welche im 'Authorization' Header gegeben sein muss.", - }); - } - - return opts.next(); }); \ No newline at end of file diff --git a/src/lib/trpc/middlewares/adminProcedure.ts b/src/lib/trpc/middlewares/adminProcedure.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/lib/trpc/middlewares/loggedProcedure.ts b/src/lib/trpc/middlewares/loggedProcedure.ts new file mode 100644 index 00000000..0ac228fe --- /dev/null +++ b/src/lib/trpc/middlewares/loggedProcedure.ts @@ -0,0 +1,26 @@ +import { prisma } from "@ibcornelsen/database"; +import { privateProcedure } from "./privateProcedure"; + +// NOTE: Muss noch mit der Datenbank eingebunden werden. +export const loggedProcedure = privateProcedure.use(async (opts) => { + const start = Date.now(); + + const result = await opts.next(); + + const durationMs = Date.now() - start; + + await prisma.apiRequests.create({ + data: { + ip: opts.ctx.ip, + method: opts.type, + path: opts.path, + responseSize: JSON.stringify(result).length, + responseTime: durationMs, + userAgent: opts.ctx.req.headers["user-agent"] || "", + status: result.ok ? 200 : 500, + user_id: opts.ctx.user.id + } + }) + + return result; +}); diff --git a/src/lib/trpc/middlewares/privateProcedure.ts b/src/lib/trpc/middlewares/privateProcedure.ts new file mode 100644 index 00000000..c9839cdb --- /dev/null +++ b/src/lib/trpc/middlewares/privateProcedure.ts @@ -0,0 +1,44 @@ +import { TRPCError } from "@trpc/server"; +import { publicProcedure } from "./publicProcedure"; +import { decodeToken } from "#lib/JsonWebToken"; +import { User } from "#lib/User"; + +export const privateProcedure = publicProcedure.use(async (opts) => { + const { ctx } = opts; + + if (!ctx.authorization) { + throw new TRPCError({ code: "UNAUTHORIZED", message: "Für diese Action ist ein Bearer Token verpflichtend." }); + } + + const [authorizationType, value] = ctx.authorization.split(" "); + + if (authorizationType != "Bearer") { + throw new TRPCError({ code: "UNAUTHORIZED", message: "Für diese Action ist ein Bearer Token verpflichtend." }); + } + + const stringToken = Buffer.from(value, "base64").toString(); + try { + const token = decodeToken( + stringToken + ); + const uid = token.uid; + + if (!uid) { + throw new TRPCError({ code: "UNAUTHORIZED", message: "Der gegebene Token ist fehlerhaft." }); + } + + const user = await User.fromUID(uid); + + if (!user) { + throw new TRPCError({ code: "UNAUTHORIZED", message: "Der gegebene Token ist fehlerhaft." }); + } + + return opts.next({ + ctx: { + user, + }, + }); + } catch (e) { + throw new TRPCError({ code: "UNAUTHORIZED", message: "Der gegebene Token ist fehlerhaft." }); + } +}); diff --git a/src/lib/trpc/middlewares/publicProcedure.ts b/src/lib/trpc/middlewares/publicProcedure.ts new file mode 100644 index 00000000..d84c9310 --- /dev/null +++ b/src/lib/trpc/middlewares/publicProcedure.ts @@ -0,0 +1,3 @@ +import { t } from "#lib/trpc/context"; + +export const publicProcedure = t.procedure; \ No newline at end of file diff --git a/src/lib/trpc/procedures/v1/benutzer/entfernen.ts b/src/lib/trpc/procedures/v1/benutzer/entfernen.ts new file mode 100644 index 00000000..e69de29b diff --git a/src/lib/trpc/procedures/v1/benutzer/erstellen.ts b/src/lib/trpc/procedures/v1/benutzer/erstellen.ts new file mode 100644 index 00000000..5b18f904 --- /dev/null +++ b/src/lib/trpc/procedures/v1/benutzer/erstellen.ts @@ -0,0 +1,64 @@ +import { z } from "zod"; +import moment from "moment"; +import { prisma } from "@ibcornelsen/database"; +import { publicProcedure } from "#lib/trpc/middlewares/publicProcedure"; +import { encodeToken } from "#lib/JsonWebToken"; +import { hashPassword } from "#lib/Password"; +import { TRPCError } from "@trpc/server"; + +export const tRPC_V1_BenutzerErstellenProcedure = publicProcedure + .input( + z.object({ + email: z.string().email(), + passwort: z.string().min(8).max(100), + vorname: z.string().min(1).max(100), + name: z.string().min(1).max(100), + }) + ) + .output( + z.object({ + uid: z.string().uuid(), + token: z.string(), + exp: z.number(), + }) + ) + .query(async (opts) => { + const hashedPassword = hashPassword(opts.input.passwort); + + // Vielleicht existiert der Benutzer ja schon? + const existingUser = await prisma.benutzer.findUnique({ + where: { + email: opts.input.email + } + }) + + if (existingUser) { + throw new TRPCError({ code: "CONFLICT", message: "Der Benutzer existiert bereits." }); + } + + const user = await prisma.benutzer.create({ + data: { + email: opts.input.email, + passwort: hashedPassword, + vorname: opts.input.vorname, + name: opts.input.name + }, + select: { + id: true, + uid: true + } + }) + + if (!user) { + throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: "Der Benutzer konnte nicht erstellt werden." }); + } + + const expiry = moment().add(2, "days").unix(); + const token = encodeToken({ uid: user.uid, exp: expiry }) + + return { + uid: user.uid, + token: token, + exp: expiry + } + }); diff --git a/src/lib/trpc/procedures/v1/benutzer/fromPrivateId.ts b/src/lib/trpc/procedures/v1/benutzer/fromPrivateId.ts new file mode 100644 index 00000000..7d7af8c5 --- /dev/null +++ b/src/lib/trpc/procedures/v1/benutzer/fromPrivateId.ts @@ -0,0 +1,31 @@ +import { z } from "zod"; +import { BenutzerSchema, prisma } from "@ibcornelsen/database"; +import { TRPCError } from "@trpc/server"; +import { loggedProcedure } from "#lib/trpc/middlewares/loggedProcedure"; + +export const tRPC_V1_BenutzerFromPrivateIdProcedure = loggedProcedure + .input( + z.object({ + id: z.number(), + }) + ) + .output( + BenutzerSchema + ) + .query(async (opts) => { + if (opts.ctx.user.id !== opts.input.id) { + throw new TRPCError({ code: "UNAUTHORIZED", message: "Sie sind nicht dazu authorisiert die Daten dieses Benutzers einzusehen." }); + } + + const user = await prisma.benutzer.findUnique({ + where: { + id: opts.input.id, + }, + }); + + if (!user) { + throw new TRPCError({ code: "BAD_REQUEST", message: "Der gesuchte Benutzer existiert nicht." }); + } + + return user; + }); diff --git a/src/lib/trpc/procedures/v1/benutzer/fromPublicId.ts b/src/lib/trpc/procedures/v1/benutzer/fromPublicId.ts new file mode 100644 index 00000000..b7dde15e --- /dev/null +++ b/src/lib/trpc/procedures/v1/benutzer/fromPublicId.ts @@ -0,0 +1,31 @@ +import { z } from "zod"; +import { BenutzerSchema, prisma } from "@ibcornelsen/database"; +import { TRPCError } from "@trpc/server"; +import { loggedProcedure } from "#lib/trpc/middlewares/loggedProcedure"; + +export const tRPC_V1_BenutzerFromPublicIdProcedure = loggedProcedure + .input( + z.object({ + uid: z.string().uuid(), + }) + ) + .output( + BenutzerSchema + ) + .query(async (opts) => { + if (opts.ctx.user.uid !== opts.input.uid) { + throw new TRPCError({ code: "UNAUTHORIZED", message: "Sie sind nicht dazu authorisiert die Daten dieses Benutzers einzusehen." }); + } + + const user = await prisma.benutzer.findUnique({ + where: { + uid: opts.input.uid, + }, + }); + + if (!user) { + throw new TRPCError({ code: "BAD_REQUEST", message: "Der gesuchte Benutzer existiert nicht." }); + } + + return user; + }); diff --git a/src/lib/trpc/procedures/v1/benutzer/tokenErneuern.ts b/src/lib/trpc/procedures/v1/benutzer/tokenErneuern.ts new file mode 100644 index 00000000..51a3c01c --- /dev/null +++ b/src/lib/trpc/procedures/v1/benutzer/tokenErneuern.ts @@ -0,0 +1,50 @@ +import { z } from "zod"; +import moment from "moment"; +import { prisma } from "@ibcornelsen/database"; +import { publicProcedure } from "#lib/trpc/middlewares/publicProcedure"; +import { encodeToken } from "#lib/JsonWebToken"; +import { hashPassword } from "#lib/Password"; +import { TRPCError } from "@trpc/server"; + +export const tRPC_V1_BenutzerTokenErneuernProcedure = publicProcedure + .input( + z.object({ + email: z.string().email(), + passwort: z.string().min(8).max(100), + }) + ) + .output( + z.object({ + uid: z.string().uuid(), + token: z.string(), + exp: z.number(), + }) + ) + .query(async (opts) => { + const hashedPassword = hashPassword(opts.input.passwort); + + // Falls der Nutzer nicht existiert, wird eine Fehlermeldung zurückgegeben. + const user = await prisma.benutzer.findUnique({ + where: { + email: opts.input.email + } + }) + + if (!user) { + throw new TRPCError({ code: "BAD_REQUEST", message: "Der gesuchte Benutzer existiert nicht oder das Password ist falsch." }); + } + + // Falls das Passwort nicht stimmt, wird eine Fehlermeldung zurückgegeben. + if (user.passwort !== hashedPassword) { + throw new TRPCError({ code: "BAD_REQUEST", message: "Der gesuchte Benutzer existiert nicht oder das Password ist falsch." }); + } + + const expiry = moment().add(2, "days").unix(); + const token = encodeToken({ uid: user.uid, exp: expiry }) + + return { + uid: user.uid, + token: token, + exp: expiry + } + }); diff --git a/src/lib/trpc/procedures/v1/benutzer/tokenValidieren.ts b/src/lib/trpc/procedures/v1/benutzer/tokenValidieren.ts new file mode 100644 index 00000000..e8453e6f --- /dev/null +++ b/src/lib/trpc/procedures/v1/benutzer/tokenValidieren.ts @@ -0,0 +1,49 @@ +import { z } from "zod"; +import { prisma } from "@ibcornelsen/database"; +import { publicProcedure } from "#lib/trpc/middlewares/publicProcedure"; +import { decodeToken } from "#lib/JsonWebToken"; + +export const tRPC_V1_BenutzerTokenValidierenProcedure = publicProcedure + .input( + z.object({ + token: z.string(), + }) + ) + .output( + z.object({ + uid: z.string().uuid(), + valid: z.boolean(), + exp: z.number() + }) + ) + .query(async (opts) => { + const decodedToken = decodeToken(opts.input.token); + + if (!decodedToken || !decodedToken.uid || !decodedToken.exp) { + return { + uid: "", + valid: false, + exp: 0, + }; + } + + const user = await prisma.benutzer.findUnique({ + where: { + uid: decodedToken.uid, + }, + }); + + if (!user) { + return { + uid: "", + valid: false, + exp: 0, + }; + } + + return { + uid: decodedToken.uid, + valid: true, + exp: decodedToken.exp + }; + }); diff --git a/src/lib/trpc/procedures/v1/index.ts b/src/lib/trpc/procedures/v1/index.ts new file mode 100644 index 00000000..b8c161fb --- /dev/null +++ b/src/lib/trpc/procedures/v1/index.ts @@ -0,0 +1,45 @@ +import { t } from "#lib/trpc/context"; +import { tRPC_V1_KlimafaktorenProcedure } from "./klimafaktoren"; +import { VerbrauchsausweisWohnen2016Erstellen } from "./verbrauchsausweis-wohnen/2016/erstellen"; +import { tRPC_V1_BenutzerErstellenProcedure } from "./benutzer/erstellen"; +import { tRPC_V1_BenutzerTokenErneuernProcedure } from "./benutzer/tokenErneuern"; +import { tRPC_V1_BenutzerTokenValidierenProcedure } from "./benutzer/tokenValidieren"; +import { tRPC_V1_BenutzerFromPublicIdProcedure } from "./benutzer/fromPublicId"; +import { tRPC_V1_BenutzerFromPrivateIdProcedure } from "./benutzer/fromPrivateId"; + +const router = t.router; + +export const v1Router = router({ + verbrauchsausweisWohnen: router({ + 2016: router({ + erstellen: VerbrauchsausweisWohnen2016Erstellen + }), + 2023: router({ + + }) + }), + verbrauchsausweisGewerbe: router({ + 2016: router({ + + }), + 2023: router({ + + }) + }), + bedarfsausweisWohen: router({ + 2016: router({ + + }), + 2023: router({ + + }) + }), + klimafaktoren: tRPC_V1_KlimafaktorenProcedure, + benutzer: router({ + erstellen: tRPC_V1_BenutzerErstellenProcedure, + tokenErneuern: tRPC_V1_BenutzerTokenErneuernProcedure, + tokenValidieren: tRPC_V1_BenutzerTokenValidierenProcedure, + fromPublicId: tRPC_V1_BenutzerFromPublicIdProcedure, + fromPrivateId: tRPC_V1_BenutzerFromPrivateIdProcedure + }) +}) \ No newline at end of file diff --git a/src/pages/api/trpc/procedures/v1/klimafaktoren.ts b/src/lib/trpc/procedures/v1/klimafaktoren.ts similarity index 92% rename from src/pages/api/trpc/procedures/v1/klimafaktoren.ts rename to src/lib/trpc/procedures/v1/klimafaktoren.ts index 0007cbe2..81edaf25 100644 --- a/src/pages/api/trpc/procedures/v1/klimafaktoren.ts +++ b/src/lib/trpc/procedures/v1/klimafaktoren.ts @@ -1,10 +1,10 @@ import { z } from "zod"; -import { t } from "../../context"; import moment from "moment"; import { TRPCError } from "@trpc/server"; import { prisma } from "@ibcornelsen/database"; +import { publicProcedure } from "#lib/trpc/middlewares/publicProcedure"; -export const tRPCKlimafaktorenProcedure = t.procedure +export const tRPC_V1_KlimafaktorenProcedure = publicProcedure .input( z.object({ plz: z.string().min(4).max(5), diff --git a/src/pages/api/trpc/procedures/v1/verbrauchsausweis-wohnen/2016/erstellen.ts b/src/lib/trpc/procedures/v1/verbrauchsausweis-wohnen/2016/erstellen.ts similarity index 86% rename from src/pages/api/trpc/procedures/v1/verbrauchsausweis-wohnen/2016/erstellen.ts rename to src/lib/trpc/procedures/v1/verbrauchsausweis-wohnen/2016/erstellen.ts index 96c2f764..cbae21d1 100644 --- a/src/pages/api/trpc/procedures/v1/verbrauchsausweis-wohnen/2016/erstellen.ts +++ b/src/lib/trpc/procedures/v1/verbrauchsausweis-wohnen/2016/erstellen.ts @@ -1,6 +1,6 @@ -import { ZodSchema, z } from "zod"; -import { publicProcedure } from "../../../../context"; -import { GebaeudeStammdaten, VerbrauchsausweisWohnen, prisma } from "@ibcornelsen/database"; +import { z } from "zod"; +import { prisma } from "@ibcornelsen/database"; +import { publicProcedure } from "#lib/trpc/middlewares/publicProcedure"; export const VerbrauchsausweisWohnen2016Erstellen = publicProcedure .meta({ @@ -16,11 +16,11 @@ export const VerbrauchsausweisWohnen2016Erstellen = publicProcedure baujahr_heizung: z.array(z.number()).optional(), zusaetzliche_heizquelle: z.boolean().optional(), brennstoff_1: z.string().max(50).optional(), - einheit_1: z.string().max(10).optional(), + einheit_1: z.string().max(50).optional(), brennstoff_2: z.string().max(50).optional(), - einheit_2: z.string().max(10).optional(), - startdatum: z.date().optional(), - enddatum: z.date().optional(), + einheit_2: z.string().max(50).optional(), + startdatum: z.coerce.date().optional(), + enddatum: z.coerce.date().optional(), verbrauch_1: z.number().optional(), verbrauch_2: z.number().optional(), verbrauch_3: z.number().optional(), @@ -86,8 +86,8 @@ export const VerbrauchsausweisWohnen2016Erstellen = publicProcedure aussenwand_min_12cm_gedaemmt: z.boolean().optional(), dachgeschoss_min_12cm_gedaemmt: z.boolean().optional(), oberste_geschossdecke_min_12cm_gedaemmt: z.boolean().optional(), - } satisfies ZodSchema)) - } satisfies ZodSchema)) + })) + })) .output( z.object({ uid: z.string().uuid(), @@ -121,7 +121,23 @@ export const VerbrauchsausweisWohnen2016Erstellen = publicProcedure }); return { uid: verbrauchsausweis.uid }; - } + } else { + // Gebäude existiert noch nicht + const gebaeude = await prisma.gebaeudeStammdaten.create({ + data: opts.input.gebaeude_stammdaten + }); - return { uid: "" }; + const verbrauchsausweis = await prisma.verbrauchsausweisWohnen.create({ + data: { + ...opts.input, + gebaeude_stammdaten: { + connect: { + uid: gebaeude.uid + } + } + } + }); + + return { uid: verbrauchsausweis.uid }; + } }); diff --git a/src/pages/api/trpc/procedures/v1/verbrauchsausweis-wohnen/2016/zusatzdaten-erfassung.xml.ts b/src/lib/trpc/procedures/v1/verbrauchsausweis-wohnen/2016/zusatzdaten-erfassung.xml.ts similarity index 100% rename from src/pages/api/trpc/procedures/v1/verbrauchsausweis-wohnen/2016/zusatzdaten-erfassung.xml.ts rename to src/lib/trpc/procedures/v1/verbrauchsausweis-wohnen/2016/zusatzdaten-erfassung.xml.ts diff --git a/src/modules/Ausweise/VerbrauchsausweisWohnenModule.svelte b/src/modules/Ausweise/VerbrauchsausweisWohnenModule.svelte index 8a42f555..5d2861d8 100644 --- a/src/modules/Ausweise/VerbrauchsausweisWohnenModule.svelte +++ b/src/modules/Ausweise/VerbrauchsausweisWohnenModule.svelte @@ -15,13 +15,14 @@ import { auditBedarfsausweisBenoetigt } from "#components/Verbrauchsausweis/audits/BedarfsausweisBenoetigt"; import { auditVerbrauchAbweichung } from "#components/Verbrauchsausweis/audits/VerbrauchAbweichung"; import { GebaeudeStammdaten, VerbrauchsausweisWohnen } from "@ibcornelsen/database"; - import trpc from "src/trpc"; + import { client } from "src/trpc"; export let uid: string = ""; let gebaeude: GebaeudeStammdaten = {} as GebaeudeStammdaten; let ausweis: VerbrauchsausweisWohnen = {} as VerbrauchsausweisWohnen; if (uid) { + // NOTE: Funktioniert nicht mehr async () => { const result = await fetch(`/api/verbrauchsausweis?uid=${uid}`, { method: "GET", @@ -62,11 +63,16 @@ async function ausweisAbschicken() { + console.log(ausweis); + overlay.ariaHidden = "false"; - const response = await trpc.v1.verbrauchsausweisWohnen[2016].erstellen.mutate({ + const response = await client.v1.verbrauchsausweisWohnen[2016].erstellen.mutate({ ...ausweis, gebaeude_stammdaten: gebaeude }) + + console.log(response.uid); + } let overlay: HTMLDivElement; diff --git a/src/modules/LoginModule.svelte b/src/modules/LoginModule.svelte index fae99ebb..cd2e2432 100644 --- a/src/modules/LoginModule.svelte +++ b/src/modules/LoginModule.svelte @@ -1,34 +1,18 @@ @@ -61,7 +54,7 @@ type="password" placeholder="********" name="password" - bind:value={password} + bind:value={passwort} required /> diff --git a/src/modules/RegisterModule.svelte b/src/modules/RegisterModule.svelte index 4ed06b8b..55d3566d 100644 --- a/src/modules/RegisterModule.svelte +++ b/src/modules/RegisterModule.svelte @@ -1,20 +1,28 @@