Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions docker-compose.prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,9 @@ services:
PORT: 3001
UPLOAD_DIR: /app/uploads
INFERENCE_URL: http://inference:8000
# The production signer credential is mounted at runtime and never baked
# into the backend image or repository.
# This credential is used for upload/read/delete and signed URLs, so it
# requires roles/storage.objectUser on the production bucket. It is
# mounted at runtime and never baked into the image or repository.
GCS_KEY_FILENAME: /run/secrets/gcs-pdf-signer.json
volumes:
- ./uploads:/app/uploads
Expand Down
52 changes: 52 additions & 0 deletions scripts/deploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,58 @@ else
fi
docker compose -f "$COMPOSE_FILE" up -d --no-deps --force-recreate nginx

echo "==> Verify backend GCS write/delete access"
docker compose -f "$COMPOSE_FILE" exec -T -w /app/packages/backend backend node <<'NODE'
const { Storage } = require('@google-cloud/storage');

async function main() {
if ((process.env.FILE_STORAGE_PROVIDER || '').toLowerCase() !== 'gcs') {
console.log(' Skipped: FILE_STORAGE_PROVIDER is not gcs.');
return;
}

const bucketName = process.env.GCS_BUCKET_NAME;
if (!bucketName) {
throw new Error('GCS_BUCKET_NAME is required when FILE_STORAGE_PROVIDER=gcs');
}

const options = {};
if (process.env.GCS_PROJECT_ID) {
options.projectId = process.env.GCS_PROJECT_ID;
}
if (process.env.GCS_KEY_FILENAME) {
options.keyFilename = process.env.GCS_KEY_FILENAME;
}

const storage = new Storage(options);
const objectName = `healthchecks/deploy-${Date.now()}-${Math.random()
.toString(16)
.slice(2)}.txt`;
const file = storage.bucket(bucketName).file(objectName);
let uploaded = false;

try {
await file.save('ok', {
contentType: 'text/plain',
resumable: false,
});
uploaded = true;
await file.delete();
uploaded = false;
console.log(` GCS write/delete smoke passed for gs://${bucketName}.`);
} finally {
if (uploaded) {
await file.delete({ ignoreNotFound: true }).catch(() => undefined);
}
}
}

main().catch((error) => {
console.error('GCS write/delete smoke failed:', error.message || error);
process.exit(1);
});
NODE

echo "==> Ensure production TLS certificate covers supported hostnames"
disable_conflicting_host_certbot
bash scripts/ensure-production-cert.sh
Expand Down
Loading