Keep adoption transfer codes stable
Deploy / deploy-dev (push) Has been skipped
Deploy / deploy-prod (push) Successful in 2m17s

This commit is contained in:
Corey Blais
2026-06-03 12:10:27 -04:00
parent 7b7171c109
commit 0411ec5175
5 changed files with 116 additions and 40 deletions
+54 -38
View File
@@ -48,6 +48,7 @@ import {
getBirdById,
getBirdByPublicProfileCode,
getOpenBirdTransferCode,
getOpenBirdTransferCodeForBird,
listBirds,
listDueBirdMilestoneReminders,
listMemorializedBirds,
@@ -654,6 +655,33 @@ const normalizeBird = (row: BirdRow) => ({
const createBirdTransferCodeValue = () => crypto.randomBytes(12).toString('base64url');
const ensureOpenBirdTransferCode = async (birdId: string, sourceWorkspaceId: number, requestedByUserId: string) => {
const existingTransferCode = await getOpenBirdTransferCodeForBird(birdId, sourceWorkspaceId);
if (existingTransferCode) {
return existingTransferCode;
}
for (let attempt = 0; attempt < 3; attempt += 1) {
try {
return await createBirdTransferCode({
code: createBirdTransferCodeValue(),
birdId,
sourceWorkspaceId,
requestedByUserId,
});
} catch (error) {
if (typeof error === 'object' && error && 'code' in error && error.code === '23505' && attempt < 2) {
continue;
}
throw error;
}
}
return null;
};
const normalizePublicBirdProfile = (row: BirdRow) => ({
id: row.id,
workspaceId: row.workspace_id,
@@ -3272,25 +3300,7 @@ app.post(
return;
}
let transferCode = null;
for (let attempt = 0; attempt < 3; attempt += 1) {
try {
transferCode = await createBirdTransferCode({
code: createBirdTransferCodeValue(),
birdId: sourceBird.id,
sourceWorkspaceId: req.auth!.workspace.id,
requestedByUserId: req.auth!.user.id,
});
break;
} catch (error) {
if (typeof error === 'object' && error && 'code' in error && error.code === '23505' && attempt < 2) {
continue;
}
throw error;
}
}
const transferCode = await ensureOpenBirdTransferCode(sourceBird.id, req.auth!.workspace.id, req.auth!.user.id);
if (!transferCode) {
throw new Error('Unable to create bird transfer code.');
@@ -3312,6 +3322,30 @@ app.post(
},
);
app.get('/api/birds/:birdId/transfer-code', requireAuth, requireWriteAccess, requireSessionAuth, requireWorkspaceRole(['owner', 'assistant']), async (req: Request, res: Response, next: NextFunction) => {
try {
const sourceBird = await getBirdById(req.params.birdId, req.auth!.workspace.id);
if (!sourceBird) {
res.status(404).json({ error: 'Bird not found.' });
return;
}
const transferCode = await getOpenBirdTransferCodeForBird(sourceBird.id, req.auth!.workspace.id);
res.json({
transferCode: transferCode
? {
code: transferCode.code,
bird: normalizeBird(sourceBird),
}
: null,
});
} catch (error) {
next(error);
}
});
app.post(
'/api/bird-transfer-codes/:code/accept',
requireAuth,
@@ -3384,25 +3418,7 @@ app.post(
return;
}
let transferCode = null;
for (let attempt = 0; attempt < 3; attempt += 1) {
try {
transferCode = await createBirdTransferCode({
code: createBirdTransferCodeValue(),
birdId: sourceBird.id,
sourceWorkspaceId: req.auth!.workspace.id,
requestedByUserId: req.auth!.user.id,
});
break;
} catch (error) {
if (typeof error === 'object' && error && 'code' in error && error.code === '23505' && attempt < 2) {
continue;
}
throw error;
}
}
const transferCode = await ensureOpenBirdTransferCode(sourceBird.id, req.auth!.workspace.id, req.auth!.user.id);
if (!transferCode) {
throw new Error('Unable to create bird transfer code.');
+5 -1
View File
@@ -154,8 +154,12 @@ const drawSimpleWeightChart = (doc: PDFKit.PDFDocument, weights: WeightRow[], bi
}
const latestDate = new Date(`${plottedWeights[plottedWeights.length - 1].recorded_on.slice(0, 10)}T00:00:00Z`);
const earliestDate = new Date(`${plottedWeights[0].recorded_on.slice(0, 10)}T00:00:00Z`);
const startDate = new Date(latestDate);
startDate.setUTCDate(startDate.getUTCDate() - 29);
startDate.setUTCDate(startDate.getUTCDate() - 13);
if (earliestDate > startDate) {
startDate.setTime(earliestDate.getTime());
}
const visibleWeights = plottedWeights.filter((entry) => {
const recordedOn = new Date(`${entry.recorded_on.slice(0, 10)}T00:00:00Z`);
return recordedOn >= startDate && recordedOn <= latestDate;
+1 -1
View File
@@ -12,7 +12,7 @@ import { getSignedS3ObjectUrl } from '../storage/s3Client.js';
import type { BirdRow } from '../types.js';
import { renderAdoptionReportPdf } from './adoptionReport.js';
const adoptionReportWeightHistoryDays = 425;
const adoptionReportWeightHistoryDays = 14;
const parseDataImage = (value: string | null) => {
if (!value) {
@@ -734,6 +734,22 @@ export const createBirdTransferCode = async ({
return result.rows[0] ?? null;
};
export const getOpenBirdTransferCodeForBird = async (birdId: string, sourceWorkspaceId: number) => {
const result = await db.query<BirdTransferCodeRow>(
`SELECT id, code, bird_id, source_workspace_id, requested_by_user_id, completed_at::text, completed_workspace_id, revoked_at::text, created_at
FROM bird_transfer_codes
WHERE bird_id = $1
AND source_workspace_id = $2
AND completed_at IS NULL
AND revoked_at IS NULL
ORDER BY created_at DESC
LIMIT 1`,
[birdId, sourceWorkspaceId],
);
return result.rows[0] ?? null;
};
export const getOpenBirdTransferCode = async (code: string) => {
const result = await db.query<
BirdRow & {