diff --git a/.changeset/ripe-zebras-sell.md b/.changeset/ripe-zebras-sell.md new file mode 100644 index 0000000000..a845151cc8 --- /dev/null +++ b/.changeset/ripe-zebras-sell.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/server/src/db/working-group-db.ts b/server/src/db/working-group-db.ts index 407dbbac3e..f4e10ed0fd 100644 --- a/server/src/db/working-group-db.ts +++ b/server/src/db/working-group-db.ts @@ -153,6 +153,7 @@ export class WorkingGroupDatabase { const COLUMN_MAP: Record = { name: 'name', + slug: 'slug', description: 'description', slack_channel_url: 'slack_channel_url', slack_channel_id: 'slack_channel_id', diff --git a/server/src/routes/committees.ts b/server/src/routes/committees.ts index a04bd463a1..a484b701b0 100644 --- a/server/src/routes/committees.ts +++ b/server/src/routes/committees.ts @@ -313,6 +313,25 @@ export function createCommitteeRouters(): { updates.region = null; } + // Validate slug format and uniqueness if changing + if (updates.slug) { + const slugPattern = /^[a-z0-9-]+$/; + if (!slugPattern.test(updates.slug)) { + return res.status(400).json({ + error: 'Invalid slug', + message: 'Slug must contain only lowercase letters, numbers, and hyphens' + }); + } + + const slugAvailable = await workingGroupDb.isSlugAvailable(updates.slug, id); + if (!slugAvailable) { + return res.status(409).json({ + error: 'Slug already exists', + message: `A working group with slug '${updates.slug}' already exists` + }); + } + } + // Check if we're adding/changing a Slack channel const existingGroup = await workingGroupDb.getWorkingGroupById(id); const isAddingChannel = updates.slack_channel_url && (!existingGroup?.slack_channel_id || updates.slack_channel_url !== existingGroup.slack_channel_url);