Monorepo Integration: Unified Backend, Frontend & Documentation

- Reorganize project into monorepo structure
  - backend/app/ - New FastAPI backend (modular with src/)
  - backend/legacy/ - Legacy database modules (relational & vector)
  - frontend/ - React text editor application

- Add launcher.py for easy full-stack startup
- Complete documentation in README.md
  - Quick start guide
  - API endpoints reference
  - Development setup
  - Troubleshooting

- Refactor main.py to 35 lines (app configuration only)
- Update .gitignore for full-stack project
- Add CHANGELOG.md with version history (v0.1.0-v0.1.1)

Structure is now clean and ready for team collaboration.
This commit is contained in:
Krzysztof Cieślik
2026-04-09 17:06:59 +02:00
parent fddaad962b
commit 6bbb24e633
55 changed files with 808 additions and 93 deletions

3
.env.example Normal file
View File

@@ -0,0 +1,3 @@
ENVIRONMENT=development
DATABASE_PATH=archivium.db
ALLOWED_ORIGINS=http://localhost:3000,http://localhost:5173

67
.gitignore vendored Normal file
View File

@@ -0,0 +1,67 @@
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
env/
venv/
ENV/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
# Virtual environments
.venv/
venv/
env/
# Database
*.db
*.sqlite
*.sqlite3
# IDE
.vscode/
.idea/
*.swp
*.swo
*~
# Environment
.env
.env.local
.env.*.local
# Testing
.pytest_cache/
.coverage
htmlcov/
# OS
.DS_Store
Thumbs.db
# Node/Frontend
node_modules/
npm-debug.log
yarn-error.log
dist/
build/
# Lock files (optional - uncomment if you want to enforce exact versions)
# package-lock.json
# pnpm-lock.yaml

8
.idea/.gitignore generated vendored
View File

@@ -1,8 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

8
.idea/Editor.iml generated
View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="jdk" jdkName="Python 3.13" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@@ -1,6 +0,0 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

7
.idea/misc.xml generated
View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Black">
<option name="sdkName" value="Python 3.13" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.13" project-jdk-type="Python SDK" />
</project>

8
.idea/modules.xml generated
View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/Editor.iml" filepath="$PROJECT_DIR$/.idea/Editor.iml" />
</modules>
</component>
</project>

83
CHANGELOG.md Normal file
View File

@@ -0,0 +1,83 @@
# Changelog
## [0.1.1] - 2026-04-09
### Changed
- Reorganized project structure: moved all modules to `src/` folder
- Refactored endpoints into separate router modules:
- `src/routers/init.py` - System initialization endpoint
- `src/routers/login.py` - Authentication endpoint
- `src/routers/status.py` - Status check endpoint
- Reduced `main.py` from 100+ lines to 35 lines (only app configuration)
- Updated all internal imports to use relative imports within `src/`
### Project Structure
```
archivium-backend/
├── main.py # Entry point (35 lines)
├── src/
│ ├── __init__.py
│ ├── config.py # Configuration
│ ├── models.py # Database models
│ ├── schemas.py # Request/response schemas
│ ├── database.py # Database setup
│ ├── security.py # Password hashing
│ └── routers/
│ ├── __init__.py
│ ├── init.py # POST /api/init
│ ├── login.py # POST /api/login
│ └── status.py # GET /api/status
├── pyproject.toml
├── requirements.txt
└── README.md
```
---
## [0.1.0] - 2026-04-09
### Changed
- Removed excessive Polish comments and restructured code for readability
- Refactored monolithic `main.py` into modular structure:
- `config.py` - Environment configuration and CORS settings
- `models.py` - SQLAlchemy ORM models
- `schemas.py` - Pydantic request/response schemas with validation
- `database.py` - Database initialization and session management
- `security.py` - Password hashing and recovery key generation
- `main.py` - FastAPI application and endpoint handlers
- Added official Python docstrings for public functions and classes only
- Improved project metadata with description and version in FastAPI app
### Security Improvements
- Restricted CORS to explicit allowed origins instead of wildcard ("*")
- Limited allowed HTTP methods to POST and GET only
- Restricted allowed headers to Content-Type only
- Added password validation (minimum 8 characters, maximum 128)
- Improved error handling with try-except for password verification
- Database operations now properly managed with dependency injection
### Added
- `pyproject.toml` for modern Python package management (compatible with uv)
- `requirements.txt` for traditional pip/env management
- Proper dependency pinning with specific versions
- Database initialization on startup event
- Dependency injection for database sessions via `Depends(get_db)`
- Recovery key generation moved to dedicated security module
- Startup lifecycle event to ensure schema creation
### Dependencies
- fastapi>=0.104.0
- uvicorn[standard]>=0.24.0
- pydantic>=2.5.0
- sqlalchemy>=2.0.0
- passlib[argon2]>=1.7.4
### Notes
- SQLite remains in use for development (no encryption at rest)
- For production deployment, consider:
- Using PostgreSQL or equivalent encrypted database
- Setting ENVIRONMENT=production env var
- Configuring CORS_ORIGINS for specific domains
- Enabling HTTPS/SSL
- Implementing rate limiting
- Adding request logging and monitoring

