From 6bbb24e633835279518e623bd456dd0145f423b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Cie=C5=9Blik?= Date: Thu, 9 Apr 2026 17:06:59 +0200 Subject: [PATCH] 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. --- .env.example | 3 + .gitignore | 67 ++++ .idea/.gitignore | 8 - .idea/Editor.iml | 8 - .../inspectionProfiles/profiles_settings.xml | 6 - .idea/misc.xml | 7 - .idea/modules.xml | 8 - CHANGELOG.md | 83 +++++ README.md | 334 ++++++++++++++++++ backend/app/main.py | 35 ++ backend/app/pyproject.toml | 19 + backend/app/src/__init__.py | 1 + backend/app/src/config.py | 10 + backend/app/src/database.py | 28 ++ backend/app/src/models.py | 13 + backend/app/src/routers/__init__.py | 1 + backend/app/src/routers/init.py | 39 ++ backend/app/src/routers/login.py | 50 +++ backend/app/src/routers/status.py | 12 + backend/app/src/schemas.py | 10 + backend/app/src/security.py | 20 ++ .../local_model_miniLM/1_Pooling/config.json | 0 .../legacy}/local_model_miniLM/README.md | 0 .../legacy}/local_model_miniLM/config.json | 0 .../config_sentence_transformers.json | 0 .../local_model_miniLM/model.safetensors | Bin .../legacy}/local_model_miniLM/modules.json | 0 .../sentence_bert_config.json | 0 .../legacy}/local_model_miniLM/tokenizer.json | 0 .../local_model_miniLM/tokenizer_config.json | 0 .../legacy}/relational_database.py | 0 .../legacy}/vector_database.py | 0 {TextEditor => frontend}/.gitignore | 0 {TextEditor => frontend}/README.md | 0 {TextEditor => frontend}/package-lock.json | 0 {TextEditor => frontend}/package.json | 0 {TextEditor => frontend}/public/favicon.ico | Bin {TextEditor => frontend}/public/index.html | 0 {TextEditor => frontend}/public/logo192.png | Bin {TextEditor => frontend}/public/logo512.png | Bin {TextEditor => frontend}/public/manifest.json | 0 {TextEditor => frontend}/public/robots.txt | 0 {TextEditor => frontend}/src/App.css | 0 {TextEditor => frontend}/src/App.js | 0 {TextEditor => frontend}/src/App.test.js | 0 {TextEditor => frontend}/src/MathField.js | 0 {TextEditor => frontend}/src/TextEditor.js | 0 {TextEditor => frontend}/src/index.css | 0 {TextEditor => frontend}/src/index.js | 0 {TextEditor => frontend}/src/logo.svg | 0 .../src/reportWebVitals.js | 0 {TextEditor => frontend}/src/setupTests.js | 0 launcher.py | 83 +++++ main.py | 56 --- requirements.txt | Bin 3118 -> 303 bytes 55 files changed, 808 insertions(+), 93 deletions(-) create mode 100644 .env.example create mode 100644 .gitignore delete mode 100644 .idea/.gitignore delete mode 100644 .idea/Editor.iml delete mode 100644 .idea/inspectionProfiles/profiles_settings.xml delete mode 100644 .idea/misc.xml delete mode 100644 .idea/modules.xml create mode 100644 CHANGELOG.md create mode 100644 README.md create mode 100644 backend/app/main.py create mode 100644 backend/app/pyproject.toml create mode 100644 backend/app/src/__init__.py create mode 100644 backend/app/src/config.py create mode 100644 backend/app/src/database.py create mode 100644 backend/app/src/models.py create mode 100644 backend/app/src/routers/__init__.py create mode 100644 backend/app/src/routers/init.py create mode 100644 backend/app/src/routers/login.py create mode 100644 backend/app/src/routers/status.py create mode 100644 backend/app/src/schemas.py create mode 100644 backend/app/src/security.py rename {Database => backend/legacy}/local_model_miniLM/1_Pooling/config.json (100%) rename {Database => backend/legacy}/local_model_miniLM/README.md (100%) rename {Database => backend/legacy}/local_model_miniLM/config.json (100%) rename {Database => backend/legacy}/local_model_miniLM/config_sentence_transformers.json (100%) rename {Database => backend/legacy}/local_model_miniLM/model.safetensors (100%) rename {Database => backend/legacy}/local_model_miniLM/modules.json (100%) rename {Database => backend/legacy}/local_model_miniLM/sentence_bert_config.json (100%) rename {Database => backend/legacy}/local_model_miniLM/tokenizer.json (100%) rename {Database => backend/legacy}/local_model_miniLM/tokenizer_config.json (100%) rename {Database => backend/legacy}/relational_database.py (100%) rename {Database => backend/legacy}/vector_database.py (100%) rename {TextEditor => frontend}/.gitignore (100%) rename {TextEditor => frontend}/README.md (100%) rename {TextEditor => frontend}/package-lock.json (100%) rename {TextEditor => frontend}/package.json (100%) rename {TextEditor => frontend}/public/favicon.ico (100%) rename {TextEditor => frontend}/public/index.html (100%) rename {TextEditor => frontend}/public/logo192.png (100%) rename {TextEditor => frontend}/public/logo512.png (100%) rename {TextEditor => frontend}/public/manifest.json (100%) rename {TextEditor => frontend}/public/robots.txt (100%) rename {TextEditor => frontend}/src/App.css (100%) rename {TextEditor => frontend}/src/App.js (100%) rename {TextEditor => frontend}/src/App.test.js (100%) rename {TextEditor => frontend}/src/MathField.js (100%) rename {TextEditor => frontend}/src/TextEditor.js (100%) rename {TextEditor => frontend}/src/index.css (100%) rename {TextEditor => frontend}/src/index.js (100%) rename {TextEditor => frontend}/src/logo.svg (100%) rename {TextEditor => frontend}/src/reportWebVitals.js (100%) rename {TextEditor => frontend}/src/setupTests.js (100%) create mode 100644 launcher.py delete mode 100644 main.py diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..4b725dd --- /dev/null +++ b/.env.example @@ -0,0 +1,3 @@ +ENVIRONMENT=development +DATABASE_PATH=archivium.db +ALLOWED_ORIGINS=http://localhost:3000,http://localhost:5173 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..da56cb0 --- /dev/null +++ b/.gitignore @@ -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 + diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 13566b8..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -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 diff --git a/.idea/Editor.iml b/.idea/Editor.iml deleted file mode 100644 index d8b3f6c..0000000 --- a/.idea/Editor.iml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml deleted file mode 100644 index 105ce2d..0000000 --- a/.idea/inspectionProfiles/profiles_settings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 1d3ce46..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 3206d78..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..2d9281f --- /dev/null +++ b/CHANGELOG.md @@ -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 diff --git a/README.md b/README.md new file mode 100644 index 0000000..1c7a486 --- /dev/null +++ b/README.md @@ -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 diff --git a/backend/app/main.py b/backend/app/main.py new file mode 100644 index 0000000..8f67450 --- /dev/null +++ b/backend/app/main.py @@ -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) diff --git a/backend/app/pyproject.toml b/backend/app/pyproject.toml new file mode 100644 index 0000000..42b944e --- /dev/null +++ b/backend/app/pyproject.toml @@ -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", +] diff --git a/backend/app/src/__init__.py b/backend/app/src/__init__.py new file mode 100644 index 0000000..ea83667 --- /dev/null +++ b/backend/app/src/__init__.py @@ -0,0 +1 @@ +"""Archivium Backend Application.""" diff --git a/backend/app/src/config.py b/backend/app/src/config.py new file mode 100644 index 0000000..5de5b9b --- /dev/null +++ b/backend/app/src/config.py @@ -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(",") diff --git a/backend/app/src/database.py b/backend/app/src/database.py new file mode 100644 index 0000000..d51db8c --- /dev/null +++ b/backend/app/src/database.py @@ -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() diff --git a/backend/app/src/models.py b/backend/app/src/models.py new file mode 100644 index 0000000..9f540ae --- /dev/null +++ b/backend/app/src/models.py @@ -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) diff --git a/backend/app/src/routers/__init__.py b/backend/app/src/routers/__init__.py new file mode 100644 index 0000000..21109f0 --- /dev/null +++ b/backend/app/src/routers/__init__.py @@ -0,0 +1 @@ +"""Routers for Archivium Backend.""" diff --git a/backend/app/src/routers/init.py b/backend/app/src/routers/init.py new file mode 100644 index 0000000..12fca20 --- /dev/null +++ b/backend/app/src/routers/init.py @@ -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.", + } diff --git a/backend/app/src/routers/login.py b/backend/app/src/routers/login.py new file mode 100644 index 0000000..a1ca6be --- /dev/null +++ b/backend/app/src/routers/login.py @@ -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", + } diff --git a/backend/app/src/routers/status.py b/backend/app/src/routers/status.py new file mode 100644 index 0000000..edad17f --- /dev/null +++ b/backend/app/src/routers/status.py @@ -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)} diff --git a/backend/app/src/schemas.py b/backend/app/src/schemas.py new file mode 100644 index 0000000..16d043a --- /dev/null +++ b/backend/app/src/schemas.py @@ -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 diff --git a/backend/app/src/security.py b/backend/app/src/security.py new file mode 100644 index 0000000..a5d4f23 --- /dev/null +++ b/backend/app/src/security.py @@ -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) diff --git a/Database/local_model_miniLM/1_Pooling/config.json b/backend/legacy/local_model_miniLM/1_Pooling/config.json similarity index 100% rename from Database/local_model_miniLM/1_Pooling/config.json rename to backend/legacy/local_model_miniLM/1_Pooling/config.json diff --git a/Database/local_model_miniLM/README.md b/backend/legacy/local_model_miniLM/README.md similarity index 100% rename from Database/local_model_miniLM/README.md rename to backend/legacy/local_model_miniLM/README.md diff --git a/Database/local_model_miniLM/config.json b/backend/legacy/local_model_miniLM/config.json similarity index 100% rename from Database/local_model_miniLM/config.json rename to backend/legacy/local_model_miniLM/config.json diff --git a/Database/local_model_miniLM/config_sentence_transformers.json b/backend/legacy/local_model_miniLM/config_sentence_transformers.json similarity index 100% rename from Database/local_model_miniLM/config_sentence_transformers.json rename to backend/legacy/local_model_miniLM/config_sentence_transformers.json diff --git a/Database/local_model_miniLM/model.safetensors b/backend/legacy/local_model_miniLM/model.safetensors similarity index 100% rename from Database/local_model_miniLM/model.safetensors rename to backend/legacy/local_model_miniLM/model.safetensors diff --git a/Database/local_model_miniLM/modules.json b/backend/legacy/local_model_miniLM/modules.json similarity index 100% rename from Database/local_model_miniLM/modules.json rename to backend/legacy/local_model_miniLM/modules.json diff --git a/Database/local_model_miniLM/sentence_bert_config.json b/backend/legacy/local_model_miniLM/sentence_bert_config.json similarity index 100% rename from Database/local_model_miniLM/sentence_bert_config.json rename to backend/legacy/local_model_miniLM/sentence_bert_config.json diff --git a/Database/local_model_miniLM/tokenizer.json b/backend/legacy/local_model_miniLM/tokenizer.json similarity index 100% rename from Database/local_model_miniLM/tokenizer.json rename to backend/legacy/local_model_miniLM/tokenizer.json diff --git a/Database/local_model_miniLM/tokenizer_config.json b/backend/legacy/local_model_miniLM/tokenizer_config.json similarity index 100% rename from Database/local_model_miniLM/tokenizer_config.json rename to backend/legacy/local_model_miniLM/tokenizer_config.json diff --git a/Database/relational_database.py b/backend/legacy/relational_database.py similarity index 100% rename from Database/relational_database.py rename to backend/legacy/relational_database.py diff --git a/Database/vector_database.py b/backend/legacy/vector_database.py similarity index 100% rename from Database/vector_database.py rename to backend/legacy/vector_database.py diff --git a/TextEditor/.gitignore b/frontend/.gitignore similarity index 100% rename from TextEditor/.gitignore rename to frontend/.gitignore diff --git a/TextEditor/README.md b/frontend/README.md similarity index 100% rename from TextEditor/README.md rename to frontend/README.md diff --git a/TextEditor/package-lock.json b/frontend/package-lock.json similarity index 100% rename from TextEditor/package-lock.json rename to frontend/package-lock.json diff --git a/TextEditor/package.json b/frontend/package.json similarity index 100% rename from TextEditor/package.json rename to frontend/package.json diff --git a/TextEditor/public/favicon.ico b/frontend/public/favicon.ico similarity index 100% rename from TextEditor/public/favicon.ico rename to frontend/public/favicon.ico diff --git a/TextEditor/public/index.html b/frontend/public/index.html similarity index 100% rename from TextEditor/public/index.html rename to frontend/public/index.html diff --git a/TextEditor/public/logo192.png b/frontend/public/logo192.png similarity index 100% rename from TextEditor/public/logo192.png rename to frontend/public/logo192.png diff --git a/TextEditor/public/logo512.png b/frontend/public/logo512.png similarity index 100% rename from TextEditor/public/logo512.png rename to frontend/public/logo512.png diff --git a/TextEditor/public/manifest.json b/frontend/public/manifest.json similarity index 100% rename from TextEditor/public/manifest.json rename to frontend/public/manifest.json diff --git a/TextEditor/public/robots.txt b/frontend/public/robots.txt similarity index 100% rename from TextEditor/public/robots.txt rename to frontend/public/robots.txt diff --git a/TextEditor/src/App.css b/frontend/src/App.css similarity index 100% rename from TextEditor/src/App.css rename to frontend/src/App.css diff --git a/TextEditor/src/App.js b/frontend/src/App.js similarity index 100% rename from TextEditor/src/App.js rename to frontend/src/App.js diff --git a/TextEditor/src/App.test.js b/frontend/src/App.test.js similarity index 100% rename from TextEditor/src/App.test.js rename to frontend/src/App.test.js diff --git a/TextEditor/src/MathField.js b/frontend/src/MathField.js similarity index 100% rename from TextEditor/src/MathField.js rename to frontend/src/MathField.js diff --git a/TextEditor/src/TextEditor.js b/frontend/src/TextEditor.js similarity index 100% rename from TextEditor/src/TextEditor.js rename to frontend/src/TextEditor.js diff --git a/TextEditor/src/index.css b/frontend/src/index.css similarity index 100% rename from TextEditor/src/index.css rename to frontend/src/index.css diff --git a/TextEditor/src/index.js b/frontend/src/index.js similarity index 100% rename from TextEditor/src/index.js rename to frontend/src/index.js diff --git a/TextEditor/src/logo.svg b/frontend/src/logo.svg similarity index 100% rename from TextEditor/src/logo.svg rename to frontend/src/logo.svg diff --git a/TextEditor/src/reportWebVitals.js b/frontend/src/reportWebVitals.js similarity index 100% rename from TextEditor/src/reportWebVitals.js rename to frontend/src/reportWebVitals.js diff --git a/TextEditor/src/setupTests.js b/frontend/src/setupTests.js similarity index 100% rename from TextEditor/src/setupTests.js rename to frontend/src/setupTests.js diff --git a/launcher.py b/launcher.py new file mode 100644 index 0000000..34dd25b --- /dev/null +++ b/launcher.py @@ -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() diff --git a/main.py b/main.py deleted file mode 100644 index df4d0a9..0000000 --- a/main.py +++ /dev/null @@ -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() diff --git a/requirements.txt b/requirements.txt index 8eac4c70da330ae726f58954d22eda0b464c1146..ba94f401fbd1f0cb440ae34ad8bc2f4e997f84e3 100644 GIT binary patch literal 303 zcmXv}!D_=W488j+483fJm?U)clt4!r3Ak0?_q5Z; zWJkBSdD4}B1p9?F=y$x$F5!ffS?%-_a6Z00|I6wk7^3UD+BEgv)M{Rw4T;V|(*SAs zC{kOLxDG(s*}iMdgJ8aS@OH#$ElgcDfZ4mxGo)*vR-BDFn7tVD7^oUTMp_Cf+e*5; z;1%quPZ*^rl6A<6rKgNoJv0ttx|rz%a;q$P2R?a-DV54>-l*Xu|ip(st| znVos_=FO~s|5~JN8qy-&rY^nF=RBRH7x8_bp6Pk3&ptecv`W{h(s!Sh;cLH>be8_* zJEW&{uXFw6|LQ3Alk)DvcBbcbYT~b>^=TcbR_P`@PQzBu&JV)Xjj@~bQWm1T2sBr! z>sHvmDZ4A<9#JIz+E3@zM$LLCkWFy1?QtETfja zJQk535BgNHzDswqwTfcq-Ene~qmN$SkJWOr7Am-{12a5qg=eeN89AuUe>3{@ibLP6 zlVi@@A9_2eva6`UeX{~U@;p^{e;Z+{!(yDJl8tJq(xVUzVTU7f z7~e)u2jPROPCl7MqFh7;+2e7F-b8=Q!A9J#L!Gb)Yv|-sNDpH@jJzg3;Byh;sz z{y|l?(T!$i%j&ojI;+?m5tBZg2Escr3@^QMqhhp1d{JqU=B~`Xr?O62_E2U6^ZSXQF=RMNk(4Hrj2$_BMA*xuZMi5#%ubFo?>@E&jNtQK__-P9HJ?gH z`R?h0{j7cThT5!?PS~$?woIQ<@YmY!#SwVni7i2Q{&owqKW^0*ws9pcSD_B?TR1Ay zWKY;c5!mY4j^pN|e`aT;SlrF1s22{h)8UN4d#qpuC!6B0vK#Sb{$Gw2*y~C5*o1<%V`ezD3dRm39PK;mg|}eH?U)vCYj{ UL4}w3d