Security: API kljuc za pisalne endpointe (articles, upload)

This commit is contained in:
Claude 2026-08-12 09:59:48 +02:00
parent fda5e2ce62
commit 69d400a209

View File

@ -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" });
}