Many Improvements
This commit is contained in:
@@ -25,12 +25,12 @@ let settings = {
|
||||
storageUsedMB: 214
|
||||
},
|
||||
categoryPriority: [
|
||||
{ id: "cat-top", name: "Top stories", priorityRank: 1 },
|
||||
{ id: "cat-local", name: "Local", priorityRank: 2 },
|
||||
{ id: "cat-world", name: "World", priorityRank: 3 },
|
||||
{ id: "cat-business", name: "Business", priorityRank: 4 },
|
||||
{ id: "cat-tech", name: "Tech", priorityRank: 5 },
|
||||
{ id: "cat-culture", name: "Culture", priorityRank: 6 }
|
||||
{ id: "cat-top", name: "Top stories", priorityRank: 1, isDefault: true },
|
||||
{ id: "cat-local", name: "Local", priorityRank: 2, isDefault: true },
|
||||
{ id: "cat-world", name: "World", priorityRank: 3, isDefault: true },
|
||||
{ id: "cat-business", name: "Business", priorityRank: 4, isDefault: true },
|
||||
{ id: "cat-tech", name: "Tech", priorityRank: 5, isDefault: true },
|
||||
{ id: "cat-culture", name: "Culture", priorityRank: 6, isDefault: true }
|
||||
]
|
||||
};
|
||||
|
||||
@@ -143,5 +143,15 @@ module.exports = {
|
||||
deleteEvent: (id) => {
|
||||
events = events.filter((e) => e.id !== id);
|
||||
},
|
||||
getModels: () => models
|
||||
getModels: () => models,
|
||||
createCategory: (name) => {
|
||||
const id = `cat-${name.toLowerCase().replace(/[^a-z0-9]+/g, "-")}-${Date.now()}`;
|
||||
const maxRank = Math.max(0, ...settings.categoryPriority.map((c) => c.priorityRank));
|
||||
const created = { id, name, priorityRank: maxRank + 1, isDefault: false };
|
||||
settings.categoryPriority = [...settings.categoryPriority, created];
|
||||
return created;
|
||||
},
|
||||
deleteCategory: (id) => {
|
||||
settings.categoryPriority = settings.categoryPriority.filter((c) => c.id !== id);
|
||||
}
|
||||
};
|
||||
|
||||
+34
-3
@@ -1,9 +1,10 @@
|
||||
// Mock backend — implements the public API surface from homefeed-data-schema.md
|
||||
// so the frontend can be built and tested before the real backend exists.
|
||||
// GET /api/feed?category=&geo=&eventId=&tag=
|
||||
// GET /api/feed?category=&geo=&eventId=&tag=&before=&limit=
|
||||
// GET /api/article/:id
|
||||
// GET /api/events
|
||||
// GET /api/tags
|
||||
// GET /api/categories
|
||||
|
||||
const express = require("express");
|
||||
const cors = require("cors");
|
||||
@@ -17,7 +18,7 @@ app.use(express.json());
|
||||
const PORT = process.env.PORT || 4000;
|
||||
|
||||
app.get("/api/feed", (req, res) => {
|
||||
const { category, geo, eventId, tag } = req.query;
|
||||
const { category, geo, eventId, tag, before, limit } = req.query;
|
||||
let results = articles;
|
||||
|
||||
if (category) {
|
||||
@@ -34,7 +35,14 @@ app.get("/api/feed", (req, res) => {
|
||||
}
|
||||
|
||||
results = [...results].sort((a, b) => new Date(b.publishedAt) - new Date(a.publishedAt));
|
||||
res.json(results);
|
||||
|
||||
if (before) {
|
||||
const cursor = new Date(before).getTime();
|
||||
results = results.filter((a) => new Date(a.publishedAt).getTime() < cursor);
|
||||
}
|
||||
|
||||
const pageSize = Math.min(Number(limit) || 15, 50);
|
||||
res.json(results.slice(0, pageSize));
|
||||
});
|
||||
|
||||
app.get("/api/article/:id", (req, res) => {
|
||||
@@ -51,6 +59,11 @@ app.get("/api/events", (req, res) => {
|
||||
res.json(events);
|
||||
});
|
||||
|
||||
app.get("/api/categories", (req, res) => {
|
||||
const { categoryPriority } = admin.getSettings();
|
||||
res.json([...categoryPriority].sort((a, b) => a.priorityRank - b.priorityRank));
|
||||
});
|
||||
|
||||
// --- Admin API ---
|
||||
// No auth in the mock — the real backend enforces session auth on all /api/admin/* routes.
|
||||
|
||||
@@ -104,6 +117,24 @@ app.get("/api/admin/models", (req, res) => {
|
||||
res.json(admin.getModels());
|
||||
});
|
||||
|
||||
app.post("/api/admin/categories", (req, res) => {
|
||||
const { name } = req.body;
|
||||
if (!name || !name.trim()) return res.status(400).json({ error: "name required" });
|
||||
res.status(201).json(admin.createCategory(name.trim()));
|
||||
});
|
||||
|
||||
app.delete("/api/admin/categories/:id", (req, res) => {
|
||||
admin.deleteCategory(req.params.id);
|
||||
res.status(204).end();
|
||||
});
|
||||
|
||||
app.get("/api/admin/logs", (req, res) => {
|
||||
// The mock backend doesn't run a scheduler/pipeline, so there's nothing to log —
|
||||
// returns an empty list rather than 404ing, so the Logs tab renders its empty state
|
||||
// instead of erroring when pointed at the mock backend.
|
||||
res.json([]);
|
||||
});
|
||||
|
||||
app.get("/api/admin/ai-status", (req, res) => {
|
||||
// Simulates pinging the configured Ollama host
|
||||
const { aiServiceHost, aiServicePort } = admin.getSettings();
|
||||
|
||||
Reference in New Issue
Block a user