From 69d400a2093caff383c687703acc0dd2d0b3e709 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 12 Aug 2026 09:59:48 +0200 Subject: [PATCH] Security: API kljuc za pisalne endpointe (articles, upload) --- server/routes.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/server/routes.ts b/server/routes.ts index 4fc56d3..8e0a6e4 100644 --- a/server/routes.ts +++ b/server/routes.ts @@ -65,6 +65,15 @@ export async function registerRoutes( startDailyScheduler(); + // ---- API zascita za pisalne endpointe (boti posiljajo x-api-key) ---- + const FOLX_API_KEY = process.env.FOLX_API_KEY || ""; + const requireApiKey = (req: any, res: any, next: any) => { + if (!FOLX_API_KEY) return next(); // brez nastavljenega kljuca: ne zaklepamo (rollout varovalka) + const provided = req.get("x-api-key") || (req.get("authorization") || "").replace(/^Bearer\s+/i, ""); + if (provided && provided === FOLX_API_KEY) return next(); + return res.status(401).json({ message: "Unauthorized" }); + }; + storage.getArticles().then((articles) => { analyzeAllArticleImages(articles).catch(err => console.log("[focal-point] Analysis skipped:", err.message) @@ -232,7 +241,7 @@ export async function registerRoutes( res.json({ ...article, views: article.views + (isNew ? 1 : 0) }); }); - app.post("/api/articles", async (req, res) => { + app.post("/api/articles", requireApiKey, async (req, res) => { const parsed = insertArticleSchema.safeParse(req.body); if (!parsed.success) { return res.status(400).json({ message: "Invalid article data", errors: parsed.error.flatten() }); @@ -245,7 +254,7 @@ export async function registerRoutes( res.status(201).json(article); }); - app.patch("/api/articles/:id", async (req, res) => { + app.patch("/api/articles/:id", requireApiKey, async (req, res) => { const id = parseInt(req.params.id); const partial = insertArticleSchema.partial().safeParse(req.body); if (!partial.success) { @@ -258,13 +267,13 @@ export async function registerRoutes( res.json(article); }); - app.delete("/api/articles/:id", async (req, res) => { + app.delete("/api/articles/:id", requireApiKey, async (req, res) => { const id = parseInt(req.params.id); await storage.deleteArticle(id); res.status(204).send(); }); - app.post("/api/upload", upload.single("image"), async (req, res) => { + app.post("/api/upload", requireApiKey, upload.single("image"), async (req, res) => { if (!req.file) { return res.status(400).json({ message: "No file uploaded" }); }