diff --git a/Fabric.Backend.Center/src/entities/guild-node.entity.ts b/Fabric.Backend.Center/src/entities/guild-node.entity.ts index a59f5ee..a79c2ba 100644 --- a/Fabric.Backend.Center/src/entities/guild-node.entity.ts +++ b/Fabric.Backend.Center/src/entities/guild-node.entity.ts @@ -1,4 +1,10 @@ -import { Column, CreateDateColumn, Entity, PrimaryGeneratedColumn } from 'typeorm'; +import { + Column, + CreateDateColumn, + Entity, + PrimaryGeneratedColumn, + UpdateDateColumn, +} from 'typeorm'; @Entity('guild_nodes') export class GuildNode { @@ -21,6 +27,12 @@ export class GuildNode { }) status!: 'active' | 'offline' | 'revoked'; + @Column({ type: 'datetime', nullable: true }) + lastHeartbeatAt!: Date | null; + @CreateDateColumn() createdAt!: Date; + + @UpdateDateColumn() + updatedAt!: Date; } diff --git a/Fabric.Backend.Center/src/nodes/nodes.controller.ts b/Fabric.Backend.Center/src/nodes/nodes.controller.ts index e9270f9..9bd9487 100644 --- a/Fabric.Backend.Center/src/nodes/nodes.controller.ts +++ b/Fabric.Backend.Center/src/nodes/nodes.controller.ts @@ -65,6 +65,27 @@ export class NodesController { }; } + @Post(':nodeId/heartbeat') + async heartbeat(@Param('nodeId') nodeId: string) { + const node = await this.nodeRepo.findOne({ where: { nodeId } }); + if (!node) { + throw new NotFoundException('node not found'); + } + + node.lastHeartbeatAt = new Date(); + if (node.status !== 'revoked') { + node.status = 'active'; + } + + const saved = await this.nodeRepo.save(node); + return { + status: 'ok', + nodeId: saved.nodeId, + nodeStatus: saved.status, + lastHeartbeatAt: saved.lastHeartbeatAt, + }; + } + @Patch(':nodeId/status') async updateStatus( @Param('nodeId') nodeId: string, diff --git a/docs/TODO-backend-center-guild.md b/docs/TODO-backend-center-guild.md index 972deae..8b48e70 100644 --- a/docs/TODO-backend-center-guild.md +++ b/docs/TODO-backend-center-guild.md @@ -27,7 +27,7 @@ - [x] node 唯一性校验(nodeId/endpoint) - [x] node 状态模型(active/offline/revoked) - [x] `GET /nodes` 列表 + 分页 -- [ ] node 心跳接口(可选) +- [x] node 心跳接口(可选) ### 1.3 Center 运维能力 - [ ] 审计日志(auth/node 关键操作)