feat(guild): required channel x_type enum

Channel.x_type enum(general|work|report|discuss|triage|custom); required
and validated on channel creation (400 if missing/invalid).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
h z
2026-05-15 09:35:36 +01:00
parent 774dff11ba
commit 605d3ac092
3 changed files with 18 additions and 0 deletions

View File

@@ -24,6 +24,7 @@ export class ChannelsController {
guildId: body.guildId as string | undefined, guildId: body.guildId as string | undefined,
name: body.name as string | undefined, name: body.name as string | undefined,
kind: body.kind as string | undefined, kind: body.kind as string | undefined,
xType: body.xType as string | undefined,
isPublic: Boolean(body.isPublic), isPublic: Boolean(body.isPublic),
memberUserIds: Array.isArray(body.memberUserIds) ? (body.memberUserIds as string[]) : [], memberUserIds: Array.isArray(body.memberUserIds) ? (body.memberUserIds as string[]) : [],
}, },

View File

@@ -4,10 +4,14 @@ import { In, Repository } from 'typeorm';
import { Channel } from '../entities/channel.entity'; import { Channel } from '../entities/channel.entity';
import { ChannelMember } from '../entities/channel-member.entity'; import { ChannelMember } from '../entities/channel-member.entity';
const X_TYPES = ['general', 'work', 'report', 'discuss', 'triage', 'custom'] as const;
type XType = (typeof X_TYPES)[number];
type CreateChannelInput = { type CreateChannelInput = {
guildId?: string; guildId?: string;
name?: string; name?: string;
kind?: string; kind?: string;
xType?: string;
isPublic?: boolean; isPublic?: boolean;
memberUserIds?: string[]; memberUserIds?: string[];
}; };
@@ -44,14 +48,20 @@ export class ChannelsService {
async create(input: CreateChannelInput, creatorUserId: string) { async create(input: CreateChannelInput, creatorUserId: string) {
const guildId = String(input.guildId ?? '').trim(); const guildId = String(input.guildId ?? '').trim();
const name = String(input.name ?? '').trim(); const name = String(input.name ?? '').trim();
const xType = String(input.xType ?? '').trim() as XType;
if (!guildId) throw new BadRequestException('guildId is required'); if (!guildId) throw new BadRequestException('guildId is required');
if (!name) throw new BadRequestException('name is required'); if (!name) throw new BadRequestException('name is required');
if (!creatorUserId) throw new BadRequestException('creator is required'); if (!creatorUserId) throw new BadRequestException('creator is required');
if (!input.xType) throw new BadRequestException('xType is required');
if (!X_TYPES.includes(xType)) {
throw new BadRequestException(`xType must be one of: ${X_TYPES.join(', ')}`);
}
const channel = await this.channelRepo.save( const channel = await this.channelRepo.save(
this.channelRepo.create({ this.channelRepo.create({
guildId, guildId,
name, name,
xType,
kind: input.kind === 'announcement' ? 'announcement' : 'text', kind: input.kind === 'announcement' ? 'announcement' : 'text',
isPrivate: !input.isPublic, isPrivate: !input.isPublic,
isPublic: Boolean(input.isPublic), isPublic: Boolean(input.isPublic),

View File

@@ -13,6 +13,13 @@ export class Channel {
@Column({ type: 'varchar', length: 120 }) @Column({ type: 'varchar', length: 120 })
name!: string; name!: string;
@Column({
name: 'x_type',
type: 'enum',
enum: ['general', 'work', 'report', 'discuss', 'triage', 'custom'],
})
xType!: 'general' | 'work' | 'report' | 'discuss' | 'triage' | 'custom';
@Column({ type: 'varchar', length: 16, default: 'text' }) @Column({ type: 'varchar', length: 16, default: 'text' })
kind!: 'text' | 'announcement'; kind!: 'text' | 'announcement';