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());
|
app.useGlobalInterceptors(new DecimalInterceptor());
|
||||||
|
|
||||||
const config = new DocumentBuilder()
|
const isProduction = process.env.NODE_ENV === 'production';
|
||||||
.setTitle('Canina Iran API')
|
const enableSwagger = process.env.ENABLE_SWAGGER === 'true';
|
||||||
.setDescription(
|
|
||||||
'API Documentation for Canina Iran Pet Health & Supplement Platform',
|
|
||||||
)
|
|
||||||
.setVersion('1.0.0')
|
|
||||||
.addBearerAuth()
|
|
||||||
.build();
|
|
||||||
|
|
||||||
const document = SwaggerModule.createDocument(app, config);
|
if (!isProduction || enableSwagger) {
|
||||||
SwaggerModule.setup('api/docs', app, document);
|
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);
|
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 X-Content-Type-Options "nosniff" always;
|
||||||
add_header Referrer-Policy "no-referrer-when-downgrade" 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)
|
# Main Application (Next.js)
|
||||||
location / {
|
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_pass http://localhost:3000;
|
||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
@ -45,8 +60,22 @@ server {
|
|||||||
add_header X-Content-Type-Options "nosniff" always;
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
add_header Referrer-Policy "no-referrer-when-downgrade" 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
|
# SPA routing for Admin Panel
|
||||||
location / {
|
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;
|
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
|
- PORT=3000
|
||||||
- PRISMA_SCHEMA_ENGINE_BINARY=/app/node_modules/@prisma/engines/schema-engine-linux-musl-openssl-3.0.x
|
- 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
|
- 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:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.http.routers.canina-api-prod.rule=Host(`api.canina.ir`)"
|
- "traefik.http.routers.canina-api-prod.rule=Host(`api.canina.ir`)"
|
||||||
@ -72,3 +74,4 @@ networks:
|
|||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
canina_prod_db:
|
canina_prod_db:
|
||||||
|
canina_prod_uploads:
|
||||||
|
|||||||
@ -34,6 +34,8 @@ services:
|
|||||||
- PORT=3000
|
- PORT=3000
|
||||||
- PRISMA_SCHEMA_ENGINE_BINARY=/app/node_modules/@prisma/engines/schema-engine-linux-musl-openssl-3.0.x
|
- 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
|
- 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:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
- "traefik.http.routers.canina-api-stage.rule=Host(`stageapi.canina.ir`)"
|
- "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.rule=Host(`stage.canina.ir`) || Host(`stageadmin.canina.ir`)"
|
||||||
- "traefik.http.routers.canina-stage.entrypoints=websecure"
|
- "traefik.http.routers.canina-stage.entrypoints=websecure"
|
||||||
- "traefik.http.routers.canina-stage.tls=true"
|
- "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"
|
- "traefik.http.services.canina-stage.loadbalancer.server.port=8080"
|
||||||
depends_on:
|
depends_on:
|
||||||
- backend_stage
|
- backend_stage
|
||||||
@ -72,3 +76,4 @@ networks:
|
|||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
canina_stage_db:
|
canina_stage_db:
|
||||||
|
canina_stage_uploads:
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user