feat: add category override API endpoints, fix channel categoryId reassignment, include isPrivate on categories

- Add GET/PUT/DELETE /api/categories/:id/overrides endpoints with
  privilege escalation guard matching existing channel override pattern
- Add isCategoryPrivate() and broadcastCategoryOverrideChange() helpers
  that cascade visibility changes to all channels in a category
- Fix PATCH /api/channels/:id to recompute permissions via
  broadcastOverrideChange when categoryId changes (different category
  overrides may apply)
- Include isPrivate flag on categories in GET /api/spaces/:id payload
  using batch-fetched @everyone overrides
- Include isPrivate on categories in broadcastChannelLayout and
  PATCH /api/categories/:id broadcasts
This commit is contained in:
Jannis Braun
2026-03-21 18:29:25 +01:00
parent b5ac7d3ee4
commit 637e083e9a
2 changed files with 226 additions and 10 deletions
+17
View File
@@ -301,11 +301,28 @@ export async function spaceRoutes(app: FastifyInstance): Promise<void> {
.from(schema.channelCategories)
.where(eq(schema.channelCategories.spaceId, id))
.all();
// Batch-fetch category overrides for @everyone to determine isPrivate
const catEveryoneOverrides = db.select().from(schema.categoryOverrides)
.where(and(
eq(schema.categoryOverrides.targetType, 'role'),
eq(schema.categoryOverrides.targetId, id),
))
.all();
const privateCategoryIds = new Set<string>();
for (const o of catEveryoneOverrides) {
const denyBits = BigInt(o.deny || '0');
if ((denyBits & PermissionBits.VIEW_CHANNEL) !== 0n) {
privateCategoryIds.add(o.categoryId);
}
}
const categories: ChannelCategory[] = categoryRows.map(c => ({
id: c.id,
spaceId: c.spaceId,
name: c.name,
position: c.position ?? 0,
isPrivate: privateCategoryIds.has(c.id),
createdAt: c.createdAt,
}));