From 85070cf77b51fd979d55bf0738cfa99a5addc9fc Mon Sep 17 00:00:00 2001 From: Koncept Kit <63216427+konceptkit@users.noreply.github.com> Date: Tue, 27 Jan 2026 16:44:56 +0700 Subject: [PATCH] Update Footer to get current year --- .dockerignore | 75 ++++++++++++++++++++++++++++++++++ Dockerfile | 49 ++++++++++++++++++++++ nginx.conf | 44 ++++++++++++++++++++ src/components/MemberFooter.js | 2 +- src/components/PublicFooter.js | 2 +- 5 files changed, 170 insertions(+), 2 deletions(-) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 nginx.conf diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..ecf8441 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,75 @@ +# Git +.git +.gitignore + +# Dependencies +node_modules/ + +# Build output (we build inside Docker) +build/ +dist/ + +# IDE +.idea/ +.vscode/ +*.swp +*.swo +*~ + +# Testing +coverage/ +.nyc_output/ + +# Environment files (will be passed as build args) +.env +.env.local +.env.development +.env.development.local +.env.test +.env.test.local +.env.production +.env.production.local +*.env + +# Logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Docker +Dockerfile +docker-compose*.yml +.docker/ + +# Documentation +*.md +docs/ + +# OS files +.DS_Store +Thumbs.db + +# Temporary files +tmp/ +temp/ +*.tmp + +# ESLint cache +.eslintcache + +# Optional npm cache directory +.npm + +# Optional REPL history +.node_repl_history + +# Yarn +.yarn-integrity +.pnp.* + +# Storybook +storybook-static/ + +# Design files (if any) +.superdesign/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..e550e13 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,49 @@ +# Frontend Dockerfile - React with multi-stage build + +# Stage 1: Build +FROM node:20-alpine AS builder + +WORKDIR /app + +# Copy package files +COPY package.json yarn.lock ./ + +# Install dependencies +RUN yarn install --frozen-lockfile + +# Copy source code +COPY . . + +# Build arguments for environment variables +ARG REACT_APP_BACKEND_URL +ENV REACT_APP_BACKEND_URL=$REACT_APP_BACKEND_URL + +# Build the application +RUN yarn build + +# Stage 2: Production with Nginx +FROM nginx:alpine AS production + +# Copy custom nginx config +COPY nginx.conf /etc/nginx/conf.d/default.conf + +# Copy built assets from builder stage +COPY --from=builder /app/build /usr/share/nginx/html + +# Create non-root user for security +RUN adduser -D -g '' appuser && \ + chown -R appuser:appuser /usr/share/nginx/html && \ + chown -R appuser:appuser /var/cache/nginx && \ + chown -R appuser:appuser /var/log/nginx && \ + touch /var/run/nginx.pid && \ + chown -R appuser:appuser /var/run/nginx.pid + +# Expose port +EXPOSE 80 + +# Health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ + CMD wget --no-verbose --tries=1 --spider http://localhost:80/ || exit 1 + +# Start nginx +CMD ["nginx", "-g", "daemon off;"] diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..7321506 --- /dev/null +++ b/nginx.conf @@ -0,0 +1,44 @@ +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; + + # 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; + + # Cache static assets + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + try_files $uri =404; + } + + # Handle React Router - serve index.html for all routes + location / { + try_files $uri $uri/ /index.html; + } + + # Health check endpoint + location /health { + access_log off; + return 200 "healthy\n"; + add_header Content-Type text/plain; + } + + # Disable access to hidden files + location ~ /\. { + deny all; + access_log off; + log_not_found off; + } +} diff --git a/src/components/MemberFooter.js b/src/components/MemberFooter.js index 987b9fe..274e3cb 100644 --- a/src/components/MemberFooter.js +++ b/src/components/MemberFooter.js @@ -111,7 +111,7 @@ const MemberFooter = () => { Terms of Service Privacy Policy -
© 2025 LOAF. All rights reserved.
+© {new Date().getFullYear()} LOAF. All rights reserved.
diff --git a/src/components/PublicFooter.js b/src/components/PublicFooter.js index 6d53c1d..3648f3e 100644 --- a/src/components/PublicFooter.js +++ b/src/components/PublicFooter.js @@ -60,7 +60,7 @@ const PublicFooter = () => {- © 2025 LOAF. All Rights Reserved. + © {new Date().getFullYear()} LOAF. All Rights Reserved.
Designed and Managed by{' '}