feat(center-nodes): validate shared secret on node registration

This commit is contained in:
nav
2026-05-12 08:49:02 +00:00
parent 3ad8cc3a56
commit 0020df5d5e
4 changed files with 65 additions and 15 deletions

View File

@@ -0,0 +1,18 @@
import { IsString, IsUrl, MinLength } from 'class-validator';
export class RegisterNodeDto {
@IsString()
@MinLength(3)
nodeId!: string;
@IsString()
@MinLength(2)
name!: string;
@IsUrl({ require_tld: false })
endpoint!: string;
@IsString()
@MinLength(8)
sharedSecret!: string;
}

View File

@@ -1,24 +1,53 @@
import { Body, Controller, Get, Post } from '@nestjs/common'; import {
Body,
type NodeRegistration = { Controller,
nodeId?: string; ForbiddenException,
name?: string; Get,
endpoint?: string; Post,
handshakeProof?: string; } from '@nestjs/common';
}; import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { GuildNode } from '../entities/guild-node.entity';
import { RegisterNodeDto } from './dto.register-node.dto';
@Controller('nodes') @Controller('nodes')
export class NodesController { export class NodesController {
private readonly nodes: NodeRegistration[] = []; constructor(
@InjectRepository(GuildNode)
private readonly nodeRepo: Repository<GuildNode>,
) {}
@Post('register') @Post('register')
register(@Body() body: NodeRegistration) { async register(@Body() body: RegisterNodeDto) {
this.nodes.push(body); if (body.sharedSecret !== process.env.CENTER_SHARED_SECRET) {
return { status: 'accepted', handshake: 'todo-verify-shared-secret', node: body }; throw new ForbiddenException('invalid shared secret');
}
const node = this.nodeRepo.create({
nodeId: body.nodeId,
name: body.name,
endpoint: body.endpoint,
status: 'active',
});
const saved = await this.nodeRepo.save(node);
return {
status: 'accepted',
node: {
id: saved.id,
nodeId: saved.nodeId,
name: saved.name,
endpoint: saved.endpoint,
status: saved.status,
},
};
} }
@Get() @Get()
list() { async list() {
return { items: this.nodes }; const items = await this.nodeRepo.find({
order: { createdAt: 'DESC' },
});
return { items };
} }
} }

View File

@@ -1,7 +1,10 @@
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { NodesController } from './nodes.controller'; import { NodesController } from './nodes.controller';
import { GuildNode } from '../entities/guild-node.entity';
@Module({ @Module({
imports: [TypeOrmModule.forFeature([GuildNode])],
controllers: [NodesController], controllers: [NodesController],
}) })
export class NodesModule {} export class NodesModule {}

View File

@@ -23,7 +23,7 @@
- [x] DTO + 参数校验 + 错误码规范 - [x] DTO + 参数校验 + 错误码规范
### 1.2 Guild Node 注册与握手 ### 1.2 Guild Node 注册与握手
- [ ] `POST /nodes/register` shared-secret 校验 - [x] `POST /nodes/register` shared-secret 校验
- [ ] node 唯一性校验nodeId/endpoint - [ ] node 唯一性校验nodeId/endpoint
- [ ] node 状态模型active/offline/revoked - [ ] node 状态模型active/offline/revoked
- [ ] `GET /nodes` 列表 + 分页 - [ ] `GET /nodes` 列表 + 分页