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:
6
drizzle-pg/0002_square_pyro.sql
Normal file
6
drizzle-pg/0002_square_pyro.sql
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
CREATE TABLE "tags" (
|
||||||
|
"id" serial PRIMARY KEY NOT NULL,
|
||||||
|
"name" text NOT NULL,
|
||||||
|
"created_at" timestamp DEFAULT now() NOT NULL,
|
||||||
|
CONSTRAINT "tags_name_unique" UNIQUE("name")
|
||||||
|
);
|
||||||
1133
drizzle-pg/meta/0002_snapshot.json
Normal file
1133
drizzle-pg/meta/0002_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -15,6 +15,13 @@
|
|||||||
"when": 1775386658636,
|
"when": 1775386658636,
|
||||||
"tag": "0001_tough_boomerang",
|
"tag": "0001_tough_boomerang",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 2,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1775454835904,
|
||||||
|
"tag": "0002_square_pyro",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -141,6 +141,14 @@ export const globalItems = pgTable("global_items", {
|
|||||||
createdAt: timestamp("created_at").defaultNow().notNull(),
|
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 ───────────────────────────────────────────────
|
// ── Item Global Links ───────────────────────────────────────────────
|
||||||
|
|
||||||
export const itemGlobalLinks = pgTable("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 { imageRoutes } from "./routes/images.ts";
|
||||||
import { itemRoutes } from "./routes/items.ts";
|
import { itemRoutes } from "./routes/items.ts";
|
||||||
import { oauthRoutes, wellKnownRoute } from "./routes/oauth.ts";
|
import { oauthRoutes, wellKnownRoute } from "./routes/oauth.ts";
|
||||||
|
import { globalItemRoutes } from "./routes/global-items.ts";
|
||||||
import { profileRoutes } from "./routes/profiles.ts";
|
import { profileRoutes } from "./routes/profiles.ts";
|
||||||
|
import { tagRoutes } from "./routes/tags.ts";
|
||||||
import { settingsRoutes } from "./routes/settings.ts";
|
import { settingsRoutes } from "./routes/settings.ts";
|
||||||
import { setupRoutes } from "./routes/setups.ts";
|
import { setupRoutes } from "./routes/setups.ts";
|
||||||
import { threadRoutes } from "./routes/threads.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)
|
// Skip public setup view (GET /api/setups/:id/public)
|
||||||
if (/^\/api\/setups\/\d+\/public$/.test(c.req.path) && c.req.method === "GET")
|
if (/^\/api\/setups\/\d+\/public$/.test(c.req.path) && c.req.method === "GET")
|
||||||
return next();
|
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
|
// All other methods require auth for userId resolution
|
||||||
return requireAuth(c, next);
|
return requireAuth(c, next);
|
||||||
});
|
});
|
||||||
@@ -112,6 +120,8 @@ app.route("/api/settings", settingsRoutes);
|
|||||||
app.route("/api/threads", threadRoutes);
|
app.route("/api/threads", threadRoutes);
|
||||||
app.route("/api/users", profileRoutes);
|
app.route("/api/users", profileRoutes);
|
||||||
app.route("/api/setups", setupRoutes);
|
app.route("/api/setups", setupRoutes);
|
||||||
|
app.route("/api/global-items", globalItemRoutes);
|
||||||
|
app.route("/api/tags", tagRoutes);
|
||||||
|
|
||||||
// MCP server (conditionally mounted)
|
// MCP server (conditionally mounted)
|
||||||
if (process.env.GEARBOX_MCP !== "false") {
|
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