Security: API kljuc za pisalne endpointe (articles, upload)
This commit is contained in:
parent
fda5e2ce62
commit
69d400a209
@ -65,6 +65,15 @@ export async function registerRoutes(
|
|||||||
|
|
||||||
startDailyScheduler();
|
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) => {
|
storage.getArticles().then((articles) => {
|
||||||
analyzeAllArticleImages(articles).catch(err =>
|
analyzeAllArticleImages(articles).catch(err =>
|
||||||
console.log("[focal-point] Analysis skipped:", err.message)
|
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) });
|
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);
|
const parsed = insertArticleSchema.safeParse(req.body);
|
||||||
if (!parsed.success) {
|
if (!parsed.success) {
|
||||||
return res.status(400).json({ message: "Invalid article data", errors: parsed.error.flatten() });
|
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);
|
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 id = parseInt(req.params.id);
|
||||||
const partial = insertArticleSchema.partial().safeParse(req.body);
|
const partial = insertArticleSchema.partial().safeParse(req.body);
|
||||||
if (!partial.success) {
|
if (!partial.success) {
|
||||||
@ -258,13 +267,13 @@ export async function registerRoutes(
|
|||||||
res.json(article);
|
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);
|
const id = parseInt(req.params.id);
|
||||||
await storage.deleteArticle(id);
|
await storage.deleteArticle(id);
|
||||||
res.status(204).send();
|
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) {
|
if (!req.file) {
|
||||||
return res.status(400).json({ message: "No file uploaded" });
|
return res.status(400).json({ message: "No file uploaded" });
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user