diff --git a/deploy/.dockerignore b/deploy/.dockerignore new file mode 100644 index 0000000..c15445c --- /dev/null +++ b/deploy/.dockerignore @@ -0,0 +1,34 @@ +# Dependencies +node_modules + +# Build output (will be generated inside container) +dist + +# Git +.git +.gitignore + +# IDE +.idea +.vscode + +# Logs +*.log +npm-debug.log* + +# Environment files (use deploy/.env.staging or .production instead) +.env +.env.local +.env.*.local + +# Development +.vite +*.Dockerfile +docker-compose*.yml +deploy/Dockerfile +deploy/build.sh +deploy/.env* + +# Misc +*.md +LICENSE diff --git a/deploy/.env.example b/deploy/.env.example new file mode 100644 index 0000000..76df82e --- /dev/null +++ b/deploy/.env.example @@ -0,0 +1,2 @@ +VITE_API_IMPLEMENTATION=django +VITE_DJANGO_BASE_URL=http://localhost:7000 diff --git a/deploy/Dockerfile b/deploy/Dockerfile new file mode 100644 index 0000000..9781939 --- /dev/null +++ b/deploy/Dockerfile @@ -0,0 +1,20 @@ +# Stage 1: Build with Node.js +FROM node:20-alpine AS builder + +WORKDIR /app + +COPY package.json package-lock.json ./ +RUN npm ci + +COPY . . +RUN npm run build + +# Stage 2: Serve with Nginx +FROM nginx:alpine + +COPY --from=builder /app/dist /usr/share/nginx/html +COPY nginx.conf /etc/nginx/conf.d/default.conf + +EXPOSE 80 + +CMD ["nginx", "-g", "daemon off;"] diff --git a/deploy/build.sh b/deploy/build.sh new file mode 100755 index 0000000..beb00fa --- /dev/null +++ b/deploy/build.sh @@ -0,0 +1,82 @@ +#!/bin/bash + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" + +GITEA_REGISTRY="gitea.onecluster.org" +GITEA_USER="mono" +IMAGE_NAME="don_confiao_frontend" + +usage() { + echo "Usage: $0 [commit_sha]" + echo "" + echo "Examples:" + echo " $0 staging # Build and push staging image" + echo " $0 production # Build and push production image" + echo " $0 staging abc1234 # Build with specific commit SHA" + exit 1 +} + +if [ $# -lt 1 ]; then + usage +fi + +ENV_TYPE="$1" +COMMIT_SHA="${2:-$(git -C "$PROJECT_ROOT" rev-parse --short HEAD 2>/dev/null || echo "unknown")}" + +case "$ENV_TYPE" in + staging) + ENV_FILE="$SCRIPT_DIR/.env.staging" + IMAGE_NAME="don_confiao_frontend_staging" + TAG="latest" + ;; + production) + ENV_FILE="$SCRIPT_DIR/.env.production" + IMAGE_NAME="don_confiao_frontend" + TAG="latest" + ;; + *) + echo "Error: Invalid environment type '$ENV_TYPE'" + usage + ;; +esac + +if [ ! -f "$ENV_FILE" ]; then + echo "Error: Environment file not found: $ENV_FILE" + echo "Please create it based on: $SCRIPT_DIR/.env.example" + exit 1 +fi + +echo "=== Building $ENV_TYPE image ===" +echo "Image tag: $TAG" +echo "Commit SHA: $COMMIT_SHA" +echo "Environment file: $ENV_FILE" + +set -a +source "$ENV_FILE" +set +a + +BUILD_ARGS="--build-arg VITE_DJANGO_BASE_URL=$VITE_DJANGO_BASE_URL" +BUILD_ARGS="$BUILD_ARGS --build-arg VITE_API_IMPLEMENTATION=$VITE_API_IMPLEMENTATION" + +FULL_IMAGE_NAME="$GITEA_REGISTRY/$GITEA_USER/$IMAGE_NAME" + +echo "Building image..." +docker build \ + $BUILD_ARGS \ + -t "$FULL_IMAGE_NAME:$TAG" \ + -t "$FULL_IMAGE_NAME:$COMMIT_SHA" \ + -f "$SCRIPT_DIR/Dockerfile" \ + "$PROJECT_ROOT" + +echo "" +echo "=== Pushing image to registry ===" +docker push "$FULL_IMAGE_NAME:$TAG" +docker push "$FULL_IMAGE_NAME:$COMMIT_SHA" + +echo "" +echo "=== Done ===" +echo "Image: $FULL_IMAGE_NAME:$TAG" +echo "Commit: $FULL_IMAGE_NAME:$COMMIT_SHA" diff --git a/deploy/nginx.conf b/deploy/nginx.conf new file mode 100644 index 0000000..9cd3194 --- /dev/null +++ b/deploy/nginx.conf @@ -0,0 +1,29 @@ +server { + listen 80; + server_name localhost; + root /usr/share/nginx/html; + index index.html; + + # Gzip compression + gzip on; + gzip_vary on; + gzip_min_length 1024; + gzip_proxied expired no-cache no-store private auth; + gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml application/javascript application/json; + + # Cache static assets + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + } + + # Vue Router: redirect all non-file requests to index.html + location / { + try_files $uri $uri/ /index.html; + } + + # Security headers + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; +}