diff options
| author | saturneric <[email protected]> | 2025-07-05 17:17:45 +0000 | 
|---|---|---|
| committer | saturneric <[email protected]> | 2025-07-05 17:17:45 +0000 | 
| commit | 66856aefb1f56b7d307389ebf8f6b6ffbfd6b0fc (patch) | |
| tree | af1b0828b207ef80c12bc0d1af43ae75b93fe61a | |
| parent | docs(overview): add Qt5 build information for older systems (diff) | |
| download | Manual-66856aefb1f56b7d307389ebf8f6b6ffbfd6b0fc.tar.gz Manual-66856aefb1f56b7d307389ebf8f6b6ffbfd6b0fc.zip  | |
feat(docker): add Dockerfile and CI workflow for containerization
* Introduced a `Dockerfile` for building and running the application in a container.
* Added a CI workflow in `.gitea/workflows/ci.yaml` to automate testing and image pushing on main branch pushes.
* Configured Nginx for serving the application with health checks and caching strategies.
Diffstat (limited to '')
| -rw-r--r-- | .dockerignore | 12 | ||||
| -rw-r--r-- | .gitea/workflows/ci.yaml | 48 | ||||
| -rw-r--r-- | Dockerfile | 57 | ||||
| -rw-r--r-- | nginx/nginx.conf | 103 | 
4 files changed, 220 insertions, 0 deletions
diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..c11dd48 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,12 @@ +node_modules +.git +.github +.vscode +.netlify +.astro +README.md +.gitignore +.env* +*.log +Dockerfile +.dockerignore diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/ci.yaml new file mode 100644 index 0000000..94b2b10 --- /dev/null +++ b/.gitea/workflows/ci.yaml @@ -0,0 +1,48 @@ +name: CI + +# For all pushes to the main branch run the tests and push the image to the +# GitHub registry under an edge tag so we can use it for the nightly +# integration tests +on: [push] + +jobs: +  docker: +    runs-on: ubuntu-latest +    steps: +      # GitHub Actions do not automatically checkout your projects. If you need the code +      # you need to check it out. +      - name: Checkout +        uses: https://git.stdv.de/Mirrors/[email protected] +      - name: Prepare +        id: prep +        run: | +          DOCKER_IMAGE=git.stdv.de/saturneric/gpgfrontend-manual +          VERSION=$(git rev-parse --short HEAD) +          # Add timestamp for better versioning +          TIMESTAMP=$(date +%Y%m%d%H%M%S) +          TAGS="${DOCKER_IMAGE}:${VERSION}" +          TAGS="$TAGS,${DOCKER_IMAGE}:${VERSION}-${TIMESTAMP}" +          TAGS="$TAGS,${DOCKER_IMAGE}:latest" +          echo "tags=${TAGS}" >> $GITHUB_OUTPUT + +      - name: Set up Docker Buildx +        id: buildx +        uses: https://git.stdv.de/Mirrors/setup-buildx-action@v2 + +      - name: Login to Gitea +        if: github.event_name != 'pull_request' +        uses: https://git.stdv.de/Mirrors/login-action@v2 +        with: +          registry: git.stdv.de +          username: ${{ secrets.DOCKER_USERNAME }} +          password: ${{ secrets.DOCKER_PASSWORD }} + +      - name: Build & Push +        id: docker_build +        uses: https://git.stdv.de/Mirrors/build-push-action@v4 +        with: +          builder: ${{ steps.buildx.outputs.name }} +          context: . +          file: ./Dockerfile +          push: ${{ github.event_name != 'pull_request' }} +          tags: ${{ steps.prep.outputs.tags }} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..fe12b35 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,57 @@ +# Build stage +FROM node:lts-alpine AS build + +# Set working directory +WORKDIR /app + +# Copy package files +COPY package*.json ./ + +# Install dependencies with npm ci for faster, reliable builds +RUN npm ci --only=production + +# Copy source code +COPY . . + +# Build the application +RUN npm run build + +# Runtime stage +FROM nginx:alpine AS runtime + +# Install dumb-init for proper signal handling +RUN apk add --no-cache dumb-init + +# Create nginx user and group +RUN addgroup -g 1001 -S nginx && \ +  adduser -S -D -H -u 1001 -h /var/cache/nginx -s /sbin/nologin -G nginx -g nginx nginx + +# Copy nginx configuration +COPY ./nginx/nginx.conf /etc/nginx/nginx.conf + +# Copy built application from build stage +COPY --from=build /app/dist /usr/share/nginx/html + +# Set proper permissions +RUN chown -R nginx:nginx /usr/share/nginx/html && \ +  chown -R nginx:nginx /var/cache/nginx && \ +  chown -R nginx:nginx /var/log/nginx && \ +  chown -R nginx:nginx /etc/nginx/conf.d + +# Create nginx PID directory +RUN mkdir -p /var/run/nginx && \ +  chown -R nginx:nginx /var/run/nginx + +# Switch to non-root user +USER nginx + +# Expose port +EXPOSE 8080 + +# Add health check +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ +  CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1 + +# Use dumb-init to handle signals properly +ENTRYPOINT ["dumb-init", "--"] +CMD ["nginx", "-g", "daemon off;"]
\ No newline at end of file diff --git a/nginx/nginx.conf b/nginx/nginx.conf new file mode 100644 index 0000000..1970845 --- /dev/null +++ b/nginx/nginx.conf @@ -0,0 +1,103 @@ +user nginx; +worker_processes auto; +error_log /var/log/nginx/error.log notice; +pid /var/run/nginx.pid; + +events { +    worker_connections 1024; +} + +http { +    include /etc/nginx/mime.types; +    default_type application/octet-stream; + +    log_format main '$remote_addr - $remote_user [$time_local] "$request" ' +                    '$status $body_bytes_sent "$http_referer" ' +                    '"$http_user_agent" "$http_x_forwarded_for"'; + +    access_log /var/log/nginx/access.log main; + +    sendfile on; +    tcp_nopush on; +    tcp_nodelay on; +    keepalive_timeout 65; +    types_hash_max_size 2048; + +    # 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; +    add_header Referrer-Policy "strict-origin-when-cross-origin" always; + +    # Gzip compression +    gzip on; +    gzip_vary on; +    gzip_min_length 1024; +    gzip_proxied any; +    gzip_comp_level 6; +    gzip_types +        application/atom+xml +        application/geo+json +        application/javascript +        application/x-javascript +        application/json +        application/ld+json +        application/manifest+json +        application/rdf+xml +        application/rss+xml +        application/xhtml+xml +        application/xml +        font/eot +        font/otf +        font/ttf +        image/svg+xml +        text/css +        text/javascript +        text/plain +        text/xml; + +    server { +        listen 8080; +        server_name localhost; +        root /usr/share/nginx/html; +        index index.html; + +        # Security configurations +        server_tokens off; +         +        # Cache static assets +        location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { +            expires 1y; +            add_header Cache-Control "public, immutable"; +            add_header X-Frame-Options "SAMEORIGIN" always; +            add_header X-Content-Type-Options "nosniff" always; +        } + +        # Handle HTML files with shorter cache +        location ~* \.html$ { +            expires 1h; +            add_header Cache-Control "public, no-transform"; +            add_header X-Frame-Options "SAMEORIGIN" always; +            add_header X-Content-Type-Options "nosniff" always; +        } + +        # Handle root and fallback to index.html (SPA support) +        location / { +            try_files $uri $uri/ /index.html; +            add_header X-Frame-Options "SAMEORIGIN" always; +            add_header X-Content-Type-Options "nosniff" always; +        } + +        # Health check endpoint +        location /health { +            access_log off; +            return 200 "healthy\n"; +            add_header Content-Type text/plain; +        } + +        # Block access to hidden files +        location ~ /\. { +            deny all; +        } +    } +}  | 
