feat: Allow admin to create new text channels

This commit is contained in:
2026-03-30 01:25:56 +02:00
parent 109402cdd6
commit 83694570e3
24 changed files with 563 additions and 64 deletions

View File

@@ -16,6 +16,7 @@ export async function handleUpsertServer(command: UpsertServerCommand, dataSourc
maxUsers: server.maxUsers,
currentUsers: server.currentUsers,
tags: JSON.stringify(server.tags),
channels: JSON.stringify(server.channels ?? []),
createdAt: server.createdAt,
lastSeen: server.lastSeen
});

View File

@@ -3,10 +3,63 @@ import { ServerEntity } from '../entities/ServerEntity';
import { JoinRequestEntity } from '../entities/JoinRequestEntity';
import {
AuthUserPayload,
ServerChannelPayload,
ServerPayload,
JoinRequestPayload
} from './types';
function parseStringArray(raw: string | null | undefined): string[] {
try {
const parsed = JSON.parse(raw || '[]');
return Array.isArray(parsed)
? parsed.filter((value): value is string => typeof value === 'string')
: [];
} catch {
return [];
}
}
function parseServerChannels(raw: string | null | undefined): ServerChannelPayload[] {
try {
const parsed = JSON.parse(raw || '[]');
if (!Array.isArray(parsed)) {
return [];
}
const seenIds = new Set<string>();
const seenNames = new Set<string>();
return parsed
.filter((channel): channel is Record<string, unknown> => !!channel && typeof channel === 'object')
.map((channel, index) => {
const id = typeof channel.id === 'string' ? channel.id.trim() : '';
const name = typeof channel.name === 'string' ? channel.name.trim().replace(/\s+/g, ' ') : '';
const type = channel.type === 'text' || channel.type === 'voice' ? channel.type : null;
const position = typeof channel.position === 'number' ? channel.position : index;
const nameKey = name.toLocaleLowerCase();
if (!id || !name || !type || seenIds.has(id) || seenNames.has(nameKey)) {
return null;
}
seenIds.add(id);
seenNames.add(nameKey);
return {
id,
name,
type,
position
} satisfies ServerChannelPayload;
})
.filter((channel): channel is ServerChannelPayload => !!channel);
} catch {
return [];
}
}
export function rowToAuthUser(row: AuthUserEntity): AuthUserPayload {
return {
id: row.id,
@@ -29,7 +82,8 @@ export function rowToServer(row: ServerEntity): ServerPayload {
isPrivate: !!row.isPrivate,
maxUsers: row.maxUsers,
currentUsers: row.currentUsers,
tags: JSON.parse(row.tags || '[]'),
tags: parseStringArray(row.tags),
channels: parseServerChannels(row.channels),
createdAt: row.createdAt,
lastSeen: row.lastSeen
};

View File

@@ -28,6 +28,15 @@ export interface AuthUserPayload {
createdAt: number;
}
export type ServerChannelType = 'text' | 'voice';
export interface ServerChannelPayload {
id: string;
name: string;
type: ServerChannelType;
position: number;
}
export interface ServerPayload {
id: string;
name: string;
@@ -40,6 +49,7 @@ export interface ServerPayload {
maxUsers: number;
currentUsers: number;
tags: string[];
channels: ServerChannelPayload[];
createdAt: number;
lastSeen: number;
}