chore: add deployment infrastructure
This commit is contained in:
24
.dockerignore
Normal file
24
.dockerignore
Normal 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
|
||||||
84
.gitea/workflows/ci-pr.yml
Normal file
84
.gitea/workflows/ci-pr.yml
Normal 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
39
Dockerfile
Normal 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
17
docker-compose.yml
Normal 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
13
docker/docker-entrypoint.sh
Executable 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 "$@"
|
||||||
61
docker/nginx.conf.template
Normal file
61
docker/nginx.conf.template
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1031,9 +1031,13 @@ select {
|
|||||||
|
|
||||||
.analytics-table th:first-child,
|
.analytics-table th:first-child,
|
||||||
.analytics-table td: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);
|
color: rgb(2 36 86 / 0.5);
|
||||||
text-align: right;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.analytics-table th:last-child,
|
.analytics-table th:last-child,
|
||||||
@@ -1057,11 +1061,6 @@ select {
|
|||||||
white-space: normal !important;
|
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 th:nth-child(3),
|
||||||
.sez-priority-table td:nth-child(3) {
|
.sez-priority-table td:nth-child(3) {
|
||||||
width: 140px;
|
width: 140px;
|
||||||
@@ -1183,7 +1182,7 @@ select {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ranking-table__rank-col {
|
.ranking-table__rank-col {
|
||||||
width: 36px;
|
width: 44px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ranking-table__region-col {
|
.ranking-table__region-col {
|
||||||
@@ -1229,8 +1228,13 @@ select {
|
|||||||
|
|
||||||
.ranking-table th:first-child,
|
.ranking-table th:first-child,
|
||||||
.ranking-table td: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);
|
color: rgb(2 36 86 / 0.56);
|
||||||
text-align: right;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ranking-table th:nth-child(n + 3),
|
.ranking-table th:nth-child(n + 3),
|
||||||
@@ -1662,7 +1666,14 @@ select {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ranking-table__rank-col {
|
.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 {
|
.ranking-table__metric-col {
|
||||||
|
|||||||
Reference in New Issue
Block a user