This commit is contained in:
2025-08-31 18:29:29 +02:00
parent aeaba64865
commit 97a095b422
21 changed files with 1990 additions and 0 deletions

54
src/plugins/auth.ts Normal file
View File

@@ -0,0 +1,54 @@
import { FastifyInstance } from "fastify";
import fp from "fastify-plugin";
import jwt from "jsonwebtoken";
export default fp(async (server: FastifyInstance) => {
server.addHook("preHandler", async (req, reply) => {
try {
// 1) Token aus Cookie lesen
const cookieToken = req.cookies?.token
// 2) Token aus Header lesen (falls Cookie nicht da ist)
const authHeader = req.headers.authorization
const headerToken = authHeader?.startsWith("Bearer ")
? authHeader.slice(7)
: null
let token = null
if(headerToken !== null && headerToken.length > 10){
token = headerToken
} else if(cookieToken ){
token = cookieToken
}
if (!token) {
return // keine Exception → Route darf z. B. public sein
}
const payload = jwt.verify(token, process.env.JWT_SECRET!) as {
user_id: string;
email: string;
tenant_id?: string;
role?: string;
};
(req as any).user = payload;
} catch (err) {
return reply.code(401).send({ error: "Invalid or expired token" });
}
});
});
declare module "fastify" {
interface FastifyRequest {
user?: {
user_id: string;
email: string;
tenant_id?: string;
role?: string;
};
}
}

16
src/plugins/cors.ts Normal file
View File

@@ -0,0 +1,16 @@
import { FastifyInstance } from "fastify";
import fp from "fastify-plugin";
import cors from "@fastify/cors";
export default fp(async (server: FastifyInstance) => {
await server.register(cors, {
origin: [
"http://localhost:3000", // dein Nuxt-Frontend
"http://127.0.0.1:3000", // dein Nuxt-Frontend
],
methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
allowedHeaders: ["Content-Type", "Authorization", "Context"],
exposedHeaders: ["Authorization"], // optional, falls du ihn auch auslesen willst
credentials: true, // wichtig, falls du Cookies nutzt
});
});

19
src/plugins/supabase.ts Normal file
View File

@@ -0,0 +1,19 @@
import { FastifyInstance } from "fastify";
import fp from "fastify-plugin";
import { createClient, SupabaseClient } from "@supabase/supabase-js";
export default fp(async (server: FastifyInstance) => {
const supabaseUrl = process.env.SUPABASE_URL || "https://uwppvcxflrcsibuzsbil.supabase.co";
const supabaseServiceKey = process.env.SUPABASE_SERVICE_ROLE_KEY || "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InV3cHB2Y3hmbHJjc2lidXpzYmlsIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTcwMDkzODE5NCwiZXhwIjoyMDE2NTE0MTk0fQ.6hOkD1J8XBkVJUm-swv0ngLQ74xrEYr28EEbo0rUrts";
const supabase: SupabaseClient = createClient(supabaseUrl, supabaseServiceKey);
// Fastify um supabase erweitern
server.decorate("supabase", supabase);
});
declare module "fastify" {
interface FastifyInstance {
supabase: SupabaseClient;
}
}

29
src/plugins/swagger.ts Normal file
View File

@@ -0,0 +1,29 @@
import { FastifyInstance } from "fastify";
import fp from "fastify-plugin";
import swagger from "@fastify/swagger";
import swaggerUi from "@fastify/swagger-ui";
export default fp(async (server: FastifyInstance) => {
await server.register(swagger, {
mode: "dynamic", // wichtig: generiert echtes OpenAPI JSON
openapi: {
info: {
title: "Multi-Tenant API",
description: "API Dokumentation für dein Backend",
version: "1.0.0",
},
servers: [{ url: "http://localhost:3000" }],
},
});
await server.register(swaggerUi, {
routePrefix: "/docs", // UI erreichbar unter http://localhost:3000/docs
swagger: {
info: {
title: "Multi-Tenant API",
version: "1.0.0",
},
},
exposeRoute: true,
});
});

44
src/plugins/tenant.ts Normal file
View File

@@ -0,0 +1,44 @@
import { FastifyInstance, FastifyRequest } from "fastify";
import fp from "fastify-plugin";
export default fp(async (server: FastifyInstance) => {
server.addHook("preHandler", async (req, reply) => {
const host = req.headers.host?.split(":")[0]; // Domain ohne Port
if (!host) {
reply.code(400).send({ error: "Missing host header" });
return;
}
console.log(host)
// Tenant aus DB laden
const { data: tenant } = await server.supabase
.from("tenants")
.select("*")
.eq("portalDomain", host)
.single();
if(!tenant) {
// Multi Tenant Mode
(req as any).tenant = null;
}else {
// Tenant ins Request-Objekt hängen
(req as any).tenant = tenant;
}
});
});
// Typ-Erweiterung
declare module "fastify" {
interface FastifyRequest {
tenant?: {
id: string;
name: string;
domain?: string;
subdomain?: string;
settings?: Record<string, any>;
};
}
}