feat(hosts): generate unique 8-digit asset ID
Add a Generate button to the host create dialog that fetches a random 8-digit asset ID from the new GET /hosts/generate-asset-id endpoint. The service retries against the unique index so the returned ID is guaranteed unused. Edit mode hides the button. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -29,6 +29,15 @@ export async function get(req: Request<{ id: string }>, res: Response, next: Nex
|
||||
}
|
||||
}
|
||||
|
||||
export async function generateAssetId(_req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const result = await prisma.$transaction((tx) => svc.generateAssetId(tx));
|
||||
res.json(result);
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
}
|
||||
|
||||
export async function create(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const input = req.validated!.body as CreateHostRequest;
|
||||
|
||||
@@ -13,6 +13,7 @@ const router = Router();
|
||||
|
||||
router.get('/', requireAuth, validate('query', HostListQuery), ctrl.list);
|
||||
router.post('/', requireAuth, requireRole('ADMIN'), validate('body', CreateHostRequest), ctrl.create);
|
||||
router.get('/generate-asset-id', requireAuth, requireRole('ADMIN'), ctrl.generateAssetId);
|
||||
router.get('/:id', requireAuth, ctrl.get);
|
||||
router.get('/:id/deployed-parts', requireAuth, ctrl.listDeployedParts);
|
||||
router.get('/:id/timeline', requireAuth, validate('query', HostTimelineQuery), ctrl.getTimeline);
|
||||
|
||||
@@ -41,6 +41,18 @@ export function get(tx: Tx, id: string) {
|
||||
return tx.host.findUnique({ where: { id } });
|
||||
}
|
||||
|
||||
// Random 8-digit asset ID (zero-padded) that isn't already taken. With ~100M
|
||||
// possible values and only hundreds of hosts in practice, collisions are rare
|
||||
// — we still retry a few times to be safe, then bail instead of looping forever.
|
||||
export async function generateAssetId(tx: Tx): Promise<{ assetId: string }> {
|
||||
for (let attempt = 0; attempt < 20; attempt++) {
|
||||
const candidate = String(Math.floor(Math.random() * 100_000_000)).padStart(8, '0');
|
||||
const existing = await tx.host.findUnique({ where: { assetId: candidate }, select: { id: true } });
|
||||
if (!existing) return { assetId: candidate };
|
||||
}
|
||||
throw errors.conflict('Could not generate a unique asset ID');
|
||||
}
|
||||
|
||||
export function listDeployedParts(tx: Tx, hostId: string) {
|
||||
return tx.part.findMany({
|
||||
where: { hostId, state: 'DEPLOYED' },
|
||||
|
||||
Reference in New Issue
Block a user