Files
Fabric.Backend.Center/src/nodes/node-admin.service.ts
hzhang 44aa34d1ff refactor: migrate to ES modules
package.json type=module, tsconfig module/moduleResolution=NodeNext,
target es2022, explicit .js on all relative imports. Center: jsonwebtoken
& bcryptjs switched to default imports (ESM/CJS interop). Verified:
builds, boots, full auth + plugin round-trip work under ESM.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 18:47:35 +01:00

60 lines
1.7 KiB
TypeScript

import { ConflictException, Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import bcrypt from 'bcryptjs';
import { randomBytes } from 'crypto';
import { GuildNode } from '../entities/guild-node.entity.js';
import { AuditService } from '../audit/audit.service.js';
@Injectable()
export class NodeAdminService {
constructor(
@InjectRepository(GuildNode)
private readonly nodeRepo: Repository<GuildNode>,
private readonly audit: AuditService,
) {}
async registerNode(input: { nodeId: string; name: string; endpoint: string }) {
const existedByNodeId = await this.nodeRepo.findOne({ where: { nodeId: input.nodeId } });
if (existedByNodeId) {
throw new ConflictException('nodeId already exists');
}
const existedByEndpoint = await this.nodeRepo.findOne({ where: { endpoint: input.endpoint } });
if (existedByEndpoint) {
throw new ConflictException('endpoint already exists');
}
const node = this.nodeRepo.create({
nodeId: input.nodeId,
name: input.name,
endpoint: input.endpoint,
status: 'active',
apiKeyHash: null,
});
const rawApiKey = `gk_${randomBytes(24).toString('hex')}`;
node.apiKeyHash = await bcrypt.hash(rawApiKey, 10);
const saved = await this.nodeRepo.save(node);
await this.audit.write({
action: 'node.register',
targetType: 'node',
targetId: saved.nodeId,
detail: JSON.stringify({ endpoint: saved.endpoint, via: 'cli' }),
});
return {
node: {
id: saved.id,
nodeId: saved.nodeId,
name: saved.name,
endpoint: saved.endpoint,
status: saved.status,
},
apiKey: rawApiKey,
};
}
}