Phase 6 Quick Reference - nself-org/nchat GitHub Wiki
Phase 6 Quick Reference Guide
Advanced Channels, Communities, and Structures
Quick Start
1. Apply Database Migration
cd .backend
psql -U postgres -d nchat -f migrations/040_advanced_channels_phase6.sql
2. Import Services
import { categoryService } from '@/services/channels/category.service'
import { communityService } from '@/services/channels/community.service'
import { broadcastService } from '@/services/channels/broadcast.service'
import { permissionService } from '@/services/channels/permission.service'
3. Use GraphQL Operations
import { GET_CATEGORIES_WITH_CHANNELS } from '@/graphql/channels/categories'
import { CREATE_COMMUNITY } from '@/graphql/channels/communities'
import { SEND_BROADCAST } from '@/graphql/channels/broadcasts'
Common Operations
Categories
Create Category
const category = await categoryService.createCategory({
workspaceId: 'workspace-id',
name: 'Engineering',
icon: '💻',
color: '#3b82f6',
})
Reorder Categories
await categoryService.reorderCategories(['category-1-id', 'category-2-id', 'category-3-id'])
Move Channel
await categoryService.moveChannel({
channelId: 'channel-id',
categoryId: 'new-category-id',
position: 0,
})
Sync Permissions
await categoryService.togglePermissionSync('category-id', true)
await categoryService.syncPermissions('category-id')
Communities (WhatsApp-style)
Create Community
const community = await communityService.createCommunity({
workspaceId: 'workspace-id',
name: 'Product Updates',
description: 'Latest product news',
announcementChannelId: 'announcement-channel-id',
addGroupsPermission: 'admin', // or 'member'
membersCanInvite: true,
maxGroups: 50,
maxMembers: 10000,
})
Add Group to Community
await communityService.addGroup({
communityId: 'community-id',
channelId: 'channel-id',
position: 0,
addedBy: 'user-id',
})
Remove Group
await communityService.removeGroup('community-id', 'channel-id')
Broadcast Lists
Create Broadcast List
const broadcastList = await broadcastService.createBroadcastList({
workspaceId: 'workspace-id',
name: 'Weekly Newsletter',
description: 'Weekly team updates',
ownerId: 'user-id',
subscriptionMode: 'open', // or 'invite', 'admin'
allowReplies: false,
trackDelivery: true,
trackReads: true,
maxSubscribers: 10000,
})
Subscribe Users
// Single subscriber
await broadcastService.subscribe('broadcast-list-id', 'user-id')
// Bulk subscribe
await broadcastService.bulkSubscribe({
broadcastListId: 'broadcast-list-id',
userIds: ['user-1', 'user-2', 'user-3'],
})
Send Broadcast
const message = await broadcastService.sendBroadcast({
broadcastListId: 'broadcast-list-id',
content: 'Important announcement!',
attachments: [
{ type: 'image', url: 'https://...' },
{ type: 'file', url: 'https://...' },
],
scheduledFor: new Date('2026-02-10T10:00:00Z'), // Optional
})
Track Delivery
// Delivery stats are automatically tracked via triggers
const message = await fetch(`/api/channels/broadcasts/messages/${messageId}`)
// Returns: { delivered_count, read_count, failed_count }
Permissions
Permission Flags
import { CHANNEL_PERMISSIONS } from '@/types/advanced-channels'
// Available permissions:
CHANNEL_PERMISSIONS.VIEW_CHANNEL
CHANNEL_PERMISSIONS.SEND_MESSAGES
CHANNEL_PERMISSIONS.MANAGE_CHANNEL
// ... 28 total permissions
Create Permission Override
// Create bitfield from permission names
const allowBits = permissionService.createBitfield([
'VIEW_CHANNEL',
'SEND_MESSAGES',
'ADD_REACTIONS',
])
const denyBits = permissionService.createBitfield(['MENTION_EVERYONE'])
// Apply override
await permissionService.createOverride({
channelId: 'channel-id',
targetType: 'role', // or 'user'
targetId: 'role-id',
allowPermissions: allowBits,
denyPermissions: denyBits,
createdBy: 'admin-user-id',
})
Check Permissions
// Check single permission
const canSend = await permissionService.hasPermission('channel-id', 'user-id', 'SEND_MESSAGES')
// Get all permissions
const permissions = await permissionService.calculatePermissions('channel-id', 'user-id')
// Returns: { VIEW_CHANNEL: true, SEND_MESSAGES: false, ... }
Work with Bitfields
// Parse bitfield to array
const permissions = permissionService.parseBitfield(12345n)
// Returns: ['VIEW_CHANNEL', 'SEND_MESSAGES', ...]
// Check specific permission in bitfield
const hasPermission = permissionService.hasPermissionInBitfield(12345n, 'MANAGE_CHANNEL')
Channel Subtypes
Promote to Supergroup (Telegram-style)
// Automatically when member count > 200
UPDATE nchat_channels
SET subtype = 'supergroup'
WHERE member_count > 200 AND type = 'group'
Create Gigagroup (Admin-only posting)
await createChannel({
name: 'Announcements',
type: 'public',
subtype: 'gigagroup',
isReadonly: true, // Only admins can post
})
Create Community Announcement Channel
const announcementChannel = await createChannel({
name: 'Community Updates',
type: 'public',
subtype: 'community_announcement',
isReadonly: true,
})
const community = await communityService.createCommunity({
name: 'My Community',
announcementChannelId: announcementChannel.id,
// ...
})
Database Queries
Get Categories with Channels
SELECT * FROM nchat.channels_with_categories
WHERE workspace_id = 'workspace-id'
ORDER BY category_position, position;
Get Broadcast Stats
SELECT * FROM nchat.broadcast_lists_with_stats
WHERE workspace_id = 'workspace-id';
Get Community Stats
SELECT * FROM nchat.communities_with_stats
WHERE workspace_id = 'workspace-id';
Check Permission Override
SELECT * FROM nchat.nchat_channel_permission_overrides
WHERE channel_id = 'channel-id'
AND target_type = 'role'
AND target_id = 'role-id';
GraphQL Examples
Query Categories with Channels
query GetCategoriesWithChannels($workspaceId: uuid!) {
nchat_channel_categories(
where: { workspace_id: { _eq: $workspaceId } }
order_by: { position: asc }
) {
id
name
icon
color
position
channels(where: { is_archived: { _eq: false } }, order_by: { position: asc }) {
id
name
type
subtype
member_count
}
}
}
Create Community
mutation CreateCommunity($input: CreateCommunityInput!) {
insert_nchat_communities_one(object: $input) {
id
name
announcement_channel {
id
name
}
}
}
Subscribe to Broadcasts
subscription SubscribeToBroadcastMessages($broadcastListId: uuid!) {
nchat_broadcast_messages(
where: { broadcast_list_id: { _eq: $broadcastListId } }
order_by: { sent_at: desc }
limit: 50
) {
id
content
sent_at
delivered_count
read_count
}
}
API Endpoints
Categories
GET /api/channels/categories?workspaceId=xxx- ListPOST /api/channels/categories- CreatePATCH /api/channels/categories/:id- UpdateDELETE /api/channels/categories/:id- DeletePOST /api/channels/categories/reorder- ReorderPOST /api/channels/move- Move channel
Communities
GET /api/channels/communities?workspaceId=xxx- ListPOST /api/channels/communities- CreateGET /api/channels/communities/:id- GetPOST /api/channels/communities/:id/groups- Add groupDELETE /api/channels/communities/:id/groups/:channelId- Remove
Broadcasts
GET /api/channels/broadcasts?workspaceId=xxx- ListPOST /api/channels/broadcasts- CreatePOST /api/channels/broadcasts/:id/send- Send messagePOST /api/channels/broadcasts/:id/subscribe- SubscribePOST /api/channels/broadcasts/:id/bulk-subscribe- Bulk
Permissions
GET /api/channels/:id/permissions- List overridesPOST /api/channels/permissions- Create overrideGET /api/channels/:id/permissions/calculate?userId=xxx- Calculate
TypeScript Types
import type {
ChannelCategory,
CategoryWithChannels,
CreateCategoryInput,
UpdateCategoryInput,
Community,
CommunityWithGroups,
CreateCommunityInput,
AddCommunityGroupInput,
BroadcastList,
BroadcastMessage,
BroadcastDelivery,
CreateBroadcastListInput,
SendBroadcastInput,
ChannelPermissionOverride,
CreatePermissionOverrideInput,
ChannelPermission,
ChannelSubtype,
SubscriptionMode,
} from '@/types/advanced-channels'
Testing
Unit Tests
import { permissionService } from '@/services/channels/permission.service'
describe('PermissionService', () => {
it('creates bitfield from permissions', () => {
const bitfield = permissionService.createBitfield(['VIEW_CHANNEL', 'SEND_MESSAGES'])
expect(bitfield).toBeGreaterThan(0n)
})
it('parses bitfield to permissions', () => {
const permissions = permissionService.parseBitfield(7n)
expect(permissions).toContain('VIEW_CHANNEL')
})
})
Integration Tests
describe('Category API', () => {
it('creates and retrieves category', async () => {
const created = await categoryService.createCategory({
workspaceId: 'test-workspace',
name: 'Test Category',
})
const categories = await categoryService.getCategories()
expect(categories).toContainEqual(created)
})
})
Troubleshooting
Categories not appearing
- Check
workspace_idmatches default:ffffffff-ffff-ffff-ffff-ffffffffffff - Verify migration ran successfully
- Check user has permission to view workspace
Permissions not applying
- Verify permission override exists in database
- Check bitfield calculation (allow takes precedence over deny)
- Ensure category permission sync is enabled if using categories
Broadcast delivery not tracking
- Verify
track_deliveryis enabled on broadcast list - Check triggers are installed (
update_delivery_counts) - Ensure delivery records are created when sending
Community groups not showing
- Verify channel is not archived
- Check
positionfield for ordering - Ensure user has access to both community and channel
Performance Tips
- Cache category structures - They rarely change
- Batch permission calculations - Don't check per-message
- Lazy-load broadcast deliveries - Only load when viewing details
- Use views -
channels_with_categories,broadcast_lists_with_stats - Index properly - Composite indexes on
(workspace_id, position)
Security Notes
- Validate workspace ownership before modifying categories
- Check permissions before creating broadcast lists
- Rate limit broadcast sending (prevent spam)
- Sanitize content in broadcasts
- Validate bitfields before applying permissions
Migration Notes
- All existing channels moved to default workspace
- Public/private channels assigned to "General" category
- No breaking changes to existing functionality
- Categories, communities, broadcasts are optional features
- Permission system augments (not replaces) RBAC
Support
For issues or questions:
- Check implementation summary:
docs/Phase-6-Implementation-Summary.md - Review type definitions:
src/types/advanced-channels.ts - Examine database schema:
.backend/migrations/040_advanced_channels_phase6.sql - Test with GraphQL playground: http://localhost:8080/console
Last Updated: February 3, 2026 Version: 1.0.0 Status: Production Ready ✅