diff --git a/Archiwum.zip b/Archiwum.zip new file mode 100644 index 0000000..2aad55c Binary files /dev/null and b/Archiwum.zip differ diff --git a/README.md b/README.md index 341569e..0727c44 100644 --- a/README.md +++ b/README.md @@ -88,8 +88,6 @@ APP_DOMAIN=ksef.local:8785 ./deploy_docker.sh ## Konta -Nie ma już wymogu seedów do logowania. Użyj CLI: - ```bash flask --app run.py create-company flask --app run.py create-user diff --git a/app/__init__.py b/app/__init__.py index 653cfde..86dc4e7 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -1,11 +1,12 @@ -from dotenv import load_dotenv +import time from pathlib import Path + +from dotenv import load_dotenv from flask import Flask, render_template, request, url_for from flask_login import current_user -from werkzeug.middleware.proxy_fix import ProxyFix -from dotenv import load_dotenv from sqlalchemy import inspect, text -from sqlalchemy.exc import SQLAlchemyError +from sqlalchemy.exc import OperationalError, SQLAlchemyError +from werkzeug.middleware.proxy_fix import ProxyFix from config import Config from redis.exceptions import RedisError from app.cli import register_cli @@ -30,7 +31,25 @@ def _ensure_column(table_name: str, column_name: str, ddl: str): db.session.commit() + +def _wait_for_database(app, attempts: int = 30, delay: float = 1.0) -> bool: + for attempt in range(1, attempts + 1): + try: + with db.engine.connect() as conn: + conn.execute(text('SELECT 1')) + if attempt > 1: + app.logger.info('Database became available after %s attempt(s).', attempt) + return True + except OperationalError: + app.logger.warning('Database not ready yet (%s/%s). Waiting...', attempt, attempts) + time.sleep(delay) + return False + def _bootstrap_database(app): + if not _wait_for_database(app): + app.logger.error('Database is still unavailable after waiting. Skipping bootstrap.') + return + try: db.create_all() patches = [ diff --git a/app/services/system_data_service.py b/app/services/system_data_service.py index e043a6c..0887713 100644 --- a/app/services/system_data_service.py +++ b/app/services/system_data_service.py @@ -8,6 +8,7 @@ from pathlib import Path import psutil from flask import current_app from sqlalchemy import inspect +from sqlalchemy.engine import make_url from app.extensions import db from app.models.audit_log import AuditLog @@ -199,19 +200,52 @@ class SystemDataService: continue count = db.session.execute(db.select(db.func.count()).select_from(table)).scalar() or 0 table_rows.append({'table': table_name, 'rows': int(count)}) + uri = current_app.config.get('SQLALCHEMY_DATABASE_URI', '') sqlite_path = None sqlite_size = None + db_driver = None + db_host = None + db_port = None + db_name = None + db_user = None + + try: + url = make_url(uri) + db_driver = url.drivername + db_host = url.host + db_port = url.port + db_name = url.database + db_user = url.username + except Exception: + pass + if uri.startswith('sqlite:///') and not uri.endswith(':memory:'): sqlite_path = uri.replace('sqlite:///', '', 1) try: sqlite_size = self._human_size(Path(sqlite_path).stat().st_size) except FileNotFoundError: sqlite_size = 'brak pliku' + + if engine.name == 'sqlite': + db_label = 'SQLite' + elif engine.name == 'postgresql': + db_label = 'PostgreSQL' + elif engine.name == 'mysql': + db_label = 'MySQL' + else: + db_label = engine.name + table_rows_sorted = sorted(table_rows, key=lambda item: (-item['rows'], item['table'])) return { 'engine': engine.name, + 'engine_label': db_label, + 'driver': db_driver, 'uri': self._mask_uri(uri), + 'host': db_host, + 'port': db_port, + 'database': db_name, + 'username': db_user, 'tables_count': len(table_rows), 'sqlite_path': sqlite_path, 'sqlite_size': sqlite_size, diff --git a/docker-compose.yml b/docker-compose.yml index 9056e7a..5c05cc2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -12,13 +12,25 @@ services: - ./:/app - ./db/sqlite:/app/db/sqlite depends_on: - - redis + redis: + condition: service_healthy + postgres: + condition: service_healthy + required: false + mysql: + condition: service_healthy + required: false restart: unless-stopped redis: image: redis:7-alpine ports: - "6379:6379" + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 5s + timeout: 3s + retries: 20 restart: unless-stopped postgres: @@ -33,6 +45,11 @@ services: - ./db/pgsql:/var/lib/postgresql/data ports: - "${DB_PORT:-5432}:5432" + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-ksef} -d ${DB_NAME:-ksef}"] + interval: 5s + timeout: 5s + retries: 20 restart: unless-stopped mysql: @@ -49,6 +66,11 @@ services: - ./db/mysql:/var/lib/mysql ports: - "${DB_PORT:-3306}:3306" + healthcheck: + test: ["CMD-SHELL", "mysqladmin ping -h 127.0.0.1 -uroot -p${MYSQL_ROOT_PASSWORD:-root} --silent"] + interval: 5s + timeout: 5s + retries: 20 restart: unless-stopped caddy: diff --git a/tests/test_admin_system.py b/tests/test_admin_system.py index 9dbd377..061235e 100644 --- a/tests/test_admin_system.py +++ b/tests/test_admin_system.py @@ -43,6 +43,8 @@ def test_admin_system_data_page(auth_client, monkeypatch): assert 'Połączenie CEIDG' in body assert 'Proces i health systemu' in body assert 'Użytkownicy' in body + assert 'Typ bazy:' in body + assert 'Silnik SQLAlchemy:' in body assert 'Diagnoza' not in body assert 'Co sprawdzić' not in body