334
README.md Normal file
View File

@@ -0,0 +1,334 @@
# 📚 Archivium - System Zarządzania Archiwum
**Archivium** to integrowany system do szyfrowania, przechowywania i wyszukiwania archiwów z bezpiecznym uwierzytelnianiem i interfejsem edytora tekstu.
## 🏗️ Architektura projektu
```
archivium/
├── backend/
│ ├── app/ # Nowoczesny backend FastAPI
│ │ ├── src/
│ │ │ ├── config.py # Konfiguracja
│ │ │ ├── models.py # Modele bazy
│ │ │ ├── schemas.py # Walidacja
│ │ │ ├── database.py # Zarządzanie BD
│ │ │ ├── security.py # Hashing hasła
│ │ │ └── routers/ # Endpointy API
│ │ │ ├── init.py
│ │ │ ├── login.py
│ │ │ └── status.py
│ │ ├── main.py # Punkt wejścia (35 linii)
│ │ └── pyproject.toml
│ │
│ └── legacy/ # Stare moduły bazy danych
│ ├── relational_database.py
│ └── vector_database.py
├── frontend/ # React aplikacja (edytor tekstu)
│ ├── public/
│ ├── src/
│ └── package.json
├── launcher.py # Uruchamiacz całej aplikacji
├── requirements.txt # Zależności Python
├── README.md # Ta dokumentacja
├── CHANGELOG.md # Historia zmian
└── .git/ # Repozytorium Git
```
## 🚀 Szybki start
### Uruchomienie całej aplikacji (jedno polecenie)
```bash
python launcher.py
```
To uruchomi:
- ✅ Backend FastAPI (port 8000)
- ✅ Frontend React (port 3000)
- ✅ Otworzy przeglądarkę na http://localhost:3000
### Ręczne uruchomienie poszczególnych części
**Backend:**
```bash
cd backend/app
python main.py
```
**Frontend:**
```bash
cd frontend
npm install
npm start
```
## 📡 API Endpoints
### 1. Inicjalizacja systemu
```http
POST /api/init
Content-Type: application/json
{
"password": "twoje_bezpieczne_haslo_8znaków_min"
}
```
**Response:**
```json
{
"status": "success",
"recovery_key": "a1b2c3d4e5f6...",
"message": "System initialized. Save recovery key in safe place."
}
```
### 2. Logowanie - hasłem głównym
```http
POST /api/login
Content-Type: application/json
{
"password": "twoje_haslo",
"is_recovery": false
}
```
**Response:**
```json
{
"status": "success",
"message": "Successfully authenticated"
}
```
### 3. Logowanie - kluczem awaryjnym
```http
POST /api/login
Content-Type: application/json
{
"password": "a1b2c3d4e5f6...",
"is_recovery": true
}
```
**Response:**
```json
{
"status": "success",
"message": "Authenticated with recovery key. Please change password."
}
```
### 4. Sprawdzenie statusu
```http
GET /api/status
```
**Response:**
```json
{
"is_initialized": true
}
```
## 🔐 Bezpieczeństwo
### Zaimplementowane zabezpieczenia:
-**Hashing haseł** - Argon2 (type="ID")
-**Losowe klucze awaryjne** - 32 znaki hex
-**CORS** - ograniczony do zaufanych domen
-**Walidacja danych** - Pydantic
-**Dwie ścieżki dostępu** - hasło główne + klucz awaryjny
### Zmienne środowiskowe (.env)
```bash
ENVIRONMENT=development # development | production
DATABASE_PATH=archivium.db # lokalizacja bazy
ALLOWED_ORIGINS=http://localhost:3000,http://localhost:5173
```
### Rekomendacje produkcyjne:
1. Użyj **PostgreSQL** zamiast SQLite
2. Włącz **HTTPS/SSL**
3. Dodaj **rate limiting**
4. Włącz **logging i monitoring**
5. Regularne **backupy** bazy danych
6. Przechowuj sekrety w zmiennych środowiskowych
## 📚 Szczegółowa dokumentacja struktury
### Backend - Endpointy (src/routers/)
**init.py** - `POST /api/init`
- Inicjalizuje system
- Generuje recovery key (32 znaki)
- Hashuje hasło główne z Argon2
**login.py** - `POST /api/login`
- Logowanie hasłem głównym lub kluczem awaryjnym
- Weryfikuje hash haseł
- Zwraca status autoryzacji
**status.py** - `GET /api/status`
- Sprawdza czy system zainicjalizowany
- Zwraca boolean `is_initialized`
### Backend - Core modules (src/)
**config.py** - Konfiguracja
- CORS configuration
- DATABASE_PATH z zmiennych środowiskowych
- ALLOWED_ORIGINS
**models.py** - SQLAlchemy ORM
- SecurityConfig model
- Tabela `security_config`
- Przechowuje hashe haseł i kluczy
**schemas.py** - Pydantic validation
- InitRequest - walidacja do /api/init
- LoginRequest - walidacja do /api/login
**database.py** - Zarządzanie bazą
- SQLite setup
- Session management
- `init_db()` - inicjalizuje schemat
- `get_db()` - dependency injection dla sessionów
**security.py** - Funkcje bezpieczeństwa
- `hash_password(password: str) -> str`
- `verify_password(password: str, hash_value: str) -> bool`
- `generate_recovery_key() -> str`
**main.py** - FastAPI aplikacja (35 linii!)
- Konfiguracja CORS middleware
- Include routery
- Startup event dla init_db()
## 🛠️ Development
### Instalacja dependencies
```bash
# Backend
pip install -r requirements.txt
# Lub z uv:
cd backend/app && uv sync
# Frontend
cd frontend && npm install
```
### Testowanie API
```bash
# Curl - Inicjalizacja
curl -X POST http://localhost:8000/api/init \
-H "Content-Type: application/json" \
-d '{"password": "TestPassword123"}'
# Curl - Logowanie
curl -X POST http://localhost:8000/api/login \
-H "Content-Type: application/json" \
-d '{"password": "TestPassword123", "is_recovery": false}'
# Status
curl http://localhost:8000/api/status
```
### Dokumentacja interaktywna
Gdy serwer działa, otwórz: **http://localhost:8000/docs**
- Swagger UI do testowania API
- Automatyczna dokumentacja OpenAPI
- Try it out - testuj bezpośrednio z przeglądarki
## 📝 Historia zmian
Pełna historia zmian w [CHANGELOG.md](CHANGELOG.md)
### v0.1.1 - Monorepo Integration
- Reorganizacja całego projektu do struktury monorepo
- Backend w `backend/app/` (35 linii main.py!)
- Launcher do uruchamiania całej aplikacji
- Integracja z starym kodem z `Database/` i `TextEditor/`
- Kompletna dokumentacja
### v0.1.0 - Refactorization
- Refaktoryzacja kodu, usunięcie komentarzy
- Modularyzacja na src/
- Bezpieczeństwo (CORS, Argon2, walidacja)
## 🐛 Troubleshooting
### Problem: Port 8000 już w użyciu
```bash
# Zmień w backend/app/main.py lub użyj:
cd backend/app && uv run uvicorn main:app --port 8001
```
### Problem: Frontend nie łączy się z backendem
Sprawdź w `backend/app/src/config.py`:
```python
ALLOWED_ORIGINS = ["http://localhost:3000"]
```
### Problem: Baza danych uszkodzona
```bash
rm archivium.db # Usuń stary plik
python launcher.py # Przeinicjalizuj
```
### Problem: npm start nie działa
```bash
cd frontend
rm -rf node_modules package-lock.json
npm install
npm start
```
## 👥 Współpraca (Git)
```bash
# Clone repozytorium
git clone http://gitea.archvium.eu:30230/SzymonS/Kod.git
# Stwórz feature branch
git checkout -b feature/my-feature
# Commit
git add .
git commit -am "Add new feature"
# Push
git push origin feature/my-feature
```
## 📦 Zależności
### Backend (Python)
- **FastAPI** (0.104.0+) - Framework
- **SQLAlchemy** (2.0.0+) - ORM
- **Pydantic** (2.5.0+) - Walidacja
- **Passlib[argon2]** (1.7.4+) - Hashing
- **Uvicorn** (0.24.0+) - ASGI server
### Frontend (Node.js)
- **React** - UI framework
- Inne (patrz: `frontend/package.json`)
Pełna lista: [requirements.txt](requirements.txt)
## 📞 Support
Kontakt: SzymonS @ gitea.archvium.eu
---
**Ostatnia aktualizacja:** April 9, 2026

