diff --git a/src/app.module.ts b/src/app.module.ts index cdb338a..3432acd 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -11,6 +11,7 @@ import { ChannelsModule } from './channels/channels.module'; import { MessagingModule } from './messaging/messaging.module'; import { EventsModule } from './events/events.module'; import { RealtimeModule } from './realtime/realtime.module'; +import { MembersModule } from './members/members.module'; @Module({ imports: [ @@ -19,6 +20,7 @@ import { RealtimeModule } from './realtime/realtime.module'; RealtimeModule, GuildsModule, ChannelsModule, + MembersModule, MessagingModule, ], controllers: [HealthController, MetricsController], diff --git a/src/main.ts b/src/main.ts index e58fe90..b359079 100644 --- a/src/main.ts +++ b/src/main.ts @@ -24,6 +24,23 @@ async function bootstrap() { validateEnv(); const app = await NestFactory.create(AppModule); + const corsOrigins = (process.env.FABRIC_BACKEND_GUILD_CORS_ORIGINS ?? '') + .split(',') + .map((x) => x.trim()) + .filter(Boolean); + + app.enableCors({ + origin: (origin, callback) => { + if (!origin) return callback(null, true); + if (origin === 'null') return callback(null, true); + if (!corsOrigins.length) return callback(null, true); + if (corsOrigins.includes(origin)) return callback(null, true); + return callback(new Error('CORS origin not allowed'), false); + }, + methods: ['GET', 'POST', 'PATCH', 'PUT', 'DELETE', 'OPTIONS'], + allowedHeaders: ['Content-Type', 'Authorization', 'x-client-name', 'x-request-id', 'x-api-key'], + credentials: false, + }); app.setGlobalPrefix('api'); const metrics = app.get(MetricsService); app.use(createRequestContextMiddleware('guild', metrics)); diff --git a/src/members/members.controller.ts b/src/members/members.controller.ts new file mode 100644 index 0000000..8b1c6f5 --- /dev/null +++ b/src/members/members.controller.ts @@ -0,0 +1,13 @@ +import { Controller, Get, Query } from '@nestjs/common'; +import { MembersService } from './members.service'; + +@Controller('members') +export class MembersController { + constructor(private readonly membersService: MembersService) {} + + @Get() + list(@Query('guildId') guildId?: string) { + return this.membersService.list(guildId); + } +} + diff --git a/src/members/members.module.ts b/src/members/members.module.ts new file mode 100644 index 0000000..42a389e --- /dev/null +++ b/src/members/members.module.ts @@ -0,0 +1,13 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { GuildMember } from '../entities/guild-member.entity'; +import { MembersController } from './members.controller'; +import { MembersService } from './members.service'; + +@Module({ + imports: [TypeOrmModule.forFeature([GuildMember])], + controllers: [MembersController], + providers: [MembersService], +}) +export class MembersModule {} + diff --git a/src/members/members.service.ts b/src/members/members.service.ts new file mode 100644 index 0000000..ed7f6f9 --- /dev/null +++ b/src/members/members.service.ts @@ -0,0 +1,20 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import { GuildMember } from '../entities/guild-member.entity'; + +@Injectable() +export class MembersService { + constructor( + @InjectRepository(GuildMember) + private readonly memberRepo: Repository, + ) {} + + list(guildId?: string) { + if (guildId) { + return this.memberRepo.find({ where: { guildId, status: 'active' }, order: { createdAt: 'ASC' }, take: 500 }); + } + return this.memberRepo.find({ where: { status: 'active' }, order: { createdAt: 'ASC' }, take: 500 }); + } +} +