additional image changes

This commit is contained in:
blaisadmin
2026-05-02 12:11:20 -04:00
parent ac1afc613f
commit 6dbe51410c
5 changed files with 33 additions and 3 deletions
+1
View File
@@ -10,6 +10,7 @@ S3_ACCESS_KEY_ID=
S3_SECRET_ACCESS_KEY=
S3_PUBLIC_BASE_URL=
S3_KEY_PREFIX=bird-photos
PHOTO_DELIVERY_MODE=proxy
FRONTEND_URL=http://localhost:3000
BACKEND_URL=http://localhost:5000
VITE_API_BASE_URL=http://localhost:5000/api
+2 -1
View File
@@ -124,8 +124,9 @@ Set these when Wasabi image storage is ready:
- `S3_SECRET_ACCESS_KEY=<secret-key>`
- `S3_PUBLIC_BASE_URL=<optional CDN or public bucket base URL; leave blank for private signed URLs>`
- `S3_KEY_PREFIX=bird-photos`
- `PHOTO_DELIVERY_MODE=proxy`
Use a dedicated private bucket and access key for FlockPal images. Grant only the S3 permissions the app needs for that bucket. When `S3_PUBLIC_BASE_URL` is blank, FlockPal stores private object keys and returns short-lived signed URLs for bird photos.
Use a dedicated private bucket and access key for FlockPal images. Grant only the S3 permissions the app needs for that bucket. When `S3_PUBLIC_BASE_URL` is blank, FlockPal stores private object keys. `PHOTO_DELIVERY_MODE=proxy` streams images through the backend after validating the app photo token; `PHOTO_DELIVERY_MODE=redirect` validates the app token and redirects to a short-lived Wasabi signed URL.
Migrate existing Postgres-stored bird photos after deploying S3 image storage:
+25 -1
View File
@@ -134,6 +134,7 @@ const trustProxy = process.env.TRUST_PROXY?.trim() ?? '';
const milestoneRemindersEnabled = (process.env.MILESTONE_REMINDERS_ENABLED ?? 'true').toLowerCase() !== 'false';
const milestoneReminderTimeZone = process.env.MILESTONE_REMINDER_TIME_ZONE?.trim() || 'America/New_York';
const milestoneReminderCheckIntervalMs = 60 * 60 * 1000;
const photoDeliveryMode = process.env.PHOTO_DELIVERY_MODE === 'redirect' ? 'redirect' : 'proxy';
if (trustProxy) {
app.set('trust proxy', trustProxy === 'true' ? true : Number(trustProxy) || trustProxy);
@@ -2766,8 +2767,31 @@ app.get('/api/birds/:birdId/photo', async (req: Request, res: Response, next: Ne
expiresInSeconds: 5 * 60,
});
res.setHeader('Cache-Control', 'private, max-age=300');
res.setHeader('Cache-Control', 'private, max-age=900');
if (photoDeliveryMode === 'redirect') {
res.redirect(302, signedUrl);
return;
}
const imageResponse = await fetch(signedUrl);
if (!imageResponse.ok) {
res.status(imageResponse.status).json({ error: 'Unable to load bird photo.' });
return;
}
const contentType = imageResponse.headers.get('content-type') || bird.photo_content_type || 'application/octet-stream';
const contentLength = imageResponse.headers.get('content-length');
const imageBuffer = Buffer.from(await imageResponse.arrayBuffer());
res.setHeader('Content-Type', contentType);
if (contentLength) {
res.setHeader('Content-Length', contentLength);
}
res.send(imageBuffer);
} catch (error) {
next(error);
}
+2
View File
@@ -51,6 +51,7 @@ services:
S3_SECRET_ACCESS_KEY: ${S3_SECRET_ACCESS_KEY:-}
S3_PUBLIC_BASE_URL: ${S3_PUBLIC_BASE_URL:-}
S3_KEY_PREFIX: ${S3_KEY_PREFIX:-bird-photos}
PHOTO_DELIVERY_MODE: ${PHOTO_DELIVERY_MODE:-proxy}
FRONTEND_URL: ${FRONTEND_URL:?set FRONTEND_URL for production}
BACKEND_URL: ${BACKEND_URL:?set BACKEND_URL for production}
ADMIN_EMAILS: ${ADMIN_EMAILS:-}
@@ -127,6 +128,7 @@ services:
S3_SECRET_ACCESS_KEY: ${S3_SECRET_ACCESS_KEY:-}
S3_PUBLIC_BASE_URL: ${S3_PUBLIC_BASE_URL:-}
S3_KEY_PREFIX: ${S3_KEY_PREFIX:-bird-photos}
PHOTO_DELIVERY_MODE: ${PHOTO_DELIVERY_MODE:-proxy}
FRONTEND_URL: ${FRONTEND_URL:?set FRONTEND_URL for production}
BACKEND_URL: ${BACKEND_URL:?set BACKEND_URL for production}
ADMIN_EMAILS: ${ADMIN_EMAILS:-}
+2
View File
@@ -49,6 +49,7 @@ services:
S3_SECRET_ACCESS_KEY: ${S3_SECRET_ACCESS_KEY:-}
S3_PUBLIC_BASE_URL: ${S3_PUBLIC_BASE_URL:-}
S3_KEY_PREFIX: ${S3_KEY_PREFIX:-bird-photos}
PHOTO_DELIVERY_MODE: ${PHOTO_DELIVERY_MODE:-proxy}
FRONTEND_URL: ${FRONTEND_URL:-http://localhost:3000}
BACKEND_URL: ${BACKEND_URL:-http://localhost:5000}
ADMIN_EMAILS: ${ADMIN_EMAILS:-}
@@ -120,6 +121,7 @@ services:
S3_SECRET_ACCESS_KEY: ${S3_SECRET_ACCESS_KEY:-}
S3_PUBLIC_BASE_URL: ${S3_PUBLIC_BASE_URL:-}
S3_KEY_PREFIX: ${S3_KEY_PREFIX:-bird-photos}
PHOTO_DELIVERY_MODE: ${PHOTO_DELIVERY_MODE:-proxy}
FRONTEND_URL: ${FRONTEND_URL:-http://localhost:3000}
BACKEND_URL: ${BACKEND_URL:-http://localhost:5000}
ADMIN_EMAILS: ${ADMIN_EMAILS:-}