feat(20-01): add tags table, tag service/route, register global-items route
- Create tags table in schema with id, name (unique), createdAt - Generate migration for tags table - Create tag.service.ts with getAllTags (id+name, alphabetical order) - Create tags.ts route with GET / handler - Register /api/global-items and /api/tags routes in index.ts - Add auth skip for GET /api/tags and GET /api/global-items
This commit is contained in:
@@ -141,6 +141,14 @@ export const globalItems = pgTable("global_items", {
|
||||
createdAt: timestamp("created_at").defaultNow().notNull(),
|
||||
});
|
||||
|
||||
// ── Tags ────────────────────────────────────────────────────────────
|
||||
|
||||
export const tags = pgTable("tags", {
|
||||
id: serial("id").primaryKey(),
|
||||
name: text("name").notNull().unique(),
|
||||
createdAt: timestamp("created_at").defaultNow().notNull(),
|
||||
});
|
||||
|
||||
// ── Item Global Links ───────────────────────────────────────────────
|
||||
|
||||
export const itemGlobalLinks = pgTable("item_global_links", {
|
||||
|
||||
@@ -15,7 +15,9 @@ import { categoryRoutes } from "./routes/categories.ts";
|
||||
import { imageRoutes } from "./routes/images.ts";
|
||||
import { itemRoutes } from "./routes/items.ts";
|
||||
import { oauthRoutes, wellKnownRoute } from "./routes/oauth.ts";
|
||||
import { globalItemRoutes } from "./routes/global-items.ts";
|
||||
import { profileRoutes } from "./routes/profiles.ts";
|
||||
import { tagRoutes } from "./routes/tags.ts";
|
||||
import { settingsRoutes } from "./routes/settings.ts";
|
||||
import { setupRoutes } from "./routes/setups.ts";
|
||||
import { threadRoutes } from "./routes/threads.ts";
|
||||
@@ -98,6 +100,12 @@ app.use("/api/*", async (c, next) => {
|
||||
// Skip public setup view (GET /api/setups/:id/public)
|
||||
if (/^\/api\/setups\/\d+\/public$/.test(c.req.path) && c.req.method === "GET")
|
||||
return next();
|
||||
// Skip public tags endpoint (GET /api/tags)
|
||||
if (c.req.path.startsWith("/api/tags") && 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();
|
||||
// All other methods require auth for userId resolution
|
||||
return requireAuth(c, next);
|
||||
});
|
||||
@@ -112,6 +120,8 @@ app.route("/api/settings", settingsRoutes);
|
||||
app.route("/api/threads", threadRoutes);
|
||||
app.route("/api/users", profileRoutes);
|
||||
app.route("/api/setups", setupRoutes);
|
||||
app.route("/api/global-items", globalItemRoutes);
|
||||
app.route("/api/tags", tagRoutes);
|
||||
|
||||
// MCP server (conditionally mounted)
|
||||
if (process.env.GEARBOX_MCP !== "false") {
|
||||
|
||||
14
src/server/routes/tags.ts
Normal file
14
src/server/routes/tags.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { Hono } from "hono";
|
||||
import { getAllTags } from "../services/tag.service.ts";
|
||||
|
||||
type Env = { Variables: { db?: any } };
|
||||
|
||||
const app = new Hono<Env>();
|
||||
|
||||
app.get("/", async (c) => {
|
||||
const db = c.get("db");
|
||||
const allTags = await getAllTags(db);
|
||||
return c.json(allTags);
|
||||
});
|
||||
|
||||
export { app as tagRoutes };
|
||||
12
src/server/services/tag.service.ts
Normal file
12
src/server/services/tag.service.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { asc } from "drizzle-orm";
|
||||
import { db as prodDb } from "../../db/index.ts";
|
||||
import { tags } from "../../db/schema.ts";
|
||||
|
||||
type Db = typeof prodDb;
|
||||
|
||||
export async function getAllTags(db: Db = prodDb) {
|
||||
return db
|
||||
.select({ id: tags.id, name: tags.name })
|
||||
.from(tags)
|
||||
.orderBy(asc(tags.name));
|
||||
}
|
||||
Reference in New Issue
Block a user