chore: add deployment infrastructure

This commit is contained in:
Gleb Korotkiy
2026-06-21 11:08:30 +03:00
parent 5996c9e212
commit b99ec0c4f4
7 changed files with 259 additions and 10 deletions

24
.dockerignore Normal file
View File

@@ -0,0 +1,24 @@
.git
.gitignore
.gitea
.vscode
.idea
.fleet
node_modules
dist
dist-ssr
coverage
playwright-report
test-results
.env
.env.*
!.env.example
!.env.*.example
*.log
*.swp
*.swo
.DS_Store
Thumbs.db

View File

@@ -0,0 +1,84 @@
name: ci-pr
on:
pull_request:
branches:
- dev
workflow_dispatch:
concurrency:
group: ci-pr-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
gate:
runs-on: [frontend, ubuntu-latest]
timeout-minutes: 120
env:
PLAYWRIGHT_BROWSERS_PATH: /root/.cache/ms-playwright
steps:
- name: Checkout
uses: actions/checkout@v4.2.2
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.9
- name: Cache Bun dependencies
uses: actions/cache@v4
with:
path: |
/root/.bun/install/cache
node_modules
key: ${{ runner.os }}-${{ runner.arch }}-bun-${{ hashFiles('bun.lock') }}
restore-keys: |
${{ runner.os }}-${{ runner.arch }}-bun-
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Generate API clients
run: bun run apigen
- name: Lint check
run: bun run lint
- name: Type check
run: bun run typecheck
- name: Unit tests
run: bun run test:unit
- name: Build
run: bun run build
- name: Resolve Playwright version
id: playwright-version
run: |
version="$(bunx playwright --version | awk '{ print $2 }')"
echo "version=${version}" >> "${GITHUB_OUTPUT}"
- name: Cache Playwright browsers
id: playwright-cache
uses: actions/cache@v4
with:
path: /root/.cache/ms-playwright
key: ${{ runner.os }}-${{ runner.arch }}-playwright-${{ steps.playwright-version.outputs.version }}
- name: Install Playwright system deps
run: bunx playwright install-deps chromium
- name: Install Playwright browsers
if: steps.playwright-cache.outputs.cache-hit != 'true'
run: bunx playwright install chromium
- name: E2E
env:
CI: '1'
PLAYWRIGHT_HTML_OPEN: never
run: bun run test:e2e -- --reporter=line
- name: Docker build
run: docker build --pull -t analytical-panel-frontend:ci .

39
Dockerfile Normal file
View File

@@ -0,0 +1,39 @@
# syntax=docker/dockerfile:1.7
FROM oven/bun:1.3.9 AS builder
WORKDIR /app
ENV HUSKY=0
RUN apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends nodejs \
&& rm -rf /var/lib/apt/lists/*
COPY package.json bun.lock ./
RUN bun install --frozen-lockfile
COPY . .
RUN bun run apigen
RUN bun run build
FROM nginx:1.29-alpine AS runner
RUN apk add --no-cache gettext
COPY docker/nginx.conf.template /etc/nginx/templates/default.conf.template
COPY docker/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
COPY --from=builder /app/dist/ /usr/share/nginx/html/
EXPOSE 80
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD wget -q -O /dev/null http://127.0.0.1/ || exit 1
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
CMD ["nginx", "-g", "daemon off;"]

17
docker-compose.yml Normal file
View File

@@ -0,0 +1,17 @@
services:
frontend:
container_name: analytical-panel-frontend
build:
context: .
dockerfile: Dockerfile
environment:
BACKEND_UPSTREAM: '${BACKEND_UPSTREAM:-https://anal-back.dev.nii-ecos.ru}'
ports:
- '${FRONTEND_PORT:-8080}:80'
restart: unless-stopped
networks:
- analytical_panel
networks:
analytical_panel:
name: analytical_panel_network

13
docker/docker-entrypoint.sh Executable file
View File

@@ -0,0 +1,13 @@
#!/bin/sh
set -eu
: "${BACKEND_UPSTREAM:?BACKEND_UPSTREAM is required (example: http://analytical-panel-backend:8000)}"
BACKEND_UPSTREAM="${BACKEND_UPSTREAM%/}"
export BACKEND_UPSTREAM
envsubst '${BACKEND_UPSTREAM}' \
< /etc/nginx/templates/default.conf.template \
> /etc/nginx/conf.d/default.conf
exec "$@"

View File

@@ -0,0 +1,61 @@
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
proxy_connect_timeout 300s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
proxy_ssl_server_name on;
send_timeout 300s;
location = /index.html {
add_header Cache-Control "no-cache, no-store, must-revalidate" always;
add_header Pragma "no-cache" always;
add_header Expires "0" always;
try_files /index.html =404;
}
location ~* \.(?:js|mjs|css|png|jpg|jpeg|gif|svg|ico|webp|avif|woff2?|ttf|otf|map)$ {
add_header Cache-Control "public, max-age=31536000, immutable" always;
try_files $uri =404;
}
location = /api {
proxy_pass ${BACKEND_UPSTREAM}/api;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /api/ {
proxy_pass ${BACKEND_UPSTREAM}/api/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location = /health {
proxy_pass ${BACKEND_UPSTREAM}/health/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /health/ {
proxy_pass ${BACKEND_UPSTREAM}/health/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location / {
try_files $uri $uri/ /index.html;
}
}

View File

@@ -1031,9 +1031,13 @@ select {
.analytics-table th:first-child,
.analytics-table td:first-child {
width: 28px;
width: 44px;
min-width: 44px;
max-width: 44px;
padding-right: 0;
padding-left: 0;
color: rgb(2 36 86 / 0.5);
text-align: right;
text-align: center;
}
.analytics-table th:last-child,
@@ -1057,11 +1061,6 @@ select {
white-space: normal !important;
}
.technopark-area-table th:first-child,
.technopark-area-table td:first-child {
width: 30px;
}
.sez-priority-table th:nth-child(3),
.sez-priority-table td:nth-child(3) {
width: 140px;
@@ -1183,7 +1182,7 @@ select {
}
.ranking-table__rank-col {
width: 36px;
width: 44px;
}
.ranking-table__region-col {
@@ -1229,8 +1228,13 @@ select {
.ranking-table th:first-child,
.ranking-table td:first-child {
width: 44px;
min-width: 44px;
max-width: 44px;
padding-right: 0;
padding-left: 0;
color: rgb(2 36 86 / 0.56);
text-align: right;
text-align: center;
}
.ranking-table th:nth-child(n + 3),
@@ -1662,7 +1666,14 @@ select {
}
.ranking-table__rank-col {
width: 24px;
width: 36px;
}
.ranking-table th:first-child,
.ranking-table td:first-child {
width: 36px;
min-width: 36px;
max-width: 36px;
}
.ranking-table__metric-col {