diff --git a/server/api/admin/tags/index.post.js b/server/api/admin/tags/index.post.js new file mode 100644 index 0000000..70e0624 --- /dev/null +++ b/server/api/admin/tags/index.post.js @@ -0,0 +1,40 @@ +import Tag from '../../../models/tag.js' +import { adminTagCreateSchema } from '../../../utils/schemas.js' + +const slugify = (s) => + s + .toLowerCase() + .trim() + .replace(/\s+/g, '-') + .replace(/[^a-z0-9-]/g, '') + .replace(/-+/g, '-') + .replace(/^-|-$/g, '') + +export default defineEventHandler(async (event) => { + await requireAdmin(event) + await connectDB() + + const body = await validateBody(event, adminTagCreateSchema) + const slug = slugify(body.label) + + if (!slug) { + throw createError({ + statusCode: 400, + statusMessage: 'Tag label must contain at least one alphanumeric character' + }) + } + + const existing = await Tag.findOne({ slug }) + if (existing) { + return { tag: { slug: existing.slug, label: existing.label, pool: existing.pool } } + } + + const tag = await Tag.create({ + slug, + label: body.label, + pool: body.pool, + active: true + }) + + return { tag: { slug: tag.slug, label: tag.label, pool: tag.pool } } +}) diff --git a/server/utils/schemas.js b/server/utils/schemas.js index 8e7416b..322bbd4 100644 --- a/server/utils/schemas.js +++ b/server/utils/schemas.js @@ -383,6 +383,11 @@ export const tagSuggestionSchema = z.object({ pool: z.enum(['craft', 'cooperative']) }) +export const adminTagCreateSchema = z.object({ + label: z.string().trim().min(1).max(100), + pool: z.enum(['craft', 'cooperative']) +}) + // --- Board post / channel schemas --- export const boardPostCreateSchema = z.object({