Bildupload und Kundendaten

This commit is contained in:
Moritz Utcke
2025-01-22 15:29:11 +07:00
parent 46ef96becd
commit bd60df1ef4
29 changed files with 168 additions and 103 deletions

File diff suppressed because one or more lines are too long

View File

@@ -13,7 +13,8 @@
"format": "prettier --write .", "format": "prettier --write .",
"build:production": "astro build && bun --bun server.ts", "build:production": "astro build && bun --bun server.ts",
"i18n:generate": "bunx astro-i18next generate", "i18n:generate": "bunx astro-i18next generate",
"prisma:studio": "bunx prisma studio --schema=./node_modules/@ibcornelsen/database/prisma/schema.prisma" "prisma:studio": "bunx prisma studio --schema=./node_modules/@ibcornelsen/database/prisma/schema.prisma",
"openapi:generate": "bun astro-typesafe-api generate"
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {
@@ -43,6 +44,7 @@
"js-cookie": "^3.0.5", "js-cookie": "^3.0.5",
"js-interpolate": "^1.3.2", "js-interpolate": "^1.3.2",
"jsonwebtoken": "^9.0.2", "jsonwebtoken": "^9.0.2",
"jwt-decode": "^4.0.0",
"moment": "^2.30.1", "moment": "^2.30.1",
"moment-timezone": "^0.5.46", "moment-timezone": "^0.5.46",
"pdf-lib": "^1.17.1", "pdf-lib": "^1.17.1",

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 MiB

View File

@@ -6,9 +6,9 @@ export const createCaller = createCallerFactory({
"ticket": await import("../src/pages/api/ticket.ts"), "ticket": await import("../src/pages/api/ticket.ts"),
"aufnahme/[uid]": await import("../src/pages/api/aufnahme/[uid].ts"), "aufnahme/[uid]": await import("../src/pages/api/aufnahme/[uid].ts"),
"aufnahme": await import("../src/pages/api/aufnahme/index.ts"), "aufnahme": await import("../src/pages/api/aufnahme/index.ts"),
"bedarfsausweis-wohnen": await import("../src/pages/api/bedarfsausweis-wohnen/index.ts"),
"auth/access-token": await import("../src/pages/api/auth/access-token.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"), "auth/refresh-token": await import("../src/pages/api/auth/refresh-token.ts"),
"bedarfsausweis-wohnen": await import("../src/pages/api/bedarfsausweis-wohnen/index.ts"),
"objekt": await import("../src/pages/api/objekt/index.ts"), "objekt": await import("../src/pages/api/objekt/index.ts"),
"user": await import("../src/pages/api/user/index.ts"), "user": await import("../src/pages/api/user/index.ts"),
"user/self": await import("../src/pages/api/user/self.ts"), "user/self": await import("../src/pages/api/user/self.ts"),

View File

@@ -1,9 +1,9 @@
import Cookies from "js-cookie"; 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 { API_ACCESS_TOKEN_COOKIE_NAME, API_REFRESH_TOKEN_COOKIE_NAME, API_UID_COOKIE_NAME } from "#lib/constants.js";
import moment from "moment"; import moment from "moment";
import jwt from "jsonwebtoken" import { TokenData, TokenType } from "#lib/auth/types.js";
import { TokenData, TokenType } from "#lib/auth/token.js";
import { api } from "astro-typesafe-api/client"; import { api } from "astro-typesafe-api/client";
import { jwtDecode } from "jwt-decode"
export async function validateAccessTokenClient() { export async function validateAccessTokenClient() {
@@ -11,9 +11,7 @@ export async function validateAccessTokenClient() {
const refreshToken = Cookies.get(API_REFRESH_TOKEN_COOKIE_NAME); const refreshToken = Cookies.get(API_REFRESH_TOKEN_COOKIE_NAME);
if (accessToken) { if (accessToken) {
const { exp, typ, uid } = jwt.decode(accessToken, { const { exp, typ, uid } = jwtDecode(accessToken) as TokenData
json: true
}) as TokenData
if (exp > Date.now() && typ === TokenType.Access) { if (exp > Date.now() && typ === TokenType.Access) {
return true; return true;

View File

@@ -2,11 +2,9 @@
import { api } from "astro-typesafe-api/client" import { api } from "astro-typesafe-api/client"
import { exclude } from "#lib/exclude.js"; import { exclude } from "#lib/exclude.js";
import { bilderHochladen } from "./bilderHochladen.js";
import { prisma } from "@ibcornelsen/database/server";
import Cookies from "js-cookie"; import Cookies from "js-cookie";
import { API_ACCESS_TOKEN_COOKIE_NAME } from "#lib/constants.js"; import { API_ACCESS_TOKEN_COOKIE_NAME } from "#lib/constants.js";
import { AufnahmeClient, BenutzerClient, ObjektClient, UploadedGebaeudeBild, VerbrauchsausweisWohnenClient, } from "#components/Ausweis/types.js"; import { AufnahmeClient, ObjektClient, UploadedGebaeudeBild, VerbrauchsausweisWohnenClient, } from "#components/Ausweis/types.js";
// import { addNotification } from "@ibcornelsen/ui"; // import { addNotification } from "@ibcornelsen/ui";
export async function verbrauchsausweisWohnenSpeichern( export async function verbrauchsausweisWohnenSpeichern(

View File

@@ -1,12 +1,13 @@
<script lang="ts"> <script lang="ts">
import AusweisWeiter from "#modules/VerbrauchsausweisWohnen/AusweisWeiter.svelte"; import AusweisWeiter from "#modules/VerbrauchsausweisWohnen/AusweisWeiter.svelte";
import Hilfe from "#components/Ausweis/Hilfe.svelte"; import Hilfe from "#components/Ausweis/Hilfe.svelte";
import { AufnahmeClient, BenutzerClient, ObjektClient, UploadedGebaeudeBild, VerbrauchsausweisWohnenClient } from "./types.js";
export let ausweis; export let ausweis: VerbrauchsausweisWohnenClient;
export let images; export let bilder: UploadedGebaeudeBild[];
export let user; export let user: BenutzerClient;
export let gebaeude; export let objekt: ObjektClient;
export let aufnahme; export let aufnahme: AufnahmeClient;
export let spaeterWeitermachen; export let spaeterWeitermachen;
@@ -26,9 +27,9 @@
<div class=""> <div class="">
<AusweisWeiter <AusweisWeiter
bind:ausweis bind:ausweis
bind:images bind:bilder
bind:user bind:user
bind:gebaeude bind:objekt
bind:aufnahme bind:aufnahme
></AusweisWeiter> ></AusweisWeiter>
</div> </div>

View File

@@ -1,13 +1,13 @@
<script lang="ts"> <script lang="ts">
import AusweisWeiter from "#modules/VerbrauchsausweisWohnen/AusweisWeiter.svelte"; import AusweisWeiter from "#modules/VerbrauchsausweisWohnen/AusweisWeiter.svelte";
import Hilfe from "#components/Ausweis/Hilfe.svelte"; import Hilfe from "#components/Ausweis/Hilfe.svelte";
import { AufnahmeClient, BenutzerClient, ObjektClient, UploadedGebaeudeBild, VerbrauchsausweisWohnenClient } from "./types.js";
export let ausweis; export let ausweis: VerbrauchsausweisWohnenClient;
export let images; export let bilder: UploadedGebaeudeBild[];
export let user; export let user: BenutzerClient;
export let gebaeude; export let objekt: ObjektClient;
export let aufnahme; export let aufnahme: AufnahmeClient;
export let spaeterWeitermachen; export let spaeterWeitermachen;
</script> </script>

View File

@@ -5,8 +5,10 @@
import ZipSearch from "#components/PlzSuche.svelte"; import ZipSearch from "#components/PlzSuche.svelte";
import { Enums } from "@ibcornelsen/database/client" import { Enums } from "@ibcornelsen/database/client"
import { AufnahmeClient, ObjektClient } from "./types.js";
export let aufnahme: GebaeudeAufnahmeClient; export let aufnahme: AufnahmeClient;
export let objekt: ObjektClient;
</script> </script>
@@ -29,7 +31,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
required required
data-msg-minlength="min. 5 Zeichen" data-msg-minlength="min. 5 Zeichen"
data-msg-maxlength="max. 40 Zeichen" data-msg-maxlength="max. 40 Zeichen"
bind:value={aufnahme.adresse} bind:value={objekt.adresse}
/> />
<div class="help-label"> <div class="help-label">
@@ -47,8 +49,8 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
<Inputlabel title="PLZ *"></Inputlabel> <Inputlabel title="PLZ *"></Inputlabel>
<ZipSearch <ZipSearch
bind:zip={aufnahme.plz} bind:zip={objekt.plz}
bind:city={aufnahme.ort} bind:city={objekt.ort}
name="plz" name="plz"
/> />
@@ -61,7 +63,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
name="ort" name="ort"
data-test="ort" data-test="ort"
readonly={true} readonly={true}
bind:value={aufnahme.ort} bind:value={objekt.ort}
type="text" type="text"
/> />

View File

@@ -1,27 +1,34 @@
<script lang="ts"> <script lang="ts">
export let bereich: string; export let bereich: string;
export let title: string; export let title: string;
export let bullets: string; export let bullets: [string, boolean][];
</script> </script>
<div class=""> <div class="">
<strong>{bereich} - {title}</strong> <strong>{bereich} - {title}</strong>
<div class="mt-4 mb-6"> <div class="mt-4 mb-6">
{#each bullets as [bullet, check]} {#each bullets as [bullet, check]}
<div class="bullets grid grid-cols-[1fr_40px] items-center border-b-[1px] border-b-black/10"> <div
class="bullets grid grid-cols-[1fr_40px] items-center border-b-[1px] border-b-black/10"
>
<span>{@html bullet}</span> <span>{@html bullet}</span>
<div class="justify-self-end" class:check={check} class:check-no={!check}>{check ? "✔" : "✘"}</div> <div
class="justify-self-end"
class:check
class:check-no={!check}
>
{check ? "✔" : "✘"}
</div>
</div> </div>
{/each} {/each}
</div> </div>
</div> </div>
<style lang="postcss"> <style lang="postcss">
.check {
.check{@apply self-center font-bold text-green-700} @apply self-center font-bold text-green-700;
.check-no{@apply self-center font-bold text-red-700} }
.check-no {
@apply self-center font-bold text-red-700;
}
</style> </style>

View File

@@ -90,3 +90,17 @@ type ZodOverlapType<T> = z.ZodType<T, z.ZodTypeDef, T>;
export function ZodOverlap<T, S = z.ZodType<T, z.ZodTypeDef, T>>(arg: S): S { export function ZodOverlap<T, S = z.ZodType<T, z.ZodTypeDef, T>>(arg: S): S {
return arg; return arg;
} }
type PickNullable<T> = {
[P in keyof T as null extends T[P] ? P : never]: T[P]
}
type PickNotNullable<T> = {
[P in keyof T as null extends T[P] ? never : P]: T[P]
}
export type OptionalNullable<T> = T extends object ? {
[K in keyof PickNullable<T>]?: OptionalNullable<T[K]>
} & {
[K in keyof PickNotNullable<T>]: OptionalNullable<T[K]>
} : T;

View File

@@ -1,12 +1,5 @@
import jwt from "jsonwebtoken"; import jwt from "jsonwebtoken";
import { TokenData } from "./types.js";
export enum TokenType {
Refresh,
Access,
Reset
}
export type TokenData = { uid: string, typ: TokenType, exp: number }
export function encodeToken(data: TokenData) { export function encodeToken(data: TokenData) {
const token = jwt.sign(data, "yIvbgS$k7Bfc+mpV%TWDZAhje9#uJad4", { const token = jwt.sign(data, "yIvbgS$k7Bfc+mpV%TWDZAhje9#uJad4", {

7
src/lib/auth/types.ts Normal file
View File

@@ -0,0 +1,7 @@
export enum TokenType {
Refresh,
Access,
Reset
}
export type TokenData = { uid: string, typ: TokenType, exp: number }

View File

@@ -2,7 +2,6 @@
import PerformanceScore from "#components/Ausweis/PerformanceScore.svelte"; import PerformanceScore from "#components/Ausweis/PerformanceScore.svelte";
import ProgressBar from "#components/Ausweis/Progressbar.svelte"; import ProgressBar from "#components/Ausweis/Progressbar.svelte";
import Pruefung from "#components/Ausweis/Pruefung.svelte"; import Pruefung from "#components/Ausweis/Pruefung.svelte";
import ButtonZurueckSpeichernKaufabschluss from "#components/Ausweis/ButtonZurueckSpeichernKaufabschluss.svelte";
import type { Bezahlmethoden } from "@ibcornelsen/database/client"; import type { Bezahlmethoden } from "@ibcornelsen/database/client";
import { Enums } from "@ibcornelsen/database/client"; import { Enums } from "@ibcornelsen/database/client";
@@ -10,21 +9,17 @@
import LoginDialog from "#components/LoginDialog.svelte"; import LoginDialog from "#components/LoginDialog.svelte";
import { PRICES } from "#lib/constants.js"; import { PRICES } from "#lib/constants.js";
import { import {
AufnahmeClient,
BenutzerClient, BenutzerClient,
ObjektClient,
VerbrauchsausweisWohnenClient, VerbrauchsausweisWohnenClient,
} from "#components/Ausweis/types.js"; } from "#components/Ausweis/types.js";
import { validateAccessTokenClient } from "src/client/lib/validateAccessToken.js"; import { validateAccessTokenClient } from "src/client/lib/validateAccessToken.js";
import { client } from "src/trpc.js";
export let user: BenutzerClient; export let user: BenutzerClient;
export let ausweis: VerbrauchsausweisWohnenClient; export let ausweis: VerbrauchsausweisWohnenClient;
export let aufnahme: AufnahmeClient;
export let bereich; export let objekt: ObjektClient;
export let title;
export let bullets;
let aufnahme = ausweis.aufnahme || {};
let gebaeude = ausweis.aufnahme?.objekt || {};
let services = [ let services = [
{ {
@@ -58,8 +53,8 @@
let prices: number[] = []; let prices: number[] = [];
if (ausweis.aufnahme.ausweisart) { if (aufnahme.ausweisart) {
prices = PRICES[ausweis.aufnahme.ausweisart]; prices = PRICES[aufnahme.ausweisart];
} }
let basePrice: number = prices[0]; let basePrice: number = prices[0];
@@ -71,7 +66,7 @@
0 0
); );
async function speichern(e: MouseEvent) { async function speichern(e: SubmitEvent) {
e.preventDefault(); e.preventDefault();
// Um einen Ausweis zu speichern müssen wir eingeloggt sein, andernfalls wird die API den call ablehnen. // Um einen Ausweis zu speichern müssen wir eingeloggt sein, andernfalls wird die API den call ablehnen.
@@ -102,8 +97,8 @@
<div id="performance-box" class="w-full box relative px-4 order-2 2xl:order-1 self-stretch grid grid-cols-1"> <div id="performance-box" class="w-full box relative px-4 order-2 2xl:order-1 self-stretch grid grid-cols-1">
<PerformanceScore <PerformanceScore
bind:ausweis bind:ausweis
bind:aufnahme={aufnahme} bind:aufnahme
bind:objekt={gebaeude} bind:objekt
/> />
</div> </div>
@@ -301,7 +296,7 @@
</div> </div>
<ButtonZurueckSpeichernKaufabschluss /> <!-- <ButtonZurueckSpeichernKaufabschluss bind:ausweis bind:aufnahme bind:objekt bind:bilder bind:user /> -->
</form> </form>

View File

@@ -9,7 +9,7 @@
import { api } from "astro-typesafe-api/client" import { api } from "astro-typesafe-api/client"
export let objekt: ObjektClient; export let objekt: ObjektClient;
export let images: UploadedGebaeudeBild[]; export let bilder: UploadedGebaeudeBild[];
export let ausweis: VerbrauchsausweisWohnenClient; export let ausweis: VerbrauchsausweisWohnenClient;
export let user: BenutzerClient; export let user: BenutzerClient;
export let aufnahme: AufnahmeClient; export let aufnahme: AufnahmeClient;
@@ -31,7 +31,7 @@
const response = await verbrauchsausweisWohnenSpeichern(ausweis, const response = await verbrauchsausweisWohnenSpeichern(ausweis,
objekt, objekt,
aufnahme, aufnahme,
images) bilder)
if (response !== null) { if (response !== null) {
// Falls der Nutzer zurück navigiert, sollte er wieder auf seinen Vorgang kommen. // Falls der Nutzer zurück navigiert, sollte er wieder auf seinen Vorgang kommen.
@@ -43,6 +43,8 @@
return true return true
} }
} catch (e: any) { } catch (e: any) {
console.log(e);
await api.ticket.PUT.fetch({ await api.ticket.PUT.fetch({
titel: "Ausweis konnte nicht gespeichert werden", titel: "Ausweis konnte nicht gespeichert werden",
beschreibung: e.stack, beschreibung: e.stack,

View File

@@ -220,7 +220,7 @@ const ausweisart: Enums.Ausweisart = "VerbrauchsausweisWohnen"
<Bereich <Bereich
bereich="B" bereich="B"
title="Eingabe der Gebäudeadresse - Angaben zu Wohnfläche, Keller und Dachgeschoss" title="Eingabe der Gebäudeadresse - Angaben zu Wohnfläche, Keller und Dachgeschoss"
><GebaeudeDaten bind:aufnahme={aufnahme} /></Bereich ><GebaeudeDaten bind:aufnahme bind:objekt /></Bereich
> >
<!-- C Eingabe von 3 zusammenhängenden Verbrauchsjahren --> <!-- C Eingabe von 3 zusammenhängenden Verbrauchsjahren -->
@@ -296,10 +296,10 @@ const ausweisart: Enums.Ausweisart = "VerbrauchsausweisWohnen"
<ButtonWeiterHilfe {spaeterWeitermachen} <ButtonWeiterHilfe {spaeterWeitermachen}
bind:ausweis bind:ausweis
bind:images={bilder} bind:bilder
bind:user bind:user
bind:gebaeude={objekt} bind:objekt
bind:aufnahme={aufnahme} bind:aufnahme
> >
</ButtonWeiterHilfe> </ButtonWeiterHilfe>

View File

@@ -1,4 +1,4 @@
import { AufnahmeClient, ZodOverlap } from "#components/Ausweis/types.js"; import { AufnahmeClient, OptionalNullable, ZodOverlap } from "#components/Ausweis/types.js";
import { exclude } from "#lib/exclude.js"; import { exclude } from "#lib/exclude.js";
import { authorizationMiddleware } from "#lib/middleware/authorization.js"; import { authorizationMiddleware } from "#lib/middleware/authorization.js";
import { AufnahmeSchema, prisma } from "@ibcornelsen/database/server"; import { AufnahmeSchema, prisma } from "@ibcornelsen/database/server";
@@ -57,7 +57,7 @@ export const GET = defineApiRoute({
} }
} }
}, },
output: ZodOverlap<AufnahmeClient>(AufnahmeSchema.omit({ output: ZodOverlap<OptionalNullable<AufnahmeClient>>(AufnahmeSchema.omit({
id: true, id: true,
objekt_id: true, objekt_id: true,
benutzer_id: true benutzer_id: true

View File

@@ -11,7 +11,7 @@ export const PUT = defineApiRoute({
benutzer_id: true, benutzer_id: true,
objekt_id: true, objekt_id: true,
}).merge(z.object({ }).merge(z.object({
baujahr_klima: z.array(z.number().int().positive()).optional() baujahr_klima: z.array(z.number().int().positive()).nullish()
})), })),
uid_objekt: z.string().uuid() uid_objekt: z.string().uuid()
}), }),

View File

@@ -1,9 +1,10 @@
import { z } from "zod"; import { z } from "zod";
import moment from "moment"; import moment from "moment";
import { prisma } from "@ibcornelsen/database/server"; import { prisma } from "@ibcornelsen/database/server";
import { TokenType, encodeToken } from "../../../lib/auth/token.js"; import { encodeToken } from "../../../lib/auth/token.js";
import { TRPCError } from "@trpc/server"; import { TRPCError } from "@trpc/server";
import { defineApiRoute } from "astro-typesafe-api/server"; import { defineApiRoute } from "astro-typesafe-api/server";
import { TokenType } from "#lib/auth/types.js";
export const GET = defineApiRoute({ export const GET = defineApiRoute({
meta: { meta: {

View File

@@ -1,9 +1,10 @@
import { z } from "zod"; import { z } from "zod";
import moment from "moment"; import moment from "moment";
import { prisma } from "@ibcornelsen/database/server"; import { prisma } from "@ibcornelsen/database/server";
import { TokenType, encodeToken } from "../../../lib/auth/token.js"; import { encodeToken } from "../../../lib/auth/token.js";
import { hashPassword, validatePassword } from "../../../lib/password.js"; import { validatePassword } from "../../../lib/password.js";
import { APIError, defineApiRoute } from "astro-typesafe-api/server"; import { APIError, defineApiRoute } from "astro-typesafe-api/server";
import { TokenType } from "#lib/auth/types.js";
export const GET = defineApiRoute({ export const GET = defineApiRoute({
meta: { meta: {

View File

@@ -1,8 +1,10 @@
import { authorizationMiddleware } from "#lib/middleware/authorization.js"; import { authorizationMiddleware } from "#lib/middleware/authorization.js";
import { GebaeudeBilderSchema, prisma } from "@ibcornelsen/database/server"; import { GebaeudeBilderSchema, prisma } from "@ibcornelsen/database/server";
import { APIError, defineApiRoute } from "astro-typesafe-api/server"; import { APIError, defineApiRoute } from "astro-typesafe-api/server";
import { z } from "astro:content"; import { z } from "zod";
import isBase64 from "is-base64"; import isBase64 from "is-base64";
import { fileURLToPath } from "url";
import { writeFileSync } from "fs";
export const PUT = defineApiRoute({ export const PUT = defineApiRoute({
input: GebaeudeBilderSchema.pick({ input: GebaeudeBilderSchema.pick({
@@ -20,7 +22,7 @@ export const PUT = defineApiRoute({
if (!isBase64(base64, { mimeRequired: true })) { if (!isBase64(base64, { mimeRequired: true })) {
throw new APIError({ throw new APIError({
code: "BAD_REQUEST", code: "BAD_REQUEST",
message: "Das Bild ist nicht base64 kodiert.", message: "Das Bild ist nicht base64.",
}); });
} }
@@ -58,15 +60,17 @@ export const PUT = defineApiRoute({
}, },
}); });
const filePath = `/persistent/images/${bild.uid}.webp`; const filePath = fileURLToPath(new URL(`../../../../../persistent/images/${bild.uid}.webp`, import.meta.url));
try { try {
// Wir optimieren das Bild und konvertieren es in WebP // Wir optimieren das Bild und konvertieren es in WebP
// TODO: Sharp scheint nicht zu funktionieren, wir müssen das nochmal testen // TODO: Sharp scheint nicht zu funktionieren, wir müssen das nochmal testen
// const optimizedBuffer = await sharp(buffer).webp({ quality: 80 }).toArray(); // const optimizedBuffer = await sharp(buffer).webp({ quality: 80 }).toArray();
await Bun.write(filePath, buffer) writeFileSync(filePath, buffer)
} catch(e) { } catch(e) {
console.log(e);
// Bild wurde nicht gespeichert, wir löschen den Eintrag wieder // Bild wurde nicht gespeichert, wir löschen den Eintrag wieder
await prisma.gebaeudeBilder.delete({ await prisma.gebaeudeBilder.delete({
where: { where: {
@@ -97,7 +101,8 @@ export const GET = defineApiRoute({
const objekt = await prisma.objekt.findUnique({ const objekt = await prisma.objekt.findUnique({
where: { where: {
uid uid,
benutzer_id: user.id
}, },
select: { select: {
benutzer_id: true, benutzer_id: true,
@@ -110,7 +115,7 @@ export const GET = defineApiRoute({
} }
}) })
if (!objekt || objekt.benutzer_id !== user.id) { if (!objekt) {
throw new APIError({ throw new APIError({
code: "FORBIDDEN", code: "FORBIDDEN",
message: "Objekt existiert nicht oder gehört einem anderen Benutzer." message: "Objekt existiert nicht oder gehört einem anderen Benutzer."

View File

@@ -1,4 +1,4 @@
import { ObjektClient, ZodOverlap } from "#components/Ausweis/types.js"; import { ObjektClient, OptionalNullable, ZodOverlap } from "#components/Ausweis/types.js";
import { exclude } from "#lib/exclude.js"; import { exclude } from "#lib/exclude.js";
import { authorizationMiddleware } from "#lib/middleware/authorization.js"; import { authorizationMiddleware } from "#lib/middleware/authorization.js";
import { ObjektSchema, prisma } from "@ibcornelsen/database/server"; import { ObjektSchema, prisma } from "@ibcornelsen/database/server";
@@ -59,7 +59,7 @@ export const GET = defineApiRoute({
} }
} }
}, },
output: ZodOverlap<ObjektClient>(ObjektSchema.omit({ output: ZodOverlap<OptionalNullable<ObjektClient>>(ObjektSchema.omit({
benutzer_id: true, benutzer_id: true,
id: true id: true
})), })),

View File

@@ -1,4 +1,4 @@
import { VerbrauchsausweisWohnenClient, ZodOverlap } from "#components/Ausweis/types.js"; import { OptionalNullable, VerbrauchsausweisWohnenClient, ZodOverlap } from "#components/Ausweis/types.js";
import { exclude } from "#lib/exclude.js"; import { exclude } from "#lib/exclude.js";
import { authorizationMiddleware } from "#lib/middleware/authorization.js"; import { authorizationMiddleware } from "#lib/middleware/authorization.js";
import { prisma, VerbrauchsausweisWohnenSchema } from "@ibcornelsen/database/server"; import { prisma, VerbrauchsausweisWohnenSchema } from "@ibcornelsen/database/server";
@@ -60,7 +60,7 @@ export const GET = defineApiRoute({
} }
} }
}, },
output: ZodOverlap<VerbrauchsausweisWohnenClient>(VerbrauchsausweisWohnenSchema.merge(z.object({ output: ZodOverlap<OptionalNullable<VerbrauchsausweisWohnenClient>>(VerbrauchsausweisWohnenSchema.merge(z.object({
uid_aufnahme: z.string().uuid(), uid_aufnahme: z.string().uuid(),
uid_objekt: z.string().uuid(), uid_objekt: z.string().uuid(),
uid_benutzer: z.string().uuid().optional() uid_benutzer: z.string().uuid().optional()
@@ -73,8 +73,12 @@ export const GET = defineApiRoute({
async fetch(input, context, user) { async fetch(input, context, user) {
const { uid } = context.params; const { uid } = context.params;
console.log(uid); if (!uid) {
throw new APIError({
code: "BAD_REQUEST",
message: "Missing uid in request params"
})
}
const ausweis = await prisma.verbrauchsausweisWohnen.findUnique({ const ausweis = await prisma.verbrauchsausweisWohnen.findUnique({
where: { where: {

View File

@@ -3,30 +3,65 @@
import KundendatenModule from "#modules/KundendatenModule.svelte"; import KundendatenModule from "#modules/KundendatenModule.svelte";
import AusweisLayout from "#layouts/AusweisLayoutPruefung.astro"; import AusweisLayout from "#layouts/AusweisLayoutPruefung.astro";
import { Enums } from "@ibcornelsen/database/client"; import { Enums } from "@ibcornelsen/database/client";
import { createCaller } from "#lib/caller"; import { createCaller } from "../astro-typesafe-api-caller";
import { API_ACCESS_TOKEN_COOKIE_NAME, API_REFRESH_TOKEN_COOKIE_NAME } from "#lib/constants";
import { validateAccessTokenServer } from "#server/lib/validateAccessToken";
// Man sollte nur auf diese Seite kommen, wenn ein Ausweis bereits vorliegt und in der Datenbank abgespeichert wurde. // Man sollte nur auf diese Seite kommen, wenn ein Ausweis bereits vorliegt und in der Datenbank abgespeichert wurde.
const uid = Astro.url.searchParams.get("uid"); const uid = Astro.url.searchParams.get("uid");
const valid = await validateAccessTokenServer(Astro)
if (!uid) { if (!uid || !valid) {
return Astro.redirect("/404"); return Astro.redirect("/404");
} }
const caller = createCaller(Astro);
const ausweis = await caller.v1.verbrauchsausweisWohnen.get({ const caller = createCaller(Astro)
const ausweis = await caller["verbrauchsausweis-wohnen"]._uid.GET.fetch(undefined, {
headers: {
Authorization: `Bearer ${Astro.cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)?.value}`
},
params: {
uid uid
}
}) })
const user = await caller.v1.benutzer.self(); const aufnahme = await caller.aufnahme._uid.GET.fetch(undefined, {
headers: {
Authorization: `Bearer ${Astro.cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)?.value}`
},
params: {
uid: ausweis.uid_aufnahme
}
})
const objekt = await caller.objekt._uid.GET.fetch(undefined, {
headers: {
Authorization: `Bearer ${Astro.cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)?.value}`
},
params: {
uid: aufnahme.uid_objekt
}
})
const user = await caller.user.self.GET.fetch(undefined, {
headers: {
Authorization: `Bearer ${Astro.cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)?.value}`
}
});
aufnahme.ausweisart = "VerbrauchsausweisWohnen"
if (!ausweis) {
if (!ausweis || !user) {
return Astro.redirect("/404"); return Astro.redirect("/404");
} }
--- ---
<AusweisLayout title="Kundendaten Aufnehmen - IBCornelsen"> <AusweisLayout title="Kundendaten Aufnehmen - IBCornelsen">
<KundendatenModule user={user} ausweis={ausweis} selectedPaymentType={Enums.Bezahlmethoden.paypal} client:load></KundendatenModule> <KundendatenModule {user} {ausweis} {objekt} {aufnahme} selectedPaymentType={Enums.Bezahlmethoden.paypal} client:load></KundendatenModule>
</AusweisLayout> </AusweisLayout>

View File

@@ -3,7 +3,7 @@ import type { AstroGlobal } from "astro";
import moment from "moment"; import moment from "moment";
import { createCaller } from "../../astro-typesafe-api-caller.js" import { createCaller } from "../../astro-typesafe-api-caller.js"
import jwt from "jsonwebtoken" import jwt from "jsonwebtoken"
import { TokenData, TokenType } from "#lib/auth/token.js"; import { TokenData, TokenType } from "#lib/auth/types.js";
export async function validateAccessTokenServer(astro: AstroGlobal) { export async function validateAccessTokenServer(astro: AstroGlobal) {
const accessToken = astro.cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)?.value; const accessToken = astro.cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)?.value;