35
backend/app/main.py Normal file
View File

@@ -0,0 +1,35 @@
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from src.config import ALLOWED_ORIGINS
from src.database import init_db
from src.routers import init, login, status
app = FastAPI(
title="Archivium Local Backend",
description="Local archive encryption and authentication system",
version="0.1.0",
)
app.add_middleware(
CORSMiddleware,
allow_origins=ALLOWED_ORIGINS,
allow_credentials=True,
allow_methods=["POST", "GET"],
allow_headers=["Content-Type"],
)
app.include_router(init.router)
app.include_router(login.router)
app.include_router(status.router)
@app.on_event("startup")
def startup():
"""Initialize database on startup."""
init_db()
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="127.0.0.1", port=8000)

View File

@@ -0,0 +1,19 @@
[project]
name = "archivium-backend"
version = "0.1.0"
description = "Local archive encryption and authentication system"
requires-python = ">=3.9,<3.13"
dependencies = [
"fastapi>=0.104.0",
"uvicorn[standard]>=0.24.0",
"pydantic>=2.5.0",
"sqlalchemy>=2.0.0",
"passlib[argon2]>=1.7.4",
]
[project.optional-dependencies]
dev = [
"pytest>=7.0.0",
"pytest-asyncio>=0.21.0",
"httpx>=0.25.0",
]

