WIP on dev-moritz
This commit is contained in:
46
src/pages/api/aufnahme/[id].ts
Normal file
46
src/pages/api/aufnahme/[id].ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { authorizationMiddleware } from "#lib/middleware/authorization.js";
|
||||
import { prisma } from "@ibcornelsen/database/server";
|
||||
import { APIError, defineApiRoute } from "astro-typesafe-api/server";
|
||||
|
||||
export const PATCH = defineApiRoute({
|
||||
fetch(input, context) {},
|
||||
});
|
||||
|
||||
export const GET = defineApiRoute({
|
||||
meta: {
|
||||
description: "Gibt eine spezifische Aufnhame eines Objektes des Benutzers zurück.",
|
||||
tags: ["Aufnahme"],
|
||||
headers: {
|
||||
"Authorization": {
|
||||
description: "Ein gültiger Authentifizierungstoken",
|
||||
required: true,
|
||||
allowEmptyValue: false,
|
||||
examples: {
|
||||
Bearer: {
|
||||
value: "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
middleware: authorizationMiddleware,
|
||||
async fetch(input, context, user) {
|
||||
const { uid } = context.params;
|
||||
|
||||
const aufnahme = await prisma.aufnahme.findUnique({
|
||||
where: {
|
||||
uid,
|
||||
benutzer_id: user.id
|
||||
},
|
||||
});
|
||||
|
||||
if (!aufnahme) {
|
||||
throw new APIError({
|
||||
code: "NOT_FOUND",
|
||||
message: "Aufnahme mit dieser UID existiert nicht oder gehört nicht dem aktuellen Benutzer."
|
||||
})
|
||||
}
|
||||
|
||||
return aufnahme
|
||||
},
|
||||
});
|
||||
0
src/pages/api/aufnahme/index.ts
Normal file
0
src/pages/api/aufnahme/index.ts
Normal file
125
src/pages/api/auth/access-token.ts
Normal file
125
src/pages/api/auth/access-token.ts
Normal file
@@ -0,0 +1,125 @@
|
||||
import { z } from "zod";
|
||||
import moment from "moment";
|
||||
import { prisma } from "@ibcornelsen/database/server";
|
||||
import { TokenType, encodeToken } from "../../../lib/auth/token.js";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { defineApiRoute } from "astro-typesafe-api/server";
|
||||
|
||||
export const tRPC_V1_BenutzerGetAccessTokenProcedure = defineApiRoute({
|
||||
meta: {
|
||||
description:
|
||||
"Erstellt, basierend auf einem existierenden und gültigen Refresh Tokens, einen neuen Access Token, welcher zur Authentifizierung genutzt werden kann. Der resultierende Access Token ist nur 2 Tage gültig und muss danach neu generiert werden. Diese Funktion gibt ebenfalls einen neuen Refresh Token zurück, der alte wird dadurch invalidiert.",
|
||||
tags: ["Benutzer"],
|
||||
summary: "Access Token anfragen.",
|
||||
},
|
||||
input: z.object({
|
||||
refreshToken: z.string(),
|
||||
}),
|
||||
|
||||
output: z.object({
|
||||
accessToken: z.string(),
|
||||
accessTokenExpiry: z.number(),
|
||||
refreshToken: z.string(),
|
||||
refreshTokenExpiry: z.number(),
|
||||
}),
|
||||
|
||||
async fetch(input, ctx) {
|
||||
/**
|
||||
* Wir benutzen rolling refresh tokens, also löschen wir den alten Token und stellen einen neuen aus,
|
||||
* damit dieser nicht geklaut werden kann.
|
||||
*/
|
||||
const response = await prisma.refreshTokens.findUnique({
|
||||
where: {
|
||||
token: input.refreshToken,
|
||||
},
|
||||
});
|
||||
|
||||
if (!response) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Der gegebene refresh token ist nicht gültig.",
|
||||
});
|
||||
}
|
||||
|
||||
if (response.expiry < new Date()) {
|
||||
// Falls der Token abgelaufen ist, müssen wir ihn löschen.
|
||||
await prisma.refreshTokens.delete({
|
||||
where: {
|
||||
id: response.id,
|
||||
},
|
||||
});
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Der gegebene refresh token ist nicht gültig.",
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: Das können wir später implementieren, falls wir das wirklich brauchen.
|
||||
// if (response.ip !== opts.ctx.ip) {
|
||||
// // Falls der Token nicht von der selben IP Adresse kommt, müssen wir ihn löschen.
|
||||
// await prisma.refreshTokens.delete({
|
||||
// where: {
|
||||
// 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." });
|
||||
// }
|
||||
|
||||
const user = await prisma.benutzer.findUnique({
|
||||
where: {
|
||||
id: response.benutzer_id,
|
||||
},
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
// Falls der Nutzer nicht mehr existiert müssen wir den Refresh Token invalidieren.
|
||||
await prisma.refreshTokens.delete({
|
||||
where: {
|
||||
id: response.id,
|
||||
},
|
||||
});
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Der gegebene refresh token ist nicht mehr gültig.",
|
||||
});
|
||||
}
|
||||
|
||||
// Wir löschen den alten Token.
|
||||
await prisma.refreshTokens.delete({
|
||||
where: {
|
||||
id: response.id,
|
||||
},
|
||||
});
|
||||
|
||||
const refreshTokenExpiry = moment().add(30, "days");
|
||||
const refreshToken = encodeToken({
|
||||
uid: user.uid,
|
||||
typ: TokenType.Refresh,
|
||||
exp: refreshTokenExpiry.unix(),
|
||||
});
|
||||
|
||||
// Und erstellen einen neuen
|
||||
await prisma.refreshTokens.create({
|
||||
data: {
|
||||
benutzer_id: user.id,
|
||||
expiry: refreshTokenExpiry.toDate(),
|
||||
ip: ctx.clientAddress ?? "",
|
||||
token: refreshToken,
|
||||
},
|
||||
});
|
||||
|
||||
const accessTokenExpiry = moment().add(2, "days").unix();
|
||||
const accessToken = encodeToken({
|
||||
uid: user.uid,
|
||||
typ: TokenType.Access,
|
||||
exp: accessTokenExpiry,
|
||||
});
|
||||
|
||||
return {
|
||||
accessToken,
|
||||
accessTokenExpiry: accessTokenExpiry,
|
||||
refreshToken,
|
||||
refreshTokenExpiry: refreshTokenExpiry.unix(),
|
||||
};
|
||||
},
|
||||
});
|
||||
82
src/pages/api/auth/refresh-token.ts
Normal file
82
src/pages/api/auth/refresh-token.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import { z } from "zod";
|
||||
import moment from "moment";
|
||||
import { prisma } from "@ibcornelsen/database/server";
|
||||
import { TokenType, encodeToken } from "../../../lib/auth/token.js";
|
||||
import { hashPassword, validatePassword } from "../../../lib/password.js";
|
||||
import { APIError, defineApiRoute } from "astro-typesafe-api/server";
|
||||
|
||||
export const GET = defineApiRoute({
|
||||
meta: {
|
||||
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"],
|
||||
summary: "Refresh Token anfragen.",
|
||||
},
|
||||
input: z.object({
|
||||
email: z.string().email(),
|
||||
passwort: z.string().min(8).max(100),
|
||||
}),
|
||||
output: z.object({
|
||||
uid: z.string().uuid(),
|
||||
accessToken: z.string(),
|
||||
refreshToken: z.string(),
|
||||
refreshTokenBase64: z.string(),
|
||||
accessTokenBase64: z.string(),
|
||||
exp: z.number(),
|
||||
}),
|
||||
async fetch(input, ctx) {
|
||||
console.log(input);
|
||||
|
||||
// Falls der Nutzer nicht existiert, wird eine Fehlermeldung zurückgegeben.
|
||||
const user = await prisma.benutzer.findUnique({
|
||||
where: {
|
||||
email: input.email,
|
||||
},
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
throw new APIError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Benutzer konnte nicht gefunden werden.",
|
||||
});
|
||||
}
|
||||
|
||||
// Falls das Passwort nicht stimmt, wird eine Fehlermeldung zurückgegeben.
|
||||
if (!validatePassword(user.passwort, input.passwort)) {
|
||||
throw new APIError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Benutzer konnte nicht gefunden werden.",
|
||||
});
|
||||
}
|
||||
|
||||
const refreshTokenExpiry = moment().add(30, "days");
|
||||
const accessToken = encodeToken({
|
||||
uid: user.uid,
|
||||
typ: TokenType.Access,
|
||||
exp: moment().add(30, "minutes").valueOf(),
|
||||
});
|
||||
const refreshToken = encodeToken({
|
||||
uid: user.uid,
|
||||
typ: TokenType.Refresh,
|
||||
exp: refreshTokenExpiry.valueOf(),
|
||||
});
|
||||
|
||||
const { id } = await prisma.refreshTokens.create({
|
||||
data: {
|
||||
token: refreshToken,
|
||||
benutzer_id: user.id,
|
||||
ip: ctx.clientAddress ?? "",
|
||||
expiry: refreshTokenExpiry.toDate(),
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
uid: user.uid,
|
||||
accessToken,
|
||||
refreshToken,
|
||||
refreshTokenBase64: Buffer.from(refreshToken).toString("base64"),
|
||||
accessTokenBase64: Buffer.from(accessToken).toString("base64"),
|
||||
exp: refreshTokenExpiry.valueOf(),
|
||||
};
|
||||
},
|
||||
});
|
||||
0
src/pages/api/bedarfsausweis-wohnen/index.ts
Normal file
0
src/pages/api/bedarfsausweis-wohnen/index.ts
Normal file
46
src/pages/api/objekt/[id].ts
Normal file
46
src/pages/api/objekt/[id].ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { authorizationMiddleware } from "#lib/middleware/authorization.js";
|
||||
import { prisma } from "@ibcornelsen/database/server";
|
||||
import { APIError, defineApiRoute } from "astro-typesafe-api/server";
|
||||
|
||||
export const PATCH = defineApiRoute({
|
||||
fetch(input, context) {},
|
||||
});
|
||||
|
||||
export const GET = defineApiRoute({
|
||||
meta: {
|
||||
description: "Gibt ein spezifisches Gebäude des Benutzers zurück.",
|
||||
tags: ["Gebäude"],
|
||||
headers: {
|
||||
"Authorization": {
|
||||
description: "Ein gültiger Authentifizierungstoken",
|
||||
required: true,
|
||||
allowEmptyValue: false,
|
||||
examples: {
|
||||
Bearer: {
|
||||
value: "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
middleware: authorizationMiddleware,
|
||||
async fetch(input, context, user) {
|
||||
const { uid } = context.params;
|
||||
|
||||
const objekt = await prisma.objekt.findUnique({
|
||||
where: {
|
||||
uid,
|
||||
benutzer_id: user.id
|
||||
},
|
||||
});
|
||||
|
||||
if (!objekt) {
|
||||
throw new APIError({
|
||||
code: "NOT_FOUND",
|
||||
message: "Objekt mit dieser UID existiert nicht oder gehört nicht dem aktuellen Benutzer."
|
||||
})
|
||||
}
|
||||
|
||||
return objekt
|
||||
},
|
||||
});
|
||||
30
src/pages/api/objekt/index.ts
Normal file
30
src/pages/api/objekt/index.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { authorizationMiddleware } from "#lib/middleware/authorization.js";
|
||||
import { ObjektSchema, prisma } from "@ibcornelsen/database/server";
|
||||
import { defineApiRoute } from "astro-typesafe-api/server";
|
||||
import { z } from "zod";
|
||||
|
||||
export const POST = defineApiRoute({
|
||||
fetch(input, context) {
|
||||
|
||||
},
|
||||
})
|
||||
|
||||
export const GET = defineApiRoute({
|
||||
input: z.object({
|
||||
limit: z.number()
|
||||
}),
|
||||
output: z.array(ObjektSchema),
|
||||
middleware: authorizationMiddleware,
|
||||
async fetch(input, context, transfer) {
|
||||
const objekte = await prisma.objekt.findMany({
|
||||
take: input.limit,
|
||||
where: {
|
||||
benutzer: {
|
||||
id: transfer.id
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return objekte
|
||||
},
|
||||
})
|
||||
23
src/pages/api/user/self.ts
Normal file
23
src/pages/api/user/self.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import { BenutzerSchema } from "@ibcornelsen/database/server";
|
||||
import { z } from "zod";
|
||||
import { defineApiRoute } from "astro-typesafe-api/server";
|
||||
import { authorizationMiddleware } from "#lib/middleware/authorization.js";
|
||||
|
||||
export const GET = defineApiRoute({
|
||||
meta: {
|
||||
description:
|
||||
"Gibt die Daten des momentan eingeloggten Benutzers zurück. Falls der Authorization Key invalid ist wird stattdessen null zurückgegeben.",
|
||||
summary: "Gibt die Daten des eingeloggten Benutzers zurück.",
|
||||
tags: ["Benutzer"],
|
||||
},
|
||||
|
||||
input: z.void(),
|
||||
output: BenutzerSchema.omit({
|
||||
passwort: true,
|
||||
id: true,
|
||||
}).or(z.null()),
|
||||
middleware: authorizationMiddleware,
|
||||
async fetch(input, ctx, transfer) {
|
||||
return transfer;
|
||||
},
|
||||
});
|
||||
0
src/pages/api/verbrauchsausweis-gewerbe/index.ts
Normal file
0
src/pages/api/verbrauchsausweis-gewerbe/index.ts
Normal file
71
src/pages/api/verbrauchsausweis-wohnen/[id].ts
Normal file
71
src/pages/api/verbrauchsausweis-wohnen/[id].ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { authorizationMiddleware } from "#lib/middleware/authorization.js";
|
||||
import { prisma } from "@ibcornelsen/database/server";
|
||||
import { APIError, defineApiRoute } from "astro-typesafe-api/server";
|
||||
|
||||
export const PATCH = defineApiRoute({
|
||||
fetch(input, context) {},
|
||||
});
|
||||
|
||||
export const GET = defineApiRoute({
|
||||
meta: {
|
||||
description: "Gibt ein spezifisches Gebäude des Benutzers zurück.",
|
||||
tags: ["Gebäude"],
|
||||
headers: {
|
||||
"Authorization": {
|
||||
description: "Ein gültiger Authentifizierungstoken",
|
||||
required: true,
|
||||
allowEmptyValue: false,
|
||||
examples: {
|
||||
Bearer: {
|
||||
value: "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
middleware: authorizationMiddleware,
|
||||
async fetch(input, context, user) {
|
||||
const { uid } = context.params;
|
||||
|
||||
const ausweis = await prisma.verbrauchsausweisWohnen.findUnique({
|
||||
where: {
|
||||
uid: input.uid,
|
||||
},
|
||||
include: {
|
||||
benutzer: true,
|
||||
aufnahme: {
|
||||
include: {
|
||||
objekt: {
|
||||
include: {
|
||||
gebaeude_bilder: true
|
||||
},
|
||||
},
|
||||
rechnungen: true,
|
||||
events: {
|
||||
include: {
|
||||
benutzer: {
|
||||
select: {
|
||||
uid: true
|
||||
}
|
||||
}
|
||||
},
|
||||
orderBy: {
|
||||
date: "asc"
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
if (!ausweis || (ausweis.benutzer_id !== null && ausweis.benutzer_id !== user.id)) {
|
||||
// Falls wir den Ausweis nicht finden können, werfen wir einen Fehler
|
||||
throw new APIError({
|
||||
code: "NOT_FOUND",
|
||||
message: "Ausweis konnte nicht gefunden werden.",
|
||||
});
|
||||
}
|
||||
|
||||
return ausweis
|
||||
},
|
||||
});
|
||||
71
src/pages/api/verbrauchsausweis-wohnen/index.ts
Normal file
71
src/pages/api/verbrauchsausweis-wohnen/index.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { authorizationMiddleware } from "#lib/middleware/authorization.js";
|
||||
import { prisma } from "@ibcornelsen/database/server";
|
||||
import { APIError, defineApiRoute } from "astro-typesafe-api/server";
|
||||
|
||||
export const PATCH = defineApiRoute({
|
||||
fetch(input, context) {},
|
||||
});
|
||||
|
||||
export const GET = defineApiRoute({
|
||||
meta: {
|
||||
description: "Gibt ein spezifisches Gebäude des Benutzers zurück.",
|
||||
tags: ["Gebäude"],
|
||||
headers: {
|
||||
"Authorization": {
|
||||
description: "Ein gültiger Authentifizierungstoken",
|
||||
required: true,
|
||||
allowEmptyValue: false,
|
||||
examples: {
|
||||
Bearer: {
|
||||
value: "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
middleware: authorizationMiddleware,
|
||||
async fetch(input, context, user) {
|
||||
const { uid } = context.params;
|
||||
|
||||
const ausweis = await prisma.verbrauchsausweisWohnen.findUnique({
|
||||
where: {
|
||||
uid,
|
||||
},
|
||||
include: {
|
||||
benutzer: true,
|
||||
aufnahme: {
|
||||
include: {
|
||||
objekt: {
|
||||
include: {
|
||||
gebaeude_bilder: true
|
||||
},
|
||||
},
|
||||
rechnungen: true,
|
||||
events: {
|
||||
include: {
|
||||
benutzer: {
|
||||
select: {
|
||||
uid: true
|
||||
}
|
||||
}
|
||||
},
|
||||
orderBy: {
|
||||
date: "asc"
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
if (!ausweis || (ausweis.benutzer_id !== null && ausweis.benutzer_id !== user.id)) {
|
||||
// Falls wir den Ausweis nicht finden können, werfen wir einen Fehler
|
||||
throw new APIError({
|
||||
code: "NOT_FOUND",
|
||||
message: "Ausweis konnte nicht gefunden werden.",
|
||||
});
|
||||
}
|
||||
|
||||
return ausweis
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user