From db0d27cf4e351c8ff994bcace27a42e43f4586da Mon Sep 17 00:00:00 2001 From: striker Date: Thu, 21 May 2026 02:37:46 +0300 Subject: [PATCH] build: Dockerfile + nginx.conf + docker-compose + Gitea CI + 404 page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Dockerfile multi-stage: node:22-alpine builds Astro → nginx:1.29-alpine отдаёт dist/. healthcheck wget --spider / - nginx.conf: gzip с подходящими типами (RSS/JSON/SVG/woff2), кэш /_astro/ immutable 1y, кэш css/js/img 30d, MIME application/rss+xml для feed, text/plain для robots/ai/llms/sitemap, кастомная 404 - docker-compose: контейнер anotherreflections-ru-v2 на 127.0.0.1:4084 (старый WP на :4080 остаётся для отката) - .gitea/workflows/deploy.yml: push в main → SSH-деплой на web, git fetch+reset → docker compose build → up -d → docker image prune (retention 7d по правилу проекта). Verify-шаг curl на :4084 - src/pages/404.astro — тематическая страница «не найдено» с навигацией --- .gitea/workflows/deploy.yml | 42 +++++++++++++++++ Dockerfile | 23 ++++++++++ docker-compose.yml | 18 ++++++++ nginx.conf | 90 +++++++++++++++++++++++++++++++++++++ src/pages/404.astro | 24 ++++++++++ 5 files changed, 197 insertions(+) create mode 100644 .gitea/workflows/deploy.yml create mode 100644 Dockerfile create mode 100644 docker-compose.yml create mode 100644 nginx.conf create mode 100644 src/pages/404.astro diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml new file mode 100644 index 0000000..28b1207 --- /dev/null +++ b/.gitea/workflows/deploy.yml @@ -0,0 +1,42 @@ +name: Deploy to web.hhivp.com + +on: + push: + branches: [main] + workflow_dispatch: + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - name: Set up SSH key + run: | + mkdir -p ~/.ssh + echo "${{ secrets.SSH_PRIVATE_KEY }}" | base64 -d > ~/.ssh/deploy_key + chmod 600 ~/.ssh/deploy_key + cat >> ~/.ssh/config <7d по правилу проекта) + docker image prune -af --filter "until=168h" >/dev/null 2>&1 || true + REMOTE + + - name: Verify + run: | + ssh web 'curl -sf -H "Host: anotherreflections.ru" http://127.0.0.1:4084/ -o /dev/null -w "HTTP %{http_code} | %{size_download} bytes\n"' diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..15c948c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,23 @@ +# syntax=docker/dockerfile:1 + +# ─── Stage 1: build static site (Astro SSG) ──────────────────────────────── +FROM node:22-alpine AS build +WORKDIR /app + +COPY package.json package-lock.json ./ +RUN npm ci + +COPY . . +RUN npm run build + +# ─── Stage 2: nginx runtime ───────────────────────────────────────────────── +FROM nginx:1.29-alpine + +RUN rm -rf /usr/share/nginx/html/* +COPY --from=build /app/dist /usr/share/nginx/html +COPY nginx.conf /etc/nginx/conf.d/default.conf + +EXPOSE 80 + +HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \ + CMD wget -q --spider http://127.0.0.1/ || exit 1 diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..c146745 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,18 @@ +services: + anotherreflections-ru-v2: + build: + context: . + dockerfile: Dockerfile + image: anotherreflections-ru-v2:latest + container_name: anotherreflections-ru-v2 + restart: unless-stopped + ports: + # На web.hhivp.com nginx-хост проксирует anotherreflections.ru + # на 127.0.0.1:4084. Менять при необходимости. + - "127.0.0.1:4084:80" + +# Деплой: +# git pull в /opt/docker/sites/anotherreflections-ru-v2/ +# docker compose build && docker compose up -d +# Откат — оставлен старый WP на 127.0.0.1:4080 (контейнер anotherreflections-ru), +# nginx vhost переключить обратно. diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..e120074 --- /dev/null +++ b/nginx.conf @@ -0,0 +1,90 @@ +# nginx-конфиг внутри контейнера (статика Astro) +# TLS терминируется на хост-уровне (web.hhivp.com), здесь только HTTP. + +server { + listen 80 default_server; + server_name _; + + root /usr/share/nginx/html; + index index.html; + + # Логи — на stdout/stderr контейнера (видны через docker logs) + access_log /dev/stdout; + error_log /dev/stderr warn; + + # Безопасность: запрет на скрытые файлы (но разрешаем .well-known для LE) + location ~ /\.(?!well-known) { + deny all; + log_not_found off; + access_log off; + } + + # ──────────────────────────────────────────────────────────────── + # Сжатие + # ──────────────────────────────────────────────────────────────── + gzip on; + gzip_vary on; + gzip_min_length 256; + gzip_proxied any; + gzip_comp_level 6; + gzip_types + text/plain text/css text/xml + application/json application/xml application/rss+xml application/atom+xml + application/javascript application/x-javascript + image/svg+xml font/woff2; + + # ──────────────────────────────────────────────────────────────── + # MIME для RSS-фидов и текстовых служебных файлов + # ──────────────────────────────────────────────────────────────── + location = /feed.xml { + default_type application/rss+xml; + charset utf-8; + try_files /feed.xml =404; + expires 5m; + add_header Cache-Control "public, max-age=300, must-revalidate"; + } + location ~ ^/category/[^/]+/feed\.xml$ { + default_type application/rss+xml; + charset utf-8; + expires 5m; + add_header Cache-Control "public, max-age=300, must-revalidate"; + } + location = /sitemap.txt { + default_type text/plain; + charset utf-8; + } + location = /robots.txt { default_type text/plain; charset utf-8; } + location = /ai.txt { default_type text/plain; charset utf-8; } + location = /llms.txt { default_type text/plain; charset utf-8; } + + # ──────────────────────────────────────────────────────────────── + # Кэш статики (хеш у Astro в имени файла) + # ──────────────────────────────────────────────────────────────── + location /_astro/ { + expires 1y; + add_header Cache-Control "public, immutable, max-age=31536000"; + access_log off; + } + location ~* \.(?:css|js|woff2?|ttf|otf|eot)$ { + expires 30d; + add_header Cache-Control "public, max-age=2592000"; + access_log off; + } + location ~* \.(?:png|jpe?g|gif|svg|webp|avif|ico)$ { + expires 30d; + add_header Cache-Control "public, max-age=2592000"; + access_log off; + } + + # ──────────────────────────────────────────────────────────────── + # Маршрутизация: trailingSlash: 'always' уже встроен в Astro, + # все страницы экспортированы как dir/index.html + # ──────────────────────────────────────────────────────────────── + location / { + try_files $uri $uri/ $uri.html =404; + } + + # Кастомная 404 + error_page 404 /404.html; + location = /404.html { internal; } +} diff --git a/src/pages/404.astro b/src/pages/404.astro new file mode 100644 index 0000000..65e6aa2 --- /dev/null +++ b/src/pages/404.astro @@ -0,0 +1,24 @@ +--- +import BaseLayout from '../layouts/BaseLayout.astro'; +--- + +
+ 404 +

Не найдено

+

+ Возможно, страница переехала вместе с обновлением сайта, либо её никогда здесь не было — иные отражения причудливо тасуют реальность. +

+
+ +
+

+ Попробуйте начать с одной из этих точек входа: +

+

+ К ленте новостей + Миры + О нас + Контакты +

+
+