feat: scaffold center and guild backend NestJS skeletons

This commit is contained in:
nav
2026-05-12 08:31:43 +00:00
parent 026be99393
commit 88bec71cf8
26 changed files with 3597 additions and 0 deletions

2
Fabric.Backend.Guild/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
node_modules/
dist/

1640
Fabric.Backend.Guild/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,23 @@
{
"name": "fabric-backend-guild",
"version": "0.1.0",
"private": true,
"description": "Fabric Guild Node service",
"scripts": {
"build": "tsc -p tsconfig.build.json",
"start": "node dist/main.js",
"start:dev": "ts-node src/main.ts"
},
"dependencies": {
"@nestjs/common": "^10.4.8",
"@nestjs/core": "^10.4.8",
"@nestjs/platform-express": "^10.4.8",
"reflect-metadata": "^0.2.2",
"rxjs": "^7.8.1"
},
"devDependencies": {
"@types/node": "^22.10.1",
"ts-node": "^10.9.2",
"typescript": "^5.7.2"
}
}

View File

@@ -0,0 +1,11 @@
import { Module } from '@nestjs/common';
import { HealthController } from './common/health.controller';
import { GuildsModule } from './guilds/guilds.module';
import { ChannelsModule } from './channels/channels.module';
import { MessagingModule } from './messaging/messaging.module';
@Module({
imports: [GuildsModule, ChannelsModule, MessagingModule],
controllers: [HealthController],
})
export class AppModule {}

View File

@@ -0,0 +1,9 @@
import { Body, Controller, Post } from '@nestjs/common';
@Controller('channels')
export class ChannelsController {
@Post()
create(@Body() body: Record<string, unknown>) {
return { status: 'todo', action: 'create-channel', received: body };
}
}

View File

@@ -0,0 +1,7 @@
import { Module } from '@nestjs/common';
import { ChannelsController } from './channels.controller';
@Module({
controllers: [ChannelsController],
})
export class ChannelsModule {}

View File

@@ -0,0 +1,9 @@
import { Controller, Get } from '@nestjs/common';
@Controller('healthz')
export class HealthController {
@Get()
get() {
return { ok: true, service: 'guild' };
}
}

View File

@@ -0,0 +1,9 @@
import { Body, Controller, Post } from '@nestjs/common';
@Controller('guilds')
export class GuildsController {
@Post()
create(@Body() body: Record<string, unknown>) {
return { status: 'todo', action: 'create-guild', received: body };
}
}

View File

@@ -0,0 +1,7 @@
import { Module } from '@nestjs/common';
import { GuildsController } from './guilds.controller';
@Module({
controllers: [GuildsController],
})
export class GuildsModule {}

View File

@@ -0,0 +1,13 @@
import 'reflect-metadata';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.setGlobalPrefix('api');
const port = process.env.PORT ? Number(process.env.PORT) : 7002;
await app.listen(port);
console.log(`Fabric.Backend.Guild listening on :${port}`);
}
void bootstrap();

View File

@@ -0,0 +1,62 @@
import { Body, Controller, Delete, Get, Param, Patch, Post, Query } from '@nestjs/common';
type Message = {
messageId: string;
seq: number;
content: string;
};
@Controller('channels/:id/messages')
export class MessagingController {
private seqByChannel = new Map<string, number>();
private messagesByChannel = new Map<string, Message[]>();
@Post()
create(@Param('id') channelId: string, @Body() body: { content?: string; messageId?: string }) {
const next = (this.seqByChannel.get(channelId) ?? 0) + 1;
this.seqByChannel.set(channelId, next);
const message: Message = {
messageId: body.messageId ?? `m-${channelId}-${next}`,
seq: next,
content: body.content ?? '',
};
const arr = this.messagesByChannel.get(channelId) ?? [];
arr.push(message);
this.messagesByChannel.set(channelId, arr);
return message;
}
@Patch(':messageId')
edit(@Param('id') channelId: string, @Param('messageId') messageId: string, @Body() body: { content?: string }) {
const arr = this.messagesByChannel.get(channelId) ?? [];
const item = arr.find((m) => m.messageId === messageId);
if (!item) return { status: 'not_found' };
item.content = body.content ?? item.content;
return item;
}
@Delete(':messageId')
remove(@Param('id') channelId: string, @Param('messageId') messageId: string) {
const arr = this.messagesByChannel.get(channelId) ?? [];
const next = arr.filter((m) => m.messageId !== messageId);
this.messagesByChannel.set(channelId, next);
return { status: 'deleted', messageId };
}
@Get()
listBySeq(
@Param('id') channelId: string,
@Query('seq_from') seqFrom?: string,
@Query('seq_to') seqTo?: string,
) {
const from = seqFrom ? Number(seqFrom) : 1;
const to = seqTo ? Number(seqTo) : Number.MAX_SAFE_INTEGER;
const arr = this.messagesByChannel.get(channelId) ?? [];
return {
items: arr.filter((m) => m.seq >= from && m.seq <= to),
};
}
}

View File

@@ -0,0 +1,7 @@
import { Module } from '@nestjs/common';
import { MessagingController } from './messaging.controller';
@Module({
controllers: [MessagingController],
})
export class MessagingModule {}

View File

@@ -0,0 +1,7 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"declaration": false
},
"exclude": ["node_modules", "dist"]
}

View File

@@ -0,0 +1,15 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es2020",
"strict": true,
"esModuleInterop": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"skipLibCheck": true
},
"include": ["src/**/*.ts"]
}