From 2bdba1581d18ca3467285558ba9ee6fb1ecfeae4 Mon Sep 17 00:00:00 2001 From: gitea_admin Date: Fri, 6 Feb 2026 17:11:35 +0000 Subject: [PATCH] frontend side --- Dockerfile | 50 ++++++++++++++++++++++++++++++++++++++++++++++++ eslint.config.js | 29 ++++++++++++++++++++++++++++ index.html | 13 +++++++++++++ nginx.conf | 33 ++++++++++++++++++++++++++++++++ 4 files changed, 125 insertions(+) create mode 100644 Dockerfile create mode 100644 eslint.config.js create mode 100644 index.html create mode 100644 nginx.conf diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..d938746 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,50 @@ +# Multi-stage build for frontend service +# Stage 1: Build +FROM node:20-alpine AS builder + +WORKDIR /app + +# Copy package files +COPY package*.json ./ + +# Install dependencies +RUN npm ci + +# Copy source code +COPY . . + +# Build for production +RUN npm run build + +# Stage 2: Production with nginx +FROM nginx:alpine + +# Create non-root user +RUN addgroup -g 1001 -S nginx-app && \ + adduser -S nginx-app -u 1001 + +# Copy custom nginx configuration +COPY nginx.conf /etc/nginx/conf.d/default.conf + +# Copy built assets from builder +COPY --from=builder /app/dist /usr/share/nginx/html + +# Change ownership +RUN chown -R nginx-app:nginx-app /usr/share/nginx/html && \ + chown -R nginx-app:nginx-app /var/cache/nginx && \ + chown -R nginx-app:nginx-app /var/log/nginx && \ + touch /var/run/nginx.pid && \ + chown -R nginx-app:nginx-app /var/run/nginx.pid + +# Switch to non-root user +USER nginx-app + +# Expose port +EXPOSE 80 + +# Health check +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://localhost/ || exit 1 + +# Start nginx +CMD ["nginx", "-g", "daemon off;"] diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..4fa125d --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,29 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import { defineConfig, globalIgnores } from 'eslint/config' + +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{js,jsx}'], + extends: [ + js.configs.recommended, + reactHooks.configs.flat.recommended, + reactRefresh.configs.vite, + ], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + parserOptions: { + ecmaVersion: 'latest', + ecmaFeatures: { jsx: true }, + sourceType: 'module', + }, + }, + rules: { + 'no-unused-vars': ['error', { varsIgnorePattern: '^[A-Z_]' }], + }, + }, +]) diff --git a/index.html b/index.html new file mode 100644 index 0000000..9d07d18 --- /dev/null +++ b/index.html @@ -0,0 +1,13 @@ + + + + + + + frontend-service + + +
+ + + diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..1f2c810 --- /dev/null +++ b/nginx.conf @@ -0,0 +1,33 @@ +server { + listen 80; + server_name _; + root /usr/share/nginx/html; + index 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; + + # Gzip compression + gzip on; + gzip_vary on; + gzip_min_length 1024; + gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; + + # SPA routing - serve index.html for all routes + location / { + try_files $uri $uri/ /index.html; + } + + # Cache static assets + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + } + + # Disable cache for index.html + location = /index.html { + add_header Cache-Control "no-cache, no-store, must-revalidate"; + } +}