feat(26-02): discovery HTTP routes, server registration, and route tests
- Create src/server/routes/discovery.ts with GET /setups, /items, /categories handlers - Register discoveryRoutes in src/server/index.ts with browseTier rate limiting - Add auth skip for /api/discovery/* GET requests in auth middleware - Create tests/routes/discovery.test.ts with 10 tests covering all endpoints and pagination
This commit is contained in:
@@ -13,6 +13,7 @@ import { requireAuth } from "./middleware/auth.ts";
|
||||
import { createRateLimit } from "./middleware/rateLimit.ts";
|
||||
import { authRoutes } from "./routes/auth.ts";
|
||||
import { categoryRoutes } from "./routes/categories.ts";
|
||||
import { discoveryRoutes } from "./routes/discovery.ts";
|
||||
import { globalItemRoutes } from "./routes/global-items.ts";
|
||||
import { imageRoutes } from "./routes/images.ts";
|
||||
import { itemRoutes } from "./routes/items.ts";
|
||||
@@ -123,6 +124,10 @@ const browseTier = createRateLimit(120, 60_000);
|
||||
const detailTier = createRateLimit(60, 60_000);
|
||||
|
||||
// Browse endpoints — higher limit for list/search
|
||||
app.use("/api/discovery/*", async (c, next) => {
|
||||
if (c.req.method === "GET") return browseTier(c, next);
|
||||
return next();
|
||||
});
|
||||
app.use("/api/global-items", async (c, next) => {
|
||||
if (c.req.method === "GET" && !c.req.path.match(/^\/api\/global-items\/\d+$/))
|
||||
return browseTier(c, next);
|
||||
@@ -162,6 +167,9 @@ app.use("/api/*", async (c, next) => {
|
||||
// Skip public tags endpoint (GET /api/tags)
|
||||
if (c.req.path.startsWith("/api/tags") && c.req.method === "GET")
|
||||
return next();
|
||||
// Skip public discovery endpoints (GET /api/discovery/*)
|
||||
if (c.req.path.startsWith("/api/discovery") && c.req.method === "GET")
|
||||
return next();
|
||||
// Skip public global-items endpoint (GET /api/global-items)
|
||||
if (c.req.path.startsWith("/api/global-items") && c.req.method === "GET")
|
||||
return next();
|
||||
@@ -179,6 +187,7 @@ app.route("/api/settings", settingsRoutes);
|
||||
app.route("/api/threads", threadRoutes);
|
||||
app.route("/api/users", profileRoutes);
|
||||
app.route("/api/setups", setupRoutes);
|
||||
app.route("/api/discovery", discoveryRoutes);
|
||||
app.route("/api/global-items", globalItemRoutes);
|
||||
app.route("/api/tags", tagRoutes);
|
||||
|
||||
|
||||
38
src/server/routes/discovery.ts
Normal file
38
src/server/routes/discovery.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { Hono } from "hono";
|
||||
import {
|
||||
getPopularSetups,
|
||||
getRecentGlobalItems,
|
||||
getTrendingCategories,
|
||||
} from "../services/discovery.service.ts";
|
||||
|
||||
type Env = { Variables: { db?: any } };
|
||||
|
||||
const app = new Hono<Env>();
|
||||
|
||||
app.get("/setups", async (c) => {
|
||||
const db = c.get("db");
|
||||
const limitParam = c.req.query("limit");
|
||||
const cursor = c.req.query("cursor");
|
||||
const limit = Math.min(limitParam ? parseInt(limitParam, 10) || 6 : 6, 50);
|
||||
const result = await getPopularSetups(db, limit, cursor);
|
||||
return c.json(result);
|
||||
});
|
||||
|
||||
app.get("/items", async (c) => {
|
||||
const db = c.get("db");
|
||||
const limitParam = c.req.query("limit");
|
||||
const cursor = c.req.query("cursor");
|
||||
const limit = Math.min(limitParam ? parseInt(limitParam, 10) || 8 : 8, 50);
|
||||
const result = await getRecentGlobalItems(db, limit, cursor);
|
||||
return c.json(result);
|
||||
});
|
||||
|
||||
app.get("/categories", async (c) => {
|
||||
const db = c.get("db");
|
||||
const limitParam = c.req.query("limit");
|
||||
const limit = Math.min(limitParam ? parseInt(limitParam, 10) || 12 : 12, 50);
|
||||
const result = await getTrendingCategories(db, limit);
|
||||
return c.json(result);
|
||||
});
|
||||
|
||||
export { app as discoveryRoutes };
|
||||
Reference in New Issue
Block a user