added qr, cleaned up profile views, and added the critical alerts

This commit is contained in:
blaisadmin
2026-05-20 21:54:17 -04:00
parent f2c506ec16
commit 1c0d57299d
9 changed files with 949 additions and 60 deletions
+42
View File
@@ -44,6 +44,7 @@ import {
deleteMedicationForBird,
deleteVetVisitForBird,
getBirdById,
getBirdByPublicProfileCode,
listBirds,
listDueBirdMilestoneReminders,
listMemorializedBirds,
@@ -231,6 +232,8 @@ const lostBirdReportSchema = z.object({
message: z.string().trim().max(1000).optional().or(z.literal('')),
});
const publicProfileCodeSchema = z.string().trim().regex(/^[A-Za-z0-9_-]{8,32}$/);
const birdSchema = z.object({
name: z.string().trim().min(1).max(120),
tagId: z.string().trim().max(80).optional().or(z.literal('')),
@@ -245,6 +248,7 @@ const birdSchema = z.object({
photoDataUrl: z.union([photoDataUrlSchema, photoUrlSchema, z.literal('')]).optional(),
notifyOnDob: z.boolean().optional(),
notifyOnGotchaDay: z.boolean().optional(),
publicProfileEnabled: z.boolean().optional(),
});
const memorializeBirdSchema = z.object({
@@ -325,6 +329,7 @@ const hashToken = (token: string) => crypto.createHash('sha256').update(token).d
const createSessionToken = () => crypto.randomBytes(32).toString('hex');
const photoAccessSecret = process.env.PHOTO_ACCESS_SECRET?.trim() || process.env.S3_SECRET_ACCESS_KEY?.trim() || createSessionToken();
const createIntegrationToken = () => `flpt_${crypto.randomBytes(24).toString('hex')}`;
const createPublicProfileCode = () => crypto.randomBytes(9).toString('base64url');
const createRandomId = () => crypto.randomUUID();
const createCodeVerifier = () => crypto.randomBytes(32).toString('base64url');
const createCodeChallenge = (verifier: string) => crypto.createHash('sha256').update(verifier).digest('base64url');
@@ -579,6 +584,8 @@ const normalizeBird = (row: BirdRow) => ({
photoUpdatedAt: row.photo_updated_at,
notifyOnDob: row.notify_on_dob,
notifyOnGotchaDay: row.notify_on_gotcha_day,
publicProfileCode: row.public_profile_code ?? null,
publicProfileEnabled: row.public_profile_enabled ?? false,
memorializedAt: row.memorialized_at,
memorializedOn: row.memorialized_on,
memorialNote: row.memorial_note,
@@ -588,6 +595,15 @@ const normalizeBird = (row: BirdRow) => ({
latestRecordedOn: row.latest_recorded_on,
});
const normalizePublicBirdProfile = (row: BirdRow) => ({
id: row.id,
workspaceId: row.workspace_id,
name: row.name,
gender: row.gender,
dateOfBirth: row.date_of_birth,
photoDataUrl: getBirdPhotoUrl(row),
});
const normalizeWeight = (row: WeightRow) => ({
id: row.id,
birdId: row.bird_id,
@@ -1949,6 +1965,28 @@ app.post('/api/lost-bird/report', lostBirdReportLimiter, async (req: Request, re
}
});
app.get('/api/public/birds/:publicProfileCode', async (req: Request, res: Response, next: NextFunction) => {
const parsed = publicProfileCodeSchema.safeParse(req.params.publicProfileCode);
if (!parsed.success) {
res.status(404).json({ error: 'Public bird profile not found.' });
return;
}
try {
const bird = await getBirdByPublicProfileCode(parsed.data);
if (!bird) {
res.status(404).json({ error: 'Public bird profile not found.' });
return;
}
res.json({ bird: normalizePublicBirdProfile(bird) });
} catch (error) {
next(error);
}
});
app.get('/api/auth/providers', (_req: Request, res: Response) => {
res.json({
providers: Object.values(oauthProviders).map((provider) => ({
@@ -2858,6 +2896,8 @@ app.post('/api/birds', requireAuth, requireWriteAccess, requireWorkspaceRole(['o
photoUpdatedAt: photoStorage.photoUpdatedAt,
notifyOnDob: parsed.data.notifyOnDob ?? false,
notifyOnGotchaDay: parsed.data.notifyOnGotchaDay ?? false,
publicProfileCode: createPublicProfileCode(),
publicProfileEnabled: parsed.data.publicProfileEnabled ?? false,
});
uploadedObjectKeyToCleanup = null;
@@ -2998,6 +3038,8 @@ app.put('/api/birds/:birdId', requireAuth, requireWriteAccess, requireWorkspaceR
photoUpdatedAt: photoStorage.photoUpdatedAt,
notifyOnDob: parsed.data.notifyOnDob ?? false,
notifyOnGotchaDay: parsed.data.notifyOnGotchaDay ?? false,
publicProfileCode: existingBird.public_profile_code ?? createPublicProfileCode(),
publicProfileEnabled: parsed.data.publicProfileEnabled ?? false,
});
if (!bird) {