aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsaturneric <[email protected]>2025-07-05 17:17:45 +0000
committersaturneric <[email protected]>2025-07-05 17:17:45 +0000
commit66856aefb1f56b7d307389ebf8f6b6ffbfd6b0fc (patch)
treeaf1b0828b207ef80c12bc0d1af43ae75b93fe61a
parentdocs(overview): add Qt5 build information for older systems (diff)
downloadManual-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.
-rw-r--r--.dockerignore12
-rw-r--r--.gitea/workflows/ci.yaml48
-rw-r--r--Dockerfile57
-rw-r--r--nginx/nginx.conf103
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;
+ }
+ }
+}