fixing rescue settings

This commit is contained in:
blaisadmin
2026-04-15 22:57:34 -04:00
parent ce2b7a15bf
commit 765d6c61db
8 changed files with 476 additions and 11 deletions
+96
View File
@@ -36,6 +36,7 @@ import {
listBirds,
listVetVisitsForBird,
listWeightsForBird,
transferBirdToWorkspace,
updateBird,
updateVetVisitForBird,
} from './repositories/birdRepository.js';
@@ -45,7 +46,10 @@ import {
claimWorkspaceInvites,
createWorkspace,
deleteWorkspaceMember,
deleteWorkspaceIfEmpty,
ensurePersonalWorkspaceForUser,
findAlternateWorkspaceForUser,
getWorkspaceBirdCount,
getPlatformAdminSummary,
getMembershipForUser,
getNextWorkspaceId,
@@ -158,6 +162,10 @@ const transferDraftSchema = z.object({
notes: z.string().trim().max(1000).optional().or(z.literal('')),
});
const flockTransferSchema = z.object({
targetWorkspaceId: z.coerce.number().int().positive(),
});
const birdSchema = z.object({
name: z.string().trim().min(1).max(120),
tagId: z.string().trim().min(1).max(80),
@@ -1226,6 +1234,55 @@ app.put('/api/workspace', requireAuth, requireWriteAccess, requireWorkspaceRole(
}
});
app.delete('/api/workspace', requireAuth, requireSessionAuth, requireWorkspaceRole(['owner']), async (req: Request, res: Response, next: NextFunction) => {
try {
if ((await getWorkspaceBirdCount(req.auth!.workspace.id)) > 0) {
res.status(409).json({ error: 'Remove or transfer all birds from this flock before deleting it.' });
return;
}
let nextWorkspaceId = await findAlternateWorkspaceForUser(req.auth!.user.id, req.auth!.workspace.id);
if (!nextWorkspaceId) {
const fallbackWorkspaceId = await getNextWorkspaceId();
const fallbackWorkspace = await createWorkspace({
id: fallbackWorkspaceId,
name: `${req.auth!.user.name}'s Flock`,
workspaceType: 'standard',
billingEmail: req.auth!.user.email,
billingPlan: 'household_basic',
owner: req.auth!.user,
});
nextWorkspaceId = fallbackWorkspace?.id ?? fallbackWorkspaceId;
}
await updateSessionWorkspace(req.auth!.session.id, nextWorkspaceId);
const deletion = await deleteWorkspaceIfEmpty(req.auth!.workspace.id);
if (!deletion.deleted) {
await updateSessionWorkspace(req.auth!.session.id, req.auth!.workspace.id);
res.status(404).json({ error: 'Flock not found.' });
return;
}
const updatedAuth = await resolveSessionAuth(hashToken(req.auth!.token), req.auth!.token);
if (!updatedAuth) {
throw new Error('Unable to reload session.');
}
res.json({
deletedWorkspaceId: req.auth!.workspace.id,
token: req.auth!.token,
session: await buildSessionPayload(updatedAuth),
});
} catch (error) {
next(error);
}
});
app.post(
'/api/workspace/rescue-status/cancel',
requireAuth,
@@ -1345,6 +1402,45 @@ app.post('/api/birds', requireAuth, requireWriteAccess, requireWorkspaceRole(['o
}
});
app.post('/api/birds/:birdId/transfer', requireAuth, requireWriteAccess, requireSessionAuth, requireWorkspaceRole(['owner', 'assistant']), async (req: Request, res: Response, next: NextFunction) => {
const parsed = flockTransferSchema.safeParse(req.body);
if (!parsed.success) {
res.status(400).json({ error: 'Invalid flock transfer payload', details: parsed.error.flatten() });
return;
}
if (parsed.data.targetWorkspaceId === req.auth!.workspace.id) {
res.status(400).json({ error: 'Choose a different destination flock.' });
return;
}
try {
const targetMembership = await getMembershipForUser(req.auth!.user.id, parsed.data.targetWorkspaceId);
if (!targetMembership) {
res.status(403).json({ error: 'You do not have access to that destination flock.' });
return;
}
const bird = await transferBirdToWorkspace(req.params.birdId, req.auth!.workspace.id, parsed.data.targetWorkspaceId);
if (!bird) {
res.status(404).json({ error: 'Bird not found.' });
return;
}
res.json({ bird: normalizeBird(bird) });
} catch (error) {
if (typeof error === 'object' && error && 'code' in error && error.code === '23505') {
res.status(409).json({ error: 'That band/tag ID is already in use in the destination flock.' });
return;
}
next(error);
}
});
app.put('/api/birds/:birdId', requireAuth, requireWriteAccess, requireWorkspaceRole(['owner', 'assistant', 'caregiver']), async (req: Request, res: Response, next: NextFunction) => {
const parsed = birdSchema.safeParse(req.body);