API Vollständig Umgezogen
This commit is contained in:
@@ -3,16 +3,17 @@ import { createCallerFactory } from "astro-typesafe-api/server";
|
||||
export const createCaller = createCallerFactory({
|
||||
"klimafaktoren": await import("../src/pages/api/klimafaktoren.ts"),
|
||||
"postleitzahlen": await import("../src/pages/api/postleitzahlen.ts"),
|
||||
"ticket": await import("../src/pages/api/ticket.ts"),
|
||||
"aufnahme/[uid]": await import("../src/pages/api/aufnahme/[uid].ts"),
|
||||
"aufnahme": await import("../src/pages/api/aufnahme/index.ts"),
|
||||
"bilder/[uid]": await import("../src/pages/api/bilder/[uid].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"),
|
||||
"bedarfsausweis-wohnen": await import("../src/pages/api/bedarfsausweis-wohnen/index.ts"),
|
||||
"objekt": await import("../src/pages/api/objekt/index.ts"),
|
||||
"verbrauchsausweis-gewerbe": await import("../src/pages/api/verbrauchsausweis-gewerbe/index.ts"),
|
||||
"ticket": await import("../src/pages/api/ticket/index.ts"),
|
||||
"user": await import("../src/pages/api/user/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/[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"),
|
||||
|
||||
@@ -1,65 +1,95 @@
|
||||
import { GebaeudeClient, UploadedGebaeudeBild, VerbrauchsausweisWohnenClient } from "#components/Ausweis/types";
|
||||
import {
|
||||
ObjektClient,
|
||||
UploadedGebaeudeBild,
|
||||
VerbrauchsausweisWohnenClient,
|
||||
} from "#components/Ausweis/types.js";
|
||||
import { API_ACCESS_TOKEN_COOKIE_NAME } from "#lib/constants.js";
|
||||
import { Enums } from "@ibcornelsen/database/client";
|
||||
import { addNotification, updateNotification } from "@ibcornelsen/ui";
|
||||
import { client } from "src/trpc";
|
||||
import { api } from "astro-typesafe-api/client";
|
||||
import Cookies from "js-cookie";
|
||||
|
||||
export async function bilderHochladen(images: (UploadedGebaeudeBild & { base64?: string })[], gebaeude_uid: string) {
|
||||
if (images.length == 0) {
|
||||
return images;
|
||||
}
|
||||
export async function bilderHochladen(
|
||||
images: (UploadedGebaeudeBild & { base64?: string, update?: boolean })[],
|
||||
gebaeude_uid: string
|
||||
) {
|
||||
if (images.length == 0) {
|
||||
return images;
|
||||
}
|
||||
|
||||
// Wenn Bilder hochgeladen werden konvertieren wir sie zu base64, das heißt, dass die base64 Eigenschaft bei diesen Bildern
|
||||
// existiert. Das müssen wir TypeScript nur wissen lassen, damit es uns in Ruhe lässt.
|
||||
const imagesToUpload = images.filter(image => !image.uid || image.update) as unknown as { base64: string, kategorie: string, uid?: string, update: boolean }[];
|
||||
// Wenn Bilder hochgeladen werden konvertieren wir sie zu base64, das heißt, dass die base64 Eigenschaft bei diesen Bildern
|
||||
// existiert. Das müssen wir TypeScript nur wissen lassen, damit es uns in Ruhe lässt.
|
||||
const imagesToUpload = images.filter(
|
||||
(image) => !image.uid || image.update
|
||||
) as unknown as {
|
||||
base64: string;
|
||||
kategorie: string;
|
||||
uid?: string;
|
||||
update: boolean;
|
||||
}[];
|
||||
|
||||
if (imagesToUpload.length == 0) {
|
||||
return images;
|
||||
}
|
||||
if (imagesToUpload.length == 0) {
|
||||
return images;
|
||||
}
|
||||
|
||||
// Alle Bilder hochladen
|
||||
const notification = addNotification({
|
||||
dismissable: false,
|
||||
message: "Bilder hochladen.",
|
||||
subtext: `${imagesToUpload.length} Bilder werden hochgeladen, bitte haben sie Geduld.`,
|
||||
timeout: 0,
|
||||
type: "info"
|
||||
})
|
||||
for (let i = 0; i < imagesToUpload.length; i++) {
|
||||
const image = imagesToUpload[i];
|
||||
// Alle Bilder hochladen
|
||||
const notification = addNotification({
|
||||
dismissable: false,
|
||||
message: "Bilder hochladen.",
|
||||
subtext: `${imagesToUpload.length} Bilder werden hochgeladen, bitte haben sie Geduld.`,
|
||||
timeout: 0,
|
||||
type: "info",
|
||||
});
|
||||
for (let i = 0; i < imagesToUpload.length; i++) {
|
||||
const image = imagesToUpload[i];
|
||||
|
||||
try {
|
||||
if (image.update) {
|
||||
await client.v1.bilder.update.mutate({
|
||||
uid: image.uid as string,
|
||||
base64: image.base64,
|
||||
kategorie: image.kategorie as Enums.BilderKategorie
|
||||
})
|
||||
} else {
|
||||
const response = await client.v1.bilder.upload.mutate({
|
||||
base64: image.base64,
|
||||
kategorie: image.kategorie as Enums.BilderKategorie,
|
||||
gebaeude_uid
|
||||
})
|
||||
try {
|
||||
if (image.update) {
|
||||
await api.bilder._uid.PATCH.fetch({
|
||||
base64: image.base64,
|
||||
kategorie: image.kategorie as Enums.BilderKategorie,
|
||||
}, {
|
||||
params: {
|
||||
uid: image.uid as string,
|
||||
},
|
||||
headers: {
|
||||
"Authorization": `Bearer ${Cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)}`
|
||||
}
|
||||
});
|
||||
} else {
|
||||
const response = await api.objekt._uid.bilder.PUT.fetch({
|
||||
base64: image.base64,
|
||||
kategorie: image.kategorie as Enums.BilderKategorie
|
||||
}, {
|
||||
params: {
|
||||
uid: gebaeude_uid
|
||||
},
|
||||
headers: {
|
||||
"Authorization": `Bearer ${Cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)}`
|
||||
}
|
||||
});
|
||||
|
||||
image.uid = response.uid
|
||||
}
|
||||
image.uid = response.uid;
|
||||
}
|
||||
|
||||
updateNotification(notification, {
|
||||
dismissable: true,
|
||||
message: "Bild hochgeladen.",
|
||||
subtext: `${i + 1}/${imagesToUpload.length} Bildern wurden erfolgreich hochgeladen.`,
|
||||
timeout: 3000
|
||||
})
|
||||
} catch (e) {
|
||||
updateNotification(notification, {
|
||||
dismissable: true,
|
||||
message: "Bild konnte nicht hochgeladen werden.",
|
||||
subtext: `Eines ihrer Bilder konnte nicht hochgeladen werden. Wir haben bereits ein Ticket erstellt und melden uns so schnell wie möglich bei ihnen.`,
|
||||
timeout: 15000,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
}
|
||||
updateNotification(notification, {
|
||||
dismissable: true,
|
||||
message: "Bild hochgeladen.",
|
||||
subtext: `${i + 1}/${
|
||||
imagesToUpload.length
|
||||
} Bildern wurden erfolgreich hochgeladen.`,
|
||||
timeout: 3000,
|
||||
});
|
||||
} catch (e) {
|
||||
updateNotification(notification, {
|
||||
dismissable: true,
|
||||
message: "Bild konnte nicht hochgeladen werden.",
|
||||
subtext: `Eines ihrer Bilder konnte nicht hochgeladen werden. Wir haben bereits ein Ticket erstellt und melden uns so schnell wie möglich bei ihnen.`,
|
||||
timeout: 15000,
|
||||
type: "error",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return images;
|
||||
return images;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { dialogs } from "../../../svelte-dialogs.config";
|
||||
import { addNotification } from "#components/Notifications/shared";
|
||||
import { client } from "src/trpc";
|
||||
import { dialogs } from "../../../svelte-dialogs.config.js";
|
||||
import { addNotification } from "#components/Notifications/shared.js";
|
||||
import { api } from "astro-typesafe-api/client";
|
||||
|
||||
export async function spawnSignupPrompt() {
|
||||
const result = await dialogs.prompt(
|
||||
@@ -46,7 +46,7 @@ export async function spawnSignupPrompt() {
|
||||
const [vorname, name, email, passwort] = result;
|
||||
|
||||
try {
|
||||
const response = await client.v1.benutzer.erstellen.mutate({
|
||||
const response = await api.user.PUT.fetch({
|
||||
email,
|
||||
passwort,
|
||||
vorname,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { AppRouter } from "@ibcornelsen/api";
|
||||
import { inferProcedureInput } from "@trpc/server";
|
||||
import { client } from "src/trpc";
|
||||
import { OmitKeys, TicketClient } from "#components/Ausweis/types.js";
|
||||
import { api } from "astro-typesafe-api/client";
|
||||
|
||||
export async function createTicket(info: inferProcedureInput<AppRouter["v1"]["tickets"]["erstellen"]>) {
|
||||
return await client.v1.tickets.erstellen.mutate(info)
|
||||
export async function createTicket(info: OmitKeys<TicketClient, "created_at" | "deleted_at" | "prioritaet" | "updated_at" | "status" | "uid">) {
|
||||
return await api.ticket.PUT.fetch(info)
|
||||
}
|
||||
@@ -5,12 +5,14 @@ import {
|
||||
Benutzer,
|
||||
GebaeudeBilder,
|
||||
Objekt,
|
||||
Rechnung,
|
||||
Tickets,
|
||||
VerbrauchsausweisGewerbe,
|
||||
VerbrauchsausweisWohnen,
|
||||
} from "@ibcornelsen/database/client";
|
||||
import { z, ZodSchema } from "zod";
|
||||
|
||||
type OmitKeys<T, K extends keyof T> = Omit<T, K>;
|
||||
export type OmitKeys<T, K extends keyof T> = Omit<T, K>;
|
||||
|
||||
export type UploadedGebaeudeBild = OmitKeys<GebaeudeBilder, "id" | "objekt_id"> & {
|
||||
base64: string
|
||||
@@ -84,9 +86,11 @@ export type AufnahmeClient = OmitKeys<
|
||||
uid_objekt: string
|
||||
};
|
||||
|
||||
export type TicketClient = OmitKeys<Tickets, "bearbeiter_id" | "benutzer_id" | "id">
|
||||
|
||||
export type BenutzerClient = OmitKeys<Benutzer, "id" | "passwort">;
|
||||
|
||||
type ZodOverlapType<T> = z.ZodType<T, z.ZodTypeDef, T>;
|
||||
export type RechnungClient = OmitKeys<Rechnung, "aufnahme_id" | "benutzer_id" | "id">
|
||||
|
||||
export function ZodOverlap<T, S = z.ZodType<T, z.ZodTypeDef, T>>(arg: S): S {
|
||||
return arg;
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
<script lang="ts">
|
||||
import {
|
||||
AufnahmeClient,
|
||||
ObjektClient,
|
||||
UploadedGebaeudeBild,
|
||||
VerbrauchsausweisWohnenClient,
|
||||
} from "#components/Ausweis/types.js";
|
||||
import moment from "moment";
|
||||
@@ -12,9 +15,14 @@
|
||||
QuestionMarkCircled,
|
||||
} from "radix-svelte-icons";
|
||||
import { endEnergieVerbrauchVerbrauchsausweis_2016 } from "#lib/Berechnungen/VerbrauchsausweisWohnen/VerbrauchsausweisWohnen_2016.js";
|
||||
import { client } from "src/trpc.js";
|
||||
import { api } from "astro-typesafe-api/client";
|
||||
import Cookies from "js-cookie";
|
||||
import { API_ACCESS_TOKEN_COOKIE_NAME } from "#lib/constants.js";
|
||||
|
||||
export let ausweis: VerbrauchsausweisWohnenClient;
|
||||
export let aufnahme: AufnahmeClient;
|
||||
export let bilder: UploadedGebaeudeBild[];
|
||||
export let objekt: ObjektClient;
|
||||
export let progress: number;
|
||||
|
||||
async function ausweisStornieren() {
|
||||
@@ -33,11 +41,16 @@
|
||||
});
|
||||
|
||||
if (result === true) {
|
||||
await client.v1.verbrauchsausweisWohnen.stornieren.mutate({
|
||||
uid: ausweis.uid
|
||||
await api["verbrauchsausweis-wohnen"]._uid.DELETE.fetch(undefined, {
|
||||
params: {
|
||||
uid: ausweis.uid
|
||||
},
|
||||
headers: {
|
||||
Authorization: `Bearer ${Cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)}`
|
||||
}
|
||||
})
|
||||
|
||||
ausweis.aufnahme.storniert = true;
|
||||
aufnahme.storniert = true;
|
||||
ausweis = ausweis;
|
||||
|
||||
dialogs.alert({
|
||||
@@ -58,14 +71,14 @@
|
||||
</script>
|
||||
|
||||
<div class="card lg:card-side bg-base-200 card-bordered border-base-300">
|
||||
{#if ausweis.aufnahme.storniert}
|
||||
{#if aufnahme.storniert}
|
||||
<div class="absolute top-0 left-0 w-full h-full bg-[rgba(0,0,0,0.7)] z-[5] rounded-lg select-none">
|
||||
<h1 class="absolute -rotate-[25deg] text-5xl md:text-7xl tracking-wide uppercase text-red-500 border-4 border-red-500 rounded-lg top-[50%] translate-y-[-50%] left-[50%] translate-x-[-50%]">Storniert</h1>
|
||||
</div>
|
||||
{/if}
|
||||
<figure class="lg:w-1/2">
|
||||
<img
|
||||
src={(ausweis.aufnahme.objekt.gebaeude_bilder && `/bilder/${ausweis.aufnahme.objekt.gebaeude_bilder[0]?.uid}.webp`) || "/images/placeholder.jpg"}
|
||||
src={(bilder.length > 0 && `/bilder/${bilder[0].uid}.webp`) || "/images/placeholder.jpg"}
|
||||
class="object-cover w-full h-full"
|
||||
alt="Gebäudebild"
|
||||
/>
|
||||
@@ -95,25 +108,25 @@
|
||||
</ul>
|
||||
</div>
|
||||
<div class="flex flex-row flex-wrap gap-2">
|
||||
{#if ausweis.aufnahme.ausweisart == "VerbrauchsausweisWohnen"}
|
||||
{#if aufnahme.ausweisart == "VerbrauchsausweisWohnen"}
|
||||
<div class="badge badge-accent font-semibold">
|
||||
Verbrauchsausweis Wohnen
|
||||
</div>
|
||||
{:else if ausweis.aufnahme.ausweisart == "BedarfsausweisWohnen"}
|
||||
{:else if aufnahme.ausweisart == "BedarfsausweisWohnen"}
|
||||
<div class="badge badge-accent font-semibold">
|
||||
Bedarfsausweis Wohnen
|
||||
</div>
|
||||
{:else if ausweis.aufnahme.ausweisart == "VerbrauchsausweisGewerbe"}
|
||||
{:else if aufnahme.ausweisart == "VerbrauchsausweisGewerbe"}
|
||||
<div class="badge badge-accent font-semibold">
|
||||
Verbrauchsausweis Gewerbe
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if ausweis.erledigt}
|
||||
{#if aufnahme.erledigt}
|
||||
<div class="badge badge-success font-semibold">Ausgestellt</div>
|
||||
{/if}
|
||||
</div>
|
||||
<h2 class="card-title">{ausweis.aufnahme.objekt.adresse}</h2>
|
||||
<h2 class="card-title">{objekt.adresse}</h2>
|
||||
<div class="mb-4 flex flex-row items-center gap-4">
|
||||
<progress class="progress w-full" value={progress} max="100"></progress>
|
||||
<!-- TODO: Metrics für den Fortschritt festlegen -->
|
||||
@@ -134,7 +147,7 @@
|
||||
<div class="flex flex-row justify-between">
|
||||
<span>Erstellungsdatum</span>
|
||||
<span class="font-bold text-base-content"
|
||||
>{moment(ausweis.erstellungsdatum).format(
|
||||
>{moment(aufnahme.erstellungsdatum).format(
|
||||
"DD.MM.YYYY"
|
||||
)}</span
|
||||
>
|
||||
@@ -144,16 +157,16 @@
|
||||
<span
|
||||
class="font-bold text-base-content"
|
||||
title="Gebäude / Heizung"
|
||||
>{ausweis.aufnahme.baujahr_gebaeude[0] || "N/A"} /
|
||||
{ausweis.aufnahme.baujahr_heizung[0] ||
|
||||
>{aufnahme.baujahr_gebaeude[0] || "N/A"} /
|
||||
{aufnahme.baujahr_heizung[0] ||
|
||||
"N/A"}</span
|
||||
>
|
||||
</div>
|
||||
<div class="flex flex-row justify-between">
|
||||
<span>Wohnfläche</span>
|
||||
<span class="font-bold text-base-content"
|
||||
>{ausweis.aufnahme.flaeche
|
||||
? `${ausweis.aufnahme.flaeche}m²`
|
||||
>{aufnahme.flaeche
|
||||
? `${aufnahme.flaeche}m²`
|
||||
: "N/A"}</span
|
||||
>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script lang="ts">
|
||||
import type { inferProcedureOutput } from "@trpc/server";
|
||||
import { API, api, inferOutput } from "astro-typesafe-api/client";
|
||||
|
||||
export let name: string;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { addNotification } from "#components/Notifications/shared";
|
||||
import { client } from "src/trpc";
|
||||
import { api } from "astro-typesafe-api/client";
|
||||
import { getClose } from "svelte-dialogs";
|
||||
|
||||
const close = getClose();
|
||||
@@ -8,7 +7,7 @@
|
||||
async function createTicket(e: SubmitEvent) {
|
||||
e.preventDefault();
|
||||
try {
|
||||
await client.v1.tickets.erstellen.mutate({
|
||||
await api.ticket.PUT.fetch({
|
||||
beschreibung: description,
|
||||
email: email,
|
||||
metadata: {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import moment from "moment";
|
||||
import { memoize } from "./Memoization.js";
|
||||
import { client } from "src/trpc.js";
|
||||
import { api } from "astro-typesafe-api/client"
|
||||
|
||||
export const getKlimafaktoren = memoize(async (date: Date, plz: string) => {
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
<script lang="ts">
|
||||
import { BenutzerClient, VerbrauchsausweisWohnenClient } from "#components/Ausweis/types";
|
||||
import { BenutzerClient, VerbrauchsausweisWohnenClient } from "#components/Ausweis/types.js";
|
||||
import DashboardAusweis from "#components/Dashboard/DashboardAusweis.svelte";
|
||||
import DashboardAusweisSkeleton from "#components/Dashboard/DashboardAusweisSkeleton.svelte";
|
||||
import { verbrauchsausweisWohnenCalculateFormProgress } from "#lib/VerbrauchsausweisWohnen/calculateFormProgress";
|
||||
import { client } from "src/trpc";
|
||||
import { onMount } from "svelte";
|
||||
export let user: BenutzerClient;
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import { Tabs, Tab, TabList, TabPanel } from "../../components/Tabs";
|
||||
import { dialogs } from "../../../svelte-dialogs.config";
|
||||
import { BenutzerClient } from "#components/Ausweis/types";
|
||||
import { client } from "src/trpc";
|
||||
// import { client } from "src/trpc";
|
||||
import { exclude } from "#lib/exclude";
|
||||
|
||||
export let benutzer: BenutzerClient;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { addNotification } from "@ibcornelsen/ui";
|
||||
import { client } from "src/trpc";
|
||||
import { api } from "astro-typesafe-api/client";
|
||||
|
||||
export let navigate: (target: string) => void;
|
||||
export let onRegister: (response: { email: string, name: string, vorname: string }) => void;
|
||||
@@ -12,7 +12,7 @@
|
||||
async function signUp(e: SubmitEvent) {
|
||||
e.preventDefault()
|
||||
try {
|
||||
const response = await client.v1.benutzer.erstellen.mutate({
|
||||
const response = await api.user.PUT.fetch({
|
||||
email,
|
||||
passwort,
|
||||
vorname,
|
||||
|
||||
@@ -6,20 +6,16 @@
|
||||
} from "@ibcornelsen/database/client";
|
||||
import { Enums } from "@ibcornelsen/database/client";
|
||||
import PaymentOption from "#components/PaymentOption.svelte";
|
||||
import { client } from "src/trpc";
|
||||
import type { inferProcedureInput } from "@trpc/server";
|
||||
import type { AppRouter } from "@ibcornelsen/api";
|
||||
import CheckoutItem from "#components/CheckoutItem.svelte";
|
||||
import { BenutzerClient, VerbrauchsausweisWohnenClient } from "#components/Ausweis/types";
|
||||
import { PRICES } from "#lib/constants";
|
||||
import { BenutzerClient, VerbrauchsausweisWohnenClient } from "#components/Ausweis/types.js";
|
||||
import { PRICES } from "#lib/constants.js";
|
||||
import { RechnungClient } from "#components/Ausweis/types.js";
|
||||
|
||||
export let user: BenutzerClient;
|
||||
export let ausweis:
|
||||
| VerbrauchsausweisWohnenClient;
|
||||
// TODO: überarbeiten und zu inferProcedureOutput machen
|
||||
let rechnung: inferProcedureInput<
|
||||
AppRouter["v1"]["rechnungen"]["erstellen"]
|
||||
> = {
|
||||
let rechnung: RechnungClient = {
|
||||
email: user.email,
|
||||
empfaenger: user.vorname + " " + user.name,
|
||||
strasse: user.adresse,
|
||||
@@ -65,6 +61,7 @@
|
||||
async function createPayment(e: SubmitEvent) {
|
||||
e.preventDefault();
|
||||
|
||||
// TODO
|
||||
const response = await client.v1.rechnungen.erstellen.mutate({
|
||||
...rechnung,
|
||||
ausweisart: Enums.Ausweisart.VerbrauchsausweisWohnen,
|
||||
|
||||
@@ -2,8 +2,7 @@ import { z } from "zod";
|
||||
import moment from "moment";
|
||||
import { prisma } from "@ibcornelsen/database/server";
|
||||
import { encodeToken } from "../../../lib/auth/token.js";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { 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({
|
||||
@@ -35,7 +34,7 @@ export const GET = defineApiRoute({
|
||||
});
|
||||
|
||||
if (!response) {
|
||||
throw new TRPCError({
|
||||
throw new APIError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Der gegebene refresh token ist nicht gültig.",
|
||||
});
|
||||
@@ -48,7 +47,7 @@ export const GET = defineApiRoute({
|
||||
id: response.id,
|
||||
},
|
||||
});
|
||||
throw new TRPCError({
|
||||
throw new APIError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Der gegebene refresh token ist nicht gültig.",
|
||||
});
|
||||
@@ -62,7 +61,7 @@ export const GET = defineApiRoute({
|
||||
// id: response.id
|
||||
// }
|
||||
// })
|
||||
// throw new TRPCError({ code: "BAD_REQUEST", message: "Der gegebene refresh token wurde von einer anderen IP-Adresse ausgestellt, aus Sicherheitsgründen haben wir uns entschieden diesen zu invalidieren." });
|
||||
// throw new APIError({ code: "BAD_REQUEST", message: "Der gegebene refresh token wurde von einer anderen IP-Adresse ausgestellt, aus Sicherheitsgründen haben wir uns entschieden diesen zu invalidieren." });
|
||||
// }
|
||||
|
||||
const user = await prisma.benutzer.findUnique({
|
||||
@@ -78,7 +77,7 @@ export const GET = defineApiRoute({
|
||||
id: response.id,
|
||||
},
|
||||
});
|
||||
throw new TRPCError({
|
||||
throw new APIError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Der gegebene refresh token ist nicht mehr gültig.",
|
||||
});
|
||||
|
||||
84
src/pages/api/bilder/[uid].ts
Normal file
84
src/pages/api/bilder/[uid].ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import { authorizationMiddleware } from "#lib/middleware/authorization.js";
|
||||
import { GebaeudeBilderSchema, prisma } from "@ibcornelsen/database/server";
|
||||
import { APIError, defineApiRoute } from "astro-typesafe-api/server";
|
||||
import { z } from "zod";
|
||||
import isBase64 from "is-base64";
|
||||
import { fileURLToPath } from "url";
|
||||
import { writeFileSync } from "fs";
|
||||
import { UUidWithPrefix } from "#components/Ausweis/types.js";
|
||||
|
||||
export const PATCH = defineApiRoute({
|
||||
input: GebaeudeBilderSchema.pick({
|
||||
kategorie: true,
|
||||
}).merge(z.object({
|
||||
base64: z.string()
|
||||
})),
|
||||
output: z.void(),
|
||||
middleware: authorizationMiddleware,
|
||||
async fetch(input, ctx, user) {
|
||||
if (!UUidWithPrefix.safeParse(ctx.params.uid).success) {
|
||||
throw new APIError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "UID konnte nicht verifiziert werden."
|
||||
})
|
||||
}
|
||||
|
||||
const image = await prisma.gebaeudeBilder.findUnique({
|
||||
where: {
|
||||
uid: ctx.params.uid,
|
||||
objekt: {
|
||||
benutzer_id: user.id
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (!image) {
|
||||
throw new APIError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Bild existiert nicht oder gehört einem anderen Benutzer."
|
||||
})
|
||||
}
|
||||
|
||||
const base64 = input.base64;
|
||||
|
||||
if (!isBase64(base64, { mimeRequired: true })) {
|
||||
throw new APIError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Das Bild ist nicht base64.",
|
||||
});
|
||||
}
|
||||
|
||||
const dataWithoutPrefix = base64.replace(
|
||||
/^data:image\/\w+;base64,/,
|
||||
""
|
||||
);
|
||||
const buffer = Buffer.from(dataWithoutPrefix, "base64");
|
||||
|
||||
if (input.kategorie !== image.kategorie) {
|
||||
await prisma.gebaeudeBilder.update({
|
||||
where: {
|
||||
id: image.id
|
||||
},
|
||||
data: {
|
||||
kategorie: input.kategorie
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const filePath = fileURLToPath(new URL(`../../../../../persistent/images/${image.uid}.webp`, import.meta.url));
|
||||
|
||||
try {
|
||||
// Wir optimieren das Bild und konvertieren es in WebP
|
||||
// TODO: Sharp scheint nicht zu funktionieren, wir müssen das nochmal testen
|
||||
// const optimizedBuffer = await sharp(buffer).webp({ quality: 80 }).toArray();
|
||||
|
||||
writeFileSync(filePath, buffer)
|
||||
} catch(e) {
|
||||
// Und geben einen Fehler zurück
|
||||
throw new APIError({
|
||||
code: "INTERNAL_SERVER_ERROR",
|
||||
message: "Bild konnte nicht gespeichert werden.",
|
||||
});
|
||||
}
|
||||
},
|
||||
})
|
||||
@@ -1,6 +1,6 @@
|
||||
import { OptionalNullable, UUidWithPrefix, VerbrauchsausweisWohnenClient, ZodOverlap } from "#components/Ausweis/types.js";
|
||||
import { exclude } from "#lib/exclude.js";
|
||||
import { authorizationMiddleware } from "#lib/middleware/authorization.js";
|
||||
import { authorizationHeaders, authorizationMiddleware } from "#lib/middleware/authorization.js";
|
||||
import { prisma, VerbrauchsausweisWohnenSchema } from "@ibcornelsen/database/server";
|
||||
import { APIError, defineApiRoute } from "astro-typesafe-api/server";
|
||||
import { z } from "zod";
|
||||
@@ -43,6 +43,101 @@ export const PATCH = defineApiRoute({
|
||||
},
|
||||
})
|
||||
|
||||
export const DELETE = defineApiRoute({
|
||||
meta: {
|
||||
description: "Storniert einen Ausweis"
|
||||
},
|
||||
headers: authorizationHeaders,
|
||||
middleware: authorizationMiddleware,
|
||||
async fetch(input, ctx, user) {
|
||||
const { uid } = ctx.params;
|
||||
|
||||
if (!UUidWithPrefix.safeParse(uid).success) {
|
||||
throw new APIError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "UID konnte nicht verifiziert werden."
|
||||
})
|
||||
}
|
||||
|
||||
// Wir holen uns den Verbrauchsausweis
|
||||
// Dieser MUSS mit dem Nutzer verknüpft sein.
|
||||
const ausweis = await prisma.verbrauchsausweisWohnen.findUnique({
|
||||
where: {
|
||||
uid,
|
||||
},
|
||||
include: {
|
||||
aufnahme: {
|
||||
select: {
|
||||
storniert: true
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!ausweis) {
|
||||
// Falls wir den Ausweis nicht finden können, werfen wir einen Fehler
|
||||
throw new APIError({
|
||||
code: "NOT_FOUND",
|
||||
message: "Ausweis konnte nicht gefunden werden.",
|
||||
});
|
||||
}
|
||||
|
||||
// Wir dürfen den Ausweis nur stornieren, wenn er noch nicht ausgestellt wurde
|
||||
// Außerdem müssen wir schauen, ob wir Admin oder der Besitzer des Ausweises sind.
|
||||
if ((ausweis.benutzer_id !== user.id) && user.rolle !== "ADMIN") {
|
||||
// Falls der Ausweis nicht dem Nutzer gehört, werfen wir einen Fehler
|
||||
throw new APIError({
|
||||
code: "FORBIDDEN",
|
||||
message: "Ausweis gehört nicht dem Nutzer.",
|
||||
});
|
||||
}
|
||||
|
||||
// if (ausweis.erledigt) {
|
||||
// // Falls der Ausweis bereits ausgestellt wurde, werfen wir einen Fehler
|
||||
// throw new TRPCError({
|
||||
// code: "BAD_REQUEST",
|
||||
// message: "Ausweis wurde bereits ausgestellt.",
|
||||
// });
|
||||
// }
|
||||
|
||||
if (ausweis.aufnahme.storniert) {
|
||||
// Falls der Ausweis bereits storniert ist, werfen wir einen Fehler
|
||||
throw new APIError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Ausweis wurde bereits storniert.",
|
||||
});
|
||||
}
|
||||
|
||||
await prisma.aufnahme.update({
|
||||
where: {
|
||||
id: ausweis.aufnahme_id
|
||||
},
|
||||
data: {
|
||||
storniert: true
|
||||
}
|
||||
})
|
||||
|
||||
// Wir erstellen ein Event, dass der Ausweis storniert wurde
|
||||
// Dann können wir das in der Historie anzeigen
|
||||
await prisma.event.create({
|
||||
data: {
|
||||
title: "Ausweis storniert",
|
||||
description: ((user.rolle === "ADMIN") && (ausweis.benutzer_id !== user.id)) ? "Ausweis wurde von einem Administrator storniert." : "Ausweis wurde vom Besitzer storniert.",
|
||||
benutzer: {
|
||||
connect: {
|
||||
id: user.id
|
||||
}
|
||||
},
|
||||
aufnahme: {
|
||||
connect: {
|
||||
id: ausweis.aufnahme_id
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
export const GET = defineApiRoute({
|
||||
meta: {
|
||||
description: "Gibt ein spezifisches Gebäude des Benutzers zurück.",
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { Enums } from "@ibcornelsen/database/client";
|
||||
import { test, describe, expect } from "bun:test";
|
||||
import {faker} from "@faker-js/faker";
|
||||
import { client } from "src/trpc";
|
||||
import { createTRPCProxyClient, httpBatchLink } from '@trpc/client';
|
||||
import type { AppRouter } from '@ibcornelsen/api';
|
||||
// import { client } from "src/trpc";
|
||||
import { Buffer } from 'buffer';
|
||||
|
||||
describe("Bilder hochladen", async () => {
|
||||
|
||||
Reference in New Issue
Block a user