From 4ca10f3d9bcab2b24a615d0e0b4578b96c9c959f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Gruszczy=C5=84ski?= Date: Mon, 13 Apr 2026 08:25:14 +0200 Subject: [PATCH] fixes --- backend/Dockerfile | 2 +- backend/tests/test_routers.py | 62 ++++++++++++++++ frontend/nginx/default.conf | 74 ------------------- .../app/features/auth/login-page.component.ts | 4 +- frontend/src/styles/layout.css | 1 - frontend/src/styles/pages.css | 21 ------ reverse-proxy/Dockerfile | 4 + reverse-proxy/default.conf | 65 ++++++++++++++++ 8 files changed, 134 insertions(+), 99 deletions(-) create mode 100644 backend/tests/test_routers.py create mode 100644 reverse-proxy/Dockerfile create mode 100644 reverse-proxy/default.conf diff --git a/backend/Dockerfile b/backend/Dockerfile index e3e416b..7ba22c2 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.13-slim +FROM python:3.14-slim ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1 diff --git a/backend/tests/test_routers.py b/backend/tests/test_routers.py new file mode 100644 index 0000000..567c8e6 --- /dev/null +++ b/backend/tests/test_routers.py @@ -0,0 +1,62 @@ +from fastapi.testclient import TestClient + +from app.main import app + + +def test_router_list_marks_global_ssh_key_usage(monkeypatch, tmp_path): + monkeypatch.setenv("DATABASE_URL", f"sqlite:///{tmp_path / 'routers.db'}") + monkeypatch.setenv("DATA_DIR", str(tmp_path / 'data')) + monkeypatch.setenv("SECRET_KEY", "test-secret") + monkeypatch.setenv("DEFAULT_ADMIN_USERNAME", "admin") + monkeypatch.setenv("DEFAULT_ADMIN_PASSWORD", "admin") + + with TestClient(app) as client: + login_response = client.post("/api/auth/login", data={"username": "admin", "password": "admin"}) + token = login_response.json()["access_token"] + headers = {"Authorization": f"Bearer {token}"} + + settings_response = client.put( + "/api/settings", + json={ + "backup_retention_days": 7, + "log_retention_days": 7, + "export_cron": "", + "binary_cron": "", + "retention_cron": "", + "enable_auto_export": False, + "connection_test_interval_minutes": 0, + "global_ssh_key": "-----BEGIN OPENSSH PRIVATE KEY-----\nabc\n-----END OPENSSH PRIVATE KEY-----", + "pushover_token": None, + "pushover_userkey": None, + "notify_failures_only": True, + "smtp_host": None, + "smtp_port": 587, + "smtp_login": None, + "smtp_password": None, + "smtp_notifications_enabled": False, + "recipient_email": None, + "clear_global_ssh_key": False + }, + headers=headers, + ) + assert settings_response.status_code == 200 + + create_response = client.post( + "/api/routers", + json={ + "name": "edge01", + "host": "10.0.0.1", + "port": 22, + "ssh_user": "admin", + "ssh_password": None, + "ssh_key": None + }, + headers=headers, + ) + assert create_response.status_code == 200 + + list_response = client.get("/api/routers", headers=headers) + assert list_response.status_code == 200 + payload = list_response.json() + assert payload[0]["uses_global_ssh_key"] is True + assert payload[0]["has_effective_ssh_key"] is True diff --git a/frontend/nginx/default.conf b/frontend/nginx/default.conf index d3bb718..9fa0d80 100644 --- a/frontend/nginx/default.conf +++ b/frontend/nginx/default.conf @@ -1,13 +1,3 @@ -upstream backend_upstream { - server backend:8000; - keepalive 16; -} - -map $uri $static_file { - ~*\.(?:css|js|mjs|png|jpg|jpeg|gif|svg|ico|webp|woff2?)$ 1; - default 0; -} - server { listen 80; server_name _; @@ -18,71 +8,7 @@ server { root /usr/share/nginx/html; index index.html; - #gzip on; - #gzip_comp_level 5; - #gzip_min_length 1024; - #gzip_types text/plain text/css text/javascript application/javascript application/json application/xml image/svg+xml; - - add_header X-Content-Type-Options "nosniff" always; - add_header X-Frame-Options "SAMEORIGIN" always; - add_header Referrer-Policy "strict-origin-when-cross-origin" always; - add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always; - - proxy_http_version 1.1; - 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; - proxy_set_header Connection ""; - proxy_buffering off; - proxy_read_timeout 60s; - proxy_send_timeout 60s; - - location ^~ /assets/ { - expires 7d; - add_header Cache-Control "public, max-age=604800, immutable" always; - try_files $uri =404; - } - - location = /favicon.ico { - expires 7d; - add_header Cache-Control "public, max-age=604800" always; - try_files $uri =404; - } - - location = /index.html { - add_header Cache-Control "no-store, no-cache" always; - try_files $uri =404; - } - - location ^~ /api/ { - add_header Cache-Control "no-store, no-cache, must-revalidate" always; - add_header Pragma "no-cache" always; - proxy_pass http://backend_upstream/api/; - } - - location = /docs { - add_header Cache-Control "no-store, no-cache, must-revalidate" always; - proxy_pass http://backend_upstream/docs; - } - - location = /openapi.json { - add_header Cache-Control "no-store, no-cache, must-revalidate" always; - proxy_pass http://backend_upstream/openapi.json; - } - - location = /redoc { - add_header Cache-Control "no-store, no-cache, must-revalidate" always; - proxy_pass http://backend_upstream/redoc; - } - location / { try_files $uri $uri/ /index.html; - - if ($static_file) { - add_header Cache-Control "max-age=600" always; - } - - add_header Cache-Control "no-store, no-cache" always; } } diff --git a/frontend/src/app/features/auth/login-page.component.ts b/frontend/src/app/features/auth/login-page.component.ts index 00bdc06..285924b 100644 --- a/frontend/src/app/features/auth/login-page.component.ts +++ b/frontend/src/app/features/auth/login-page.component.ts @@ -24,8 +24,8 @@ export class LoginPageComponent { error = ''; submitting = false; readonly form = this.fb.nonNullable.group({ - username: ['admin', Validators.required], - password: ['admin', Validators.required] + username: ['', Validators.required], + password: ['', Validators.required] }); submit() { diff --git a/frontend/src/styles/layout.css b/frontend/src/styles/layout.css index e94e161..ed26297 100644 --- a/frontend/src/styles/layout.css +++ b/frontend/src/styles/layout.css @@ -111,7 +111,6 @@ body.dark-theme .topbar { cursor: pointer; } -.topbar__lang-picker::after, .auth-toolbar__select-wrap::after { content: '\e902'; font-family: 'primeicons'; diff --git a/frontend/src/styles/pages.css b/frontend/src/styles/pages.css index b7a0e15..dfe2e62 100644 --- a/frontend/src/styles/pages.css +++ b/frontend/src/styles/pages.css @@ -2438,16 +2438,6 @@ body.dark-theme .api-connection-banner{ min-width: 132px; } -.topbar__lang-picker::after{ - content: "▾"; - position: absolute; - right: 0.8rem; - top: 50%; - transform: translateY(-50%); - pointer-events: none; - color: var(--text-soft); -} - .topbar__lang-select option{ background: var(--surface-1); color: var(--text-main); @@ -3377,17 +3367,6 @@ body.dark-theme .p-confirm-dialog .p-confirm-dialog-icon{ } } -/* 2026-04 layout fixes: auth centering and storage header spacing */ - - - - - - - - - - .storage-browser__header{ grid-template-columns: minmax(0, 1fr); gap: 1rem; diff --git a/reverse-proxy/Dockerfile b/reverse-proxy/Dockerfile new file mode 100644 index 0000000..3a741b8 --- /dev/null +++ b/reverse-proxy/Dockerfile @@ -0,0 +1,4 @@ +FROM nginx:mainline +COPY reverse-proxy/default.conf /etc/nginx/conf.d/default.conf +EXPOSE 80 +CMD ["nginx", "-g", "daemon off;"] diff --git a/reverse-proxy/default.conf b/reverse-proxy/default.conf new file mode 100644 index 0000000..cc902ee --- /dev/null +++ b/reverse-proxy/default.conf @@ -0,0 +1,65 @@ +upstream frontend_upstream { + server frontend:80; + keepalive 16; +} + +upstream backend_upstream { + server backend:8000; + keepalive 16; +} + +map $uri $static_file { + ~*\.(?:css|js|mjs|png|jpg|jpeg|gif|svg|ico|webp|woff2?)$ 1; + default 0; +} + +server { + listen 80; + server_name _; + + server_tokens off; + + add_header X-Content-Type-Options "nosniff" always; + add_header X-Frame-Options "SAMEORIGIN" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always; + + proxy_http_version 1.1; + 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; + proxy_set_header Connection ""; + proxy_read_timeout 60s; + proxy_send_timeout 60s; + + location ^~ /api/ { + add_header Cache-Control "no-store, no-cache, must-revalidate" always; + proxy_pass http://backend_upstream/api/; + } + + location = /docs { + add_header Cache-Control "no-store, no-cache, must-revalidate" always; + proxy_pass http://backend_upstream/docs; + } + + location = /openapi.json { + add_header Cache-Control "no-store, no-cache, must-revalidate" always; + proxy_pass http://backend_upstream/openapi.json; + } + + location = /redoc { + add_header Cache-Control "no-store, no-cache, must-revalidate" always; + proxy_pass http://backend_upstream/redoc; + } + + location / { + proxy_pass http://frontend_upstream; + + if ($static_file) { + add_header Cache-Control "public, max-age=31536000, immutable" always; + } + + add_header Cache-Control "no-store, no-cache" always; + } +}