From 83fc24be0c1e93d7987c87ec4a942e7fdb1e0fed Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sun, 28 Sep 2025 17:43:21 +0200 Subject: [PATCH] Introduced Secrets Manager --- package-lock.json | 1 + package.json | 1 + src/index.ts | 8 +++++--- src/plugins/auth.ts | 3 ++- src/plugins/supabase.ts | 6 +++--- src/routes/auth/auth.ts | 3 ++- src/routes/exports.ts | 5 +++-- src/routes/files.ts | 11 ++++++----- src/routes/tenant.ts | 3 ++- src/utils/export/datev.ts | 3 ++- src/utils/mailer.ts | 13 +++++++------ src/utils/s3.ts | 25 ++++++++++++++++--------- src/utils/secrets.ts | 29 +++++++++++++++++++++++++++++ 13 files changed, 79 insertions(+), 32 deletions(-) create mode 100644 src/utils/secrets.ts diff --git a/package-lock.json b/package-lock.json index a8ee19d..8e8963d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "@fastify/multipart": "^9.0.3", "@fastify/swagger": "^9.5.1", "@fastify/swagger-ui": "^5.2.3", + "@infisical/sdk": "^4.0.6", "@prisma/client": "^6.15.0", "@supabase/supabase-js": "^2.56.1", "@zip.js/zip.js": "^2.7.73", diff --git a/package.json b/package.json index c47b02b..6788b5b 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "@fastify/multipart": "^9.0.3", "@fastify/swagger": "^9.5.1", "@fastify/swagger-ui": "^5.2.3", + "@infisical/sdk": "^4.0.6", "@prisma/client": "^6.15.0", "@supabase/supabase-js": "^2.56.1", "@zip.js/zip.js": "^2.7.73", diff --git a/src/index.ts b/src/index.ts index f49981e..87f6fba 100644 --- a/src/index.ts +++ b/src/index.ts @@ -22,9 +22,11 @@ import exportRoutes from "./routes/exports" import emailAsUserRoutes from "./routes/emailAsUser"; import {sendMail} from "./utils/mailer"; +import {loadSecrets, secrets} from "./utils/secrets"; async function main() { const app = Fastify({ logger: true }); + await loadSecrets(); /*app.addHook("onRequest", (req, reply, done) => { console.log("Incoming:", req.method, req.url, "Headers:", req.headers) @@ -37,7 +39,7 @@ async function main() { await app.register(supabasePlugin); await app.register(tenantPlugin); app.register(fastifyCookie, { - secret: process.env.COOKIE_SECRET || "supersecret", // optional, für signierte Cookies + secret: secrets.COOKIE_SECRET, }) // Öffentliche Routes await app.register(authRoutes); @@ -68,8 +70,8 @@ async function main() { // Start try { - await app.listen({ port: 3100, host: "0.0.0.0" }); - console.log("🚀 Server läuft auf http://localhost:3100"); + await app.listen({ port: secrets.PORT, host: secrets.HOST }); + console.log(`🚀 Server läuft auf http://${secrets.HOST}:${secrets.PORT}`); } catch (err) { app.log.error(err); process.exit(1); diff --git a/src/plugins/auth.ts b/src/plugins/auth.ts index eb92f5d..fc2642c 100644 --- a/src/plugins/auth.ts +++ b/src/plugins/auth.ts @@ -1,6 +1,7 @@ import { FastifyInstance } from "fastify"; import fp from "fastify-plugin"; import jwt from "jsonwebtoken"; +import {secrets} from "../utils/secrets"; export default fp(async (server: FastifyInstance) => { server.addHook("preHandler", async (req, reply) => { @@ -28,7 +29,7 @@ export default fp(async (server: FastifyInstance) => { } - const payload = jwt.verify(token, process.env.JWT_SECRET!) as { + const payload = jwt.verify(token, secrets.JWT_SECRET!) as { user_id: string; email: string; tenant_id?: string; diff --git a/src/plugins/supabase.ts b/src/plugins/supabase.ts index 66172df..eb5ca98 100644 --- a/src/plugins/supabase.ts +++ b/src/plugins/supabase.ts @@ -1,11 +1,11 @@ import { FastifyInstance } from "fastify"; import fp from "fastify-plugin"; import { createClient, SupabaseClient } from "@supabase/supabase-js"; +import {secrets} from "../utils/secrets"; 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 supabaseUrl = secrets.SUPABASE_URL + const supabaseServiceKey = secrets.SUPABASE_SERVICE_ROLE_KEY const supabase: SupabaseClient = createClient(supabaseUrl, supabaseServiceKey); // Fastify um supabase erweitern diff --git a/src/routes/auth/auth.ts b/src/routes/auth/auth.ts index d0ac34e..86cf6d6 100644 --- a/src/routes/auth/auth.ts +++ b/src/routes/auth/auth.ts @@ -3,6 +3,7 @@ import bcrypt from "bcrypt"; import jwt from "jsonwebtoken"; import { generateRandomPassword, hashPassword } from "../../utils/password" import { sendMail } from "../../utils/mailer" +import {secrets} from "../../utils/secrets"; export default async function authRoutes(server: FastifyInstance) { // Registrierung @@ -140,7 +141,7 @@ export default async function authRoutes(server: FastifyInstance) { } else { const token = jwt.sign( { user_id: user.id, email: user.email, tenant_id: req.tenant ? req.tenant.id : null }, - process.env.JWT_SECRET!, + secrets.JWT_SECRET!, { expiresIn: "3h" } ); diff --git a/src/routes/exports.ts b/src/routes/exports.ts index eb10186..89d041b 100644 --- a/src/routes/exports.ts +++ b/src/routes/exports.ts @@ -7,6 +7,7 @@ import {GetObjectCommand, PutObjectCommand} from "@aws-sdk/client-s3" import {getSignedUrl} from "@aws-sdk/s3-request-presigner"; import dayjs from "dayjs"; import {randomUUID} from "node:crypto"; +import {secrets} from "../utils/secrets"; const createExport = async (server:FastifyInstance,req:any,startDate,endDate,beraternr,mandantennr) => { console.log(startDate,endDate,beraternr,mandantennr) @@ -22,7 +23,7 @@ const createExport = async (server:FastifyInstance,req:any,startDate,endDate,ber // 3) In S3 hochladen await s3.send( new PutObjectCommand({ - Bucket: process.env.S3_BUCKET || "FEDEO", + Bucket: secrets.S3_BUCKET, Key: fileKey, Body: buffer, ContentType: "application/zip", @@ -33,7 +34,7 @@ const createExport = async (server:FastifyInstance,req:any,startDate,endDate,ber const url = await getSignedUrl( s3, new GetObjectCommand({ - Bucket: process.env.S3_BUCKET || "FEDEO", + Bucket: secrets.S3_BUCKET, Key: fileKey, }), { expiresIn: 60 * 60 * 24 } diff --git a/src/routes/files.ts b/src/routes/files.ts index 5ec8d04..c18529c 100644 --- a/src/routes/files.ts +++ b/src/routes/files.ts @@ -4,6 +4,7 @@ import { s3 } from "../utils/s3" import {GetObjectCommand, PutObjectCommand} from "@aws-sdk/client-s3" import {getSignedUrl} from "@aws-sdk/s3-request-presigner"; import archiver from "archiver" +import {secrets} from "../utils/secrets" export default async function fileRoutes(server: FastifyInstance) { await server.register(multipart,{ @@ -59,7 +60,7 @@ export default async function fileRoutes(server: FastifyInstance) { const fileKey = `${tenantId}/filesbyid/${createdFileData.id}/${data.filename}` await s3.send(new PutObjectCommand({ - Bucket: process.env.S3_BUCKET || "FEDEO", + Bucket: secrets.S3_BUCKET, Key: fileKey, Body: fileBuffer, ContentType: data.mimetype, @@ -143,7 +144,7 @@ export default async function fileRoutes(server: FastifyInstance) { } const command = new GetObjectCommand({ - Bucket: process.env.S3_BUCKET || "FEDEO", + Bucket: secrets.S3_BUCKET, Key: data.path, }) @@ -187,7 +188,7 @@ export default async function fileRoutes(server: FastifyInstance) { for (const entry of supabaseFiles) { const command = new GetObjectCommand({ - Bucket: process.env.S3_BUCKET || "FEDEO", + Bucket: secrets.S3_BUCKET, Key: entry.path, }) @@ -217,7 +218,7 @@ export default async function fileRoutes(server: FastifyInstance) { const {data,error} = await server.supabase.from("files").select("*").eq("id", id).single() const command = new GetObjectCommand({ - Bucket: process.env.S3_BUCKET || "FEDEO", + Bucket: secrets.S3_BUCKET, Key: data.path, }); @@ -256,7 +257,7 @@ export default async function fileRoutes(server: FastifyInstance) { if(!key) console.log(file) const command = new GetObjectCommand({ - Bucket: process.env.S3_BUCKET || "FEDEO", + Bucket: secrets.S3_BUCKET, Key: key, }) diff --git a/src/routes/tenant.ts b/src/routes/tenant.ts index 86e4c04..0739e04 100644 --- a/src/routes/tenant.ts +++ b/src/routes/tenant.ts @@ -1,5 +1,6 @@ import { FastifyInstance } from "fastify"; import jwt from "jsonwebtoken"; +import {secrets} from "../utils/secrets"; export default async function routes(server: FastifyInstance) { server.get("/tenant", async (req) => { @@ -45,7 +46,7 @@ export default async function routes(server: FastifyInstance) { email: req.user.email, tenant_id: body.tenant_id, }, - process.env.JWT_SECRET!, + secrets.JWT_SECRET!, { expiresIn: "3h" } ); diff --git a/src/utils/export/datev.ts b/src/utils/export/datev.ts index 3365fc8..647d27c 100644 --- a/src/utils/export/datev.ts +++ b/src/utils/export/datev.ts @@ -5,6 +5,7 @@ import {BlobWriter, Data64URIReader, TextReader, TextWriter, ZipWriter} from "@z import {FastifyInstance} from "fastify"; import {GetObjectCommand} from "@aws-sdk/client-s3"; import {s3} from "../s3"; +import {secrets} from "../secrets"; dayjs.extend(isBetween) const getCreatedDocumentTotal = (item) => { @@ -92,7 +93,7 @@ export async function buildExportZip(server: FastifyInstance, tenant: number, st const downloadFile = async (bucketName, filePath, downloadFilePath,fileId) => { const command = new GetObjectCommand({ - Bucket: process.env.S3_BUCKET || "FEDEO", + Bucket: secrets.S3_BUCKET, Key: filePath, }) diff --git a/src/utils/mailer.ts b/src/utils/mailer.ts index 34f94e1..143b64d 100644 --- a/src/utils/mailer.ts +++ b/src/utils/mailer.ts @@ -1,12 +1,13 @@ import nodemailer from "nodemailer" +import {secrets} from "./secrets" const transporter = nodemailer.createTransport({ - host: process.env.SMTP_HOST, - port: Number(process.env.SMTP_PORT) || 587, - secure: process.env.SMTP_SSL === "true", // true für 465, false für andere Ports + host: secrets.MAILER_SMTP_HOST, + port: Number(secrets.MAILER_SMTP_PORT) || 587, + secure: secrets.MAILER_SMTP_SSL === "true", // true für 465, false für andere Ports auth: { - user: process.env.SMTP_USER, - pass: process.env.SMTP_PASS, + user: secrets.MAILER_SMTP_USER, + pass: secrets.MAILER_SMTP_PASS, }, }) @@ -17,7 +18,7 @@ export async function sendMail( ): Promise<{ success: boolean; info?: any; error?: any }> { try { const info = await transporter.sendMail({ - from: process.env.MAIL_FROM, + from: secrets.MAILER_FROM, to, subject, html, diff --git a/src/utils/s3.ts b/src/utils/s3.ts index a419fbb..7a33d1c 100644 --- a/src/utils/s3.ts +++ b/src/utils/s3.ts @@ -1,11 +1,18 @@ import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3" +import {secrets} from "./secrets"; -export const s3 = new S3Client({ - endpoint: process.env.S3_ENDPOINT || "https://fedeo.nbg1.your-objectstorage.com", // z. B. http://localhost:9000 für MinIO - region: process.env.S3_REGION || "eu-central", - credentials: { - accessKeyId: process.env.S3_ACCESS_KEY || "RYOMQRW8KSTY3UQX7RPJ", - secretAccessKey: process.env.S3_SECRET_KEY || "aZ33xBv47sPPsHuFKeHSDiLagjqF7nShnuGkj7B1", - }, - forcePathStyle: true, // wichtig für MinIO -}) \ No newline at end of file + + +export let s3 = null + +export const initS3 = async () => { + s3 = new S3Client({ + endpoint: secrets.S3_ENDPOINT, // z. B. http://localhost:9000 für MinIO + region: secrets.S3_REGION, + credentials: { + accessKeyId: secrets.S3_ACCESS_KEY, + secretAccessKey: secrets.S3_SECRET_KEY, + }, + forcePathStyle: true, // wichtig für MinIO + }) +} diff --git a/src/utils/secrets.ts b/src/utils/secrets.ts new file mode 100644 index 0000000..84447ff --- /dev/null +++ b/src/utils/secrets.ts @@ -0,0 +1,29 @@ +import {InfisicalSDK} from "@infisical/sdk" + +const client = new InfisicalSDK({ + siteUrl: "https://secrets.fedeo.io" +}) + + + +export let secrets = {} + +export async function loadSecrets () { + + await client.auth().universalAuth.login({ + clientId: process.env.INFISICAL_CLIENT_ID, + clientSecret: process.env.INFISICAL_CLIENT_SECRET, + }); + + const allSecrets = await client.secrets().listSecrets({ + environment: "dev", // stg, dev, prod, or custom environment slugs + projectId: "39774094-2aaf-49fb-a213-d6b2c10f6144" + }); + + allSecrets.secrets.forEach(secret => { + secrets[secret.secretKey] = secret.secretValue + }) + + console.log("✅ Secrets aus Infisical geladen"); +} +