feat(security/devops): disable swagger in prod, secure staging with basic auth & noindex, add DB backup script and uploads volume persistence
Some checks failed
Deploy Canina / deploy (push) Failing after 29s
Some checks failed
Deploy Canina / deploy (push) Failing after 29s
This commit is contained in:
parent
9f11d8fb2d
commit
e281ef75da
@ -83,17 +83,25 @@ async function bootstrap() {
|
||||
|
||||
app.useGlobalInterceptors(new DecimalInterceptor());
|
||||
|
||||
const config = new DocumentBuilder()
|
||||
.setTitle('Canina Iran API')
|
||||
.setDescription(
|
||||
'API Documentation for Canina Iran Pet Health & Supplement Platform',
|
||||
)
|
||||
.setVersion('1.0.0')
|
||||
.addBearerAuth()
|
||||
.build();
|
||||
const isProduction = process.env.NODE_ENV === 'production';
|
||||
const enableSwagger = process.env.ENABLE_SWAGGER === 'true';
|
||||
|
||||
const document = SwaggerModule.createDocument(app, config);
|
||||
SwaggerModule.setup('api/docs', app, document);
|
||||
if (!isProduction || enableSwagger) {
|
||||
const config = new DocumentBuilder()
|
||||
.setTitle('Canina Iran API')
|
||||
.setDescription(
|
||||
'API Documentation for Canina Iran Pet Health & Supplement Platform',
|
||||
)
|
||||
.setVersion('1.0.0')
|
||||
.addBearerAuth()
|
||||
.build();
|
||||
|
||||
const document = SwaggerModule.createDocument(app, config);
|
||||
SwaggerModule.setup('api/docs', app, document);
|
||||
console.log('Swagger UI is ACTIVE on /api/docs');
|
||||
} else {
|
||||
console.log('Swagger UI is DISABLED for security (production environment)');
|
||||
}
|
||||
|
||||
await app.listen(process.env.PORT ?? 4001);
|
||||
}
|
||||
|
||||
29
nginx.conf
29
nginx.conf
@ -9,8 +9,23 @@ server {
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header Referrer-Policy "no-referrer-when-downgrade" always;
|
||||
|
||||
# Block SEO Indexing for Staging Environment
|
||||
if ($host = "stage.canina.ir") {
|
||||
add_header X-Robots-Tag "noindex, nofollow, nosnippet, noarchive" always;
|
||||
}
|
||||
|
||||
# Main Application (Next.js)
|
||||
location / {
|
||||
# Basic Authentication for Staging Environment
|
||||
if ($host = "stage.canina.ir") {
|
||||
set $auth_type "Staging Restricted Area";
|
||||
}
|
||||
if ($host != "stage.canina.ir") {
|
||||
set $auth_type "off";
|
||||
}
|
||||
auth_basic $auth_type;
|
||||
auth_basic_user_file /etc/nginx/.htpasswd;
|
||||
|
||||
proxy_pass http://localhost:3000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
@ -45,8 +60,22 @@ server {
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header Referrer-Policy "no-referrer-when-downgrade" always;
|
||||
|
||||
# Block SEO Indexing for Staging Admin
|
||||
if ($host = "stageadmin.canina.ir") {
|
||||
add_header X-Robots-Tag "noindex, nofollow, nosnippet, noarchive" always;
|
||||
}
|
||||
|
||||
# SPA routing for Admin Panel
|
||||
location / {
|
||||
if ($host = "stageadmin.canina.ir") {
|
||||
set $auth_type "Staging Admin Restricted Area";
|
||||
}
|
||||
if ($host != "stageadmin.canina.ir") {
|
||||
set $auth_type "off";
|
||||
}
|
||||
auth_basic $auth_type;
|
||||
auth_basic_user_file /etc/nginx/.htpasswd;
|
||||
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
|
||||
36
scripts/backup_db.sh
Executable file
36
scripts/backup_db.sh
Executable file
@ -0,0 +1,36 @@
|
||||
#!/bin/bash
|
||||
set -eo pipefail
|
||||
|
||||
# Configuration
|
||||
CONTAINER_NAME="${DB_CONTAINER_NAME:-canino_db_prod}"
|
||||
DB_USER="${POSTGRES_USER:-canino_prod}"
|
||||
DB_NAME="${POSTGRES_DB:-caninodb_prod}"
|
||||
BACKUP_DIR="${BACKUP_DIR:-/var/backups/canina_db}"
|
||||
RETENTION_DAYS=7
|
||||
|
||||
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
|
||||
BACKUP_FILE="${BACKUP_DIR}/canina_db_${TIMESTAMP}.sql.gz"
|
||||
|
||||
echo "[$(date -u +'%Y-%m-%dT%H:%M:%SZ')] Starting automated PostgreSQL backup..."
|
||||
echo "Target Container: ${CONTAINER_NAME}"
|
||||
echo "Database: ${DB_NAME}"
|
||||
echo "Backup Output: ${BACKUP_FILE}"
|
||||
|
||||
# Ensure backup directory exists
|
||||
mkdir -p "${BACKUP_DIR}"
|
||||
|
||||
# Execute compressed database dump
|
||||
if docker exec "${CONTAINER_NAME}" pg_dump -U "${DB_USER}" -d "${DB_NAME}" | gzip > "${BACKUP_FILE}"; then
|
||||
BACKUP_SIZE=$(du -h "${BACKUP_FILE}" | cut -f1)
|
||||
echo "[$(date -u +'%Y-%m-%dT%H:%M:%SZ')] Backup completed successfully! File size: ${BACKUP_SIZE}"
|
||||
else
|
||||
echo "[$(date -u +'%Y-%m-%dT%H:%M:%SZ')] ERROR: Backup failed!" >&2
|
||||
rm -f "${BACKUP_FILE}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Purge backups older than RETENTION_DAYS (7 days)
|
||||
echo "[$(date -u +'%Y-%m-%dT%H:%M:%SZ')] Purging backups older than ${RETENTION_DAYS} days from ${BACKUP_DIR}..."
|
||||
find "${BACKUP_DIR}" -type f -name "canina_db_*.sql.gz" -mtime +"${RETENTION_DAYS}" -exec rm -f {} \; -verbose || true
|
||||
|
||||
echo "[$(date -u +'%Y-%m-%dT%H:%M:%SZ')] Backup process completed."
|
||||
@ -34,6 +34,8 @@ services:
|
||||
- PORT=3000
|
||||
- PRISMA_SCHEMA_ENGINE_BINARY=/app/node_modules/@prisma/engines/schema-engine-linux-musl-openssl-3.0.x
|
||||
- PRISMA_QUERY_ENGINE_LIBRARY=/app/node_modules/@prisma/engines/libquery_engine-linux-musl-openssl-3.0.x.so.node
|
||||
volumes:
|
||||
- canina_prod_uploads:/app/uploads
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.canina-api-prod.rule=Host(`api.canina.ir`)"
|
||||
@ -72,3 +74,4 @@ networks:
|
||||
|
||||
volumes:
|
||||
canina_prod_db:
|
||||
canina_prod_uploads:
|
||||
|
||||
@ -34,6 +34,8 @@ services:
|
||||
- PORT=3000
|
||||
- PRISMA_SCHEMA_ENGINE_BINARY=/app/node_modules/@prisma/engines/schema-engine-linux-musl-openssl-3.0.x
|
||||
- PRISMA_QUERY_ENGINE_LIBRARY=/app/node_modules/@prisma/engines/libquery_engine-linux-musl-openssl-3.0.x.so.node
|
||||
volumes:
|
||||
- canina_stage_uploads:/app/uploads
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.canina-api-stage.rule=Host(`stageapi.canina.ir`)"
|
||||
@ -60,6 +62,8 @@ services:
|
||||
- "traefik.http.routers.canina-stage.rule=Host(`stage.canina.ir`) || Host(`stageadmin.canina.ir`)"
|
||||
- "traefik.http.routers.canina-stage.entrypoints=websecure"
|
||||
- "traefik.http.routers.canina-stage.tls=true"
|
||||
- "traefik.http.routers.canina-stage.middlewares=staging-robots-headers"
|
||||
- "traefik.http.middlewares.staging-robots-headers.headers.customresponseheaders.X-Robots-Tag=noindex, nofollow, nosnippet, noarchive"
|
||||
- "traefik.http.services.canina-stage.loadbalancer.server.port=8080"
|
||||
depends_on:
|
||||
- backend_stage
|
||||
@ -72,3 +76,4 @@ networks:
|
||||
|
||||
volumes:
|
||||
canina_stage_db:
|
||||
canina_stage_uploads:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user