View File

@@ -0,0 +1 @@
"""Archivium Backend Application."""

10
backend/app/src/config.py Normal file
View File

@@ -0,0 +1,10 @@
import os
DB_PATH = os.getenv("DATABASE_PATH", "archivium.db")
ALLOWED_ORIGINS = os.getenv("ALLOWED_ORIGINS", "http://localhost:3000,http://localhost:5173").split(",")
if os.getenv("ENVIRONMENT") == "development":
ALLOWED_ORIGINS = ["http://localhost:3000", "http://localhost:5173"]
elif os.getenv("ENVIRONMENT") == "production":
ALLOWED_ORIGINS = os.getenv("CORS_ORIGINS", "").split(",")

View File

@@ -0,0 +1,28 @@
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, Session
from .config import DB_PATH
from .models import Base
DATABASE_URL = f"sqlite:///{DB_PATH}"
engine = create_engine(
DATABASE_URL,
connect_args={"check_same_thread": False},
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
def init_db():
"""Initialize database schema."""
Base.metadata.create_all(bind=engine)
def get_db():
"""Provide database session for dependency injection."""
db = SessionLocal()
try:
yield db
finally:
db.close()

13
backend/app/src/models.py Normal file
View File

@@ -0,0 +1,13 @@
from sqlalchemy import Column, Integer, String
from sqlalchemy.orm import declarative_base
Base = declarative_base()
class SecurityConfig(Base):
"""Storage for password and recovery key hashes."""
__tablename__ = "security_config"
id = Column(Integer, primary_key=True, index=True)
password_hash = Column(String, nullable=False)
recovery_key_hash = Column(String, nullable=False)

View File

@@ -0,0 +1 @@
"""Routers for Archivium Backend."""

View File

@@ -0,0 +1,39 @@
import os
from fastapi import APIRouter, HTTPException, Depends
from sqlalchemy.orm import Session
from ..database import get_db
from ..models import SecurityConfig
from ..schemas import InitRequest
from ..security import hash_password, generate_recovery_key
from ..config import DB_PATH
router = APIRouter(prefix="/api", tags=["init"])
@router.post("/init")
def initialize_system(request: InitRequest, db: Session = Depends(get_db)):
"""Initialize system with master password and generate recovery key."""
if os.path.exists(DB_PATH):
raise HTTPException(
status_code=400,
detail="System already initialized",
)
recovery_key = generate_recovery_key()
hashed_password = hash_password(request.password)
hashed_recovery = hash_password(recovery_key)
db.add(
SecurityConfig(
password_hash=hashed_password,
recovery_key_hash=hashed_recovery,
)
)
db.commit()
return {
"status": "success",
"recovery_key": recovery_key,
"message": "System initialized. Save recovery key in safe place.",
}

View File

@@ -0,0 +1,50 @@
import os
from fastapi import APIRouter, HTTPException, Depends
from sqlalchemy.orm import Session
from ..database import get_db
from ..models import SecurityConfig
from ..schemas import LoginRequest
from ..security import verify_password
from ..config import DB_PATH
router = APIRouter(prefix="/api", tags=["login"])
@router.post("/login")
def login(request: LoginRequest, db: Session = Depends(get_db)):
"""Authenticate with master password or recovery key."""
if not os.path.exists(DB_PATH):
raise HTTPException(
status_code=404,
detail="System not initialized",
)
config = db.query(SecurityConfig).first()
if not config:
raise HTTPException(
status_code=500,
detail="System configuration error",
)
if request.is_recovery:
if not verify_password(request.password, config.recovery_key_hash):
raise HTTPException(
status_code=401,
detail="Invalid recovery key",
)
return {
"status": "success",
"message": "Authenticated with recovery key. Please change password.",
}
if not verify_password(request.password, config.password_hash):
raise HTTPException(
status_code=401,
detail="Invalid password",
)
return {
"status": "success",
"message": "Successfully authenticated",
}

View File

@@ -0,0 +1,12 @@
import os
from fastapi import APIRouter
from ..config import DB_PATH
router = APIRouter(prefix="/api", tags=["status"])
@router.get("/status")
def get_status():
"""Check if system is initialized."""
return {"is_initialized": os.path.exists(DB_PATH)}

View File

@@ -0,0 +1,10 @@
from pydantic import BaseModel, Field
class InitRequest(BaseModel):
password: str = Field(..., min_length=8, max_length=128)
class LoginRequest(BaseModel):
password: str = Field(..., min_length=1, max_length=128)
is_recovery: bool = False

View File

@@ -0,0 +1,20 @@
import secrets
from passlib.hash import argon2
def hash_password(password: str) -> str:
"""Hash password using Argon2."""
return argon2.using(type="ID").hash(password)
def verify_password(password: str, hash_value: str) -> bool:
"""Verify password against hash."""
try:
return argon2.using(type="ID").verify(password, hash_value)
except Exception:
return False
def generate_recovery_key() -> str:
"""Generate random recovery key (32 hex characters)."""
return secrets.token_hex(16)

View File

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

83
launcher.py Normal file
View File

@@ -0,0 +1,83 @@
"""Archivium Launcher - Uruchamia całą aplikację"""
import subprocess
import os
import sys
import time
import webbrowser
def install_missing_packages():
"""Instaluj brakujące pakiety"""
packages = ["uvicorn", "fastapi", "sqlalchemy", "passlib[argon2]"]
for package in packages:
try:
__import__(package.split("[")[0].replace("-", "_"))
except ImportError:
print(f"[*] Instalowanie: {package}...")
subprocess.check_call([sys.executable, "-m", "pip", "install", package])
def run_archivium():
"""Uruchom całą aplikację Archivium"""
install_missing_packages()
base_dir = os.path.dirname(os.path.abspath(__file__))
backend_dir = os.path.join(base_dir, "backend", "app")
frontend_dir = os.path.join(base_dir, "frontend")
print("\n" + "="*50)
print(" ARCHIVIUM - System Zarządzania Archiwum")
print("="*50 + "\n")
print("[1/3] Startuje backend (Port 8000)...")
backend_process = subprocess.Popen(
[sys.executable, "main.py"],
cwd=backend_dir
)
print("[2/3] Startuje frontend (Port 3000)...")
try:
frontend_process = subprocess.Popen(
"npm start",
shell=True,
cwd=frontend_dir,
)
except Exception as e:
print(f"[!] Uwaga: Nie udało się uruchomić frontendu: {e}")
print("[*] Frontend może wymagać: npm install && npm start")
frontend_process = None
print("[3/3] Otwieranie przeglądarki...")
time.sleep(3)
try:
webbrowser.open("http://localhost:3000")
except Exception as e:
print(f"[!] Nie udało się otworzyć przeglądarki: {e}")
print("[*] Otwórz ręcznie: http://localhost:3000")
print("\n" + "="*50)
print("✓ Aplikacja uruchomiona!")
print(" Backend: http://localhost:8000")
print(" Frontend: http://localhost:3000")
print(" Docs: http://localhost:8000/docs")
print("="*50)
print("\nAby zatrzymać: Ctrl+C\n")
try:
backend_process.wait()
if frontend_process:
frontend_process.wait()
except KeyboardInterrupt:
print("\n[*] Zatrzymywanie systemu...")
backend_process.terminate()
if frontend_process:
frontend_process.terminate()
backend_process.wait()
if frontend_process:
frontend_process.wait()
print("[✓] System zatrzymany")
if __name__ == "__main__":
run_archivium()

56
main.py
View File

@@ -1,56 +0,0 @@
import subprocess
import os
import sys
import time
import webbrowser
def install_missing_packages():
packages = ["uvicorn", "fastapi", "sentence-transformers"]
for package in packages:
try:
__import__(package.replace("-", "_"))
except ImportError:
print(f"[*] Instalowanie brakującej biblioteki: {package}...")
subprocess.check_call([sys.executable, "-m", "pip", "install", package])
def run_archivium():
install_missing_packages()
base_dir = os.path.dirname(os.path.abspath(__file__))
backend_script = os.path.join(base_dir, "Database", "database.py")
frontend_dir = os.path.join(base_dir, "TextEditor")
print("\n--- URUCHAMIANIE SYSTEMU ARCHIVIUM ---")
print(f"[*] Startuję bazę danych...")
backend_process = subprocess.Popen(
[sys.executable, backend_script],
cwd=os.path.join(base_dir, "Database")
)
print(f"[*] Startuję edytor...")
frontend_process = subprocess.Popen(
"npm start",
shell=True,
cwd=frontend_dir,
creationflags=subprocess.CREATE_NEW_CONSOLE
)
print("[*] Oczekiwanie na gotowość...")
time.sleep(5)
webbrowser.open("http://localhost:3000")
try:
backend_process.wait()
frontend_process.wait()
except KeyboardInterrupt:
print("\n[*] Zamykanie systemu...")
backend_process.terminate()
frontend_process.terminate()
if __name__ == "__main__":
run_archivium()

Binary file not shown.