()
| 983 | * Returns a router to be mounted at /api/content |
| 984 | */ |
| 985 | export function createContentRouter(): Router { |
| 986 | const router = Router(); |
| 987 | |
| 988 | // GET /api/content/collections - Get available collections for content submission |
| 989 | router.get('/collections', requireAuth, async (req, res) => { |
| 990 | try { |
| 991 | const user = req.user!; |
| 992 | const pool = getPool(); |
| 993 | |
| 994 | // Get public collections (anyone can submit) |
| 995 | const publicResult = await pool.query( |
| 996 | `SELECT id, slug, name, description |
| 997 | FROM working_groups |
| 998 | WHERE accepts_public_submissions = TRUE |
| 999 | ORDER BY name` |
| 1000 | ); |
| 1001 | |
| 1002 | // Get committees user is a member of (non-public ones) |
| 1003 | // Join with slack_user_mappings to handle users who were added as leader via Slack ID |
| 1004 | const memberResult = await pool.query( |
| 1005 | `SELECT wg.id, wg.slug, wg.name, wg.description, |
| 1006 | EXISTS( |
| 1007 | SELECT 1 FROM working_group_leaders wgl |
| 1008 | LEFT JOIN slack_user_mappings sm ON wgl.user_id = sm.slack_user_id AND sm.workos_user_id IS NOT NULL |
| 1009 | WHERE wgl.working_group_id = wg.id AND (wgl.user_id = $1 OR sm.workos_user_id = $1) |
| 1010 | ) as is_leader |
| 1011 | FROM working_group_memberships wgm |
| 1012 | JOIN working_groups wg ON wg.id = wgm.working_group_id |
| 1013 | WHERE wgm.workos_user_id = $1 |
| 1014 | AND wg.accepts_public_submissions = FALSE |
| 1015 | ORDER BY wg.name`, |
| 1016 | [user.id] |
| 1017 | ); |
| 1018 | |
| 1019 | const collections = [ |
| 1020 | ...publicResult.rows.map(row => ({ |
| 1021 | slug: row.slug, |
| 1022 | name: row.name, |
| 1023 | description: row.description, |
| 1024 | type: 'public' as const, |
| 1025 | can_publish_directly: false, // Public collections always require approval |
| 1026 | })), |
| 1027 | ...memberResult.rows.map(row => ({ |
| 1028 | slug: row.slug, |
| 1029 | name: row.name, |
| 1030 | description: row.description, |
| 1031 | type: 'committee' as const, |
| 1032 | can_publish_directly: row.is_leader, |
| 1033 | })), |
| 1034 | ]; |
| 1035 | |
| 1036 | res.json({ collections }); |
| 1037 | } catch (error) { |
| 1038 | logger.error({ err: error }, 'GET /api/content/collections error'); |
| 1039 | res.status(500).json({ |
| 1040 | error: 'Failed to get collections', |
| 1041 | }); |
| 1042 | } |
no test coverage detected