# Familienarchiv — C4 Architecture Diagrams

## Level 1 — System Context

Who uses the system and what external systems does it interact with.

```mermaid
C4Context
    title System Context: Familienarchiv

    Person(admin, "Administrator", "Manages users, triggers bulk imports, reviews documents")
    Person(member, "Family Member", "Searches, browses, and reads archived documents")

    System(familienarchiv, "Familienarchiv", "Web application for digitising, organising, and searching family documents")

    Rel(admin, familienarchiv, "Manages via browser", "HTTPS")
    Rel(member, familienarchiv, "Searches and views via browser", "HTTPS")
```

---

## Level 2 — Containers

The deployable units that make up the system and how they communicate.

```mermaid
C4Container
    title Container Diagram: Familienarchiv

    Person(user, "User", "Admin or family member")

    System_Boundary(archiv, "Familienarchiv (Docker Compose)") {
        Container(frontend, "Web Frontend", "SvelteKit / Node.js", "Server-side rendered UI. Handles session cookies, search UI, document viewer, and admin panel.")

        Container(backend, "API Backend", "Spring Boot 4 / Java 21 / Jetty", "REST API. Implements document management, search, user auth, file upload/download, and Excel import.")

        ContainerDb(db, "Relational Database", "PostgreSQL 16", "Stores document metadata, persons, users, permission groups, tags, and Spring Session data.")

        ContainerDb(storage, "Object Storage", "MinIO (S3-compatible)", "Stores the actual document files (PDFs, scans). Objects keyed as documents/{UUID}_{filename}.")

        Container(mc, "Bucket Init Helper", "MinIO Client (mc)", "One-shot container on startup. Creates the archive bucket with private access policy.")
    }

    Rel(user, frontend, "Uses", "HTTPS / Browser")
    Rel(frontend, backend, "API requests with Basic Auth token", "HTTP / REST / JSON")
    Rel(backend, db, "Reads and writes metadata and sessions", "JDBC / SQL")
    Rel(backend, storage, "Uploads and streams document files", "HTTP / S3 API (AWS SDK v2)")
    Rel(mc, storage, "Creates bucket on startup", "MinIO Client CLI")
```

---

## Level 3 — Components: API Backend

The internal structure of the Spring Boot backend.

```mermaid
C4Component
    title Component Diagram: API Backend

    Container(frontend, "Web Frontend", "SvelteKit")
    ContainerDb(db, "PostgreSQL")
    ContainerDb(minio, "MinIO")

    System_Boundary(backend, "API Backend (Spring Boot)") {

        Component(secFilter, "Security Filter Chain", "Spring Security", "Enforces authentication on all requests. Parses Basic Auth header and validates credentials via BCrypt.")
        Component(permAspect, "PermissionAspect", "Spring AOP", "Intercepts methods annotated with @RequirePermission. Checks user's granted authorities against the required permission. Throws 401/403 if denied.")

        Component(docCtrl, "DocumentController", "Spring MVC — /api/documents", "CRUD for documents. Endpoints: search, get by ID, update metadata, upload file, download file, get conversation thread.")
        Component(personCtrl, "PersonController", "Spring MVC — /api/persons", "Lists and searches family members. Also returns all documents sent by a person.")
        Component(userCtrl, "UserController", "Spring MVC — /api/users", "Returns current user (/me). Creates and deletes users (requires ADMIN_USER permission).")
        Component(adminCtrl, "AdminController", "Spring MVC — /api/admin", "Triggers asynchronous Excel mass import (requires ADMIN permission).")
        Component(groupCtrl, "GroupController", "Spring MVC — /api/groups", "Lists and manages permission groups.")
        Component(tagCtrl, "TagController", "Spring MVC — /api/tags", "Lists tags for typeahead.")

        Component(docSvc, "DocumentService", "Spring Service", "Core business logic: store, update, search documents. Resolves persons and tags. Delegates file I/O to FileService. Builds JPA Specifications for dynamic search queries.")
        Component(fileSvc, "FileService", "Spring Service", "Wraps AWS SDK v2 S3Client. Uploads files with UUID-keyed paths. Downloads with content-type detection (PDF, JPEG, PNG, octet-stream).")
        Component(excelSvc, "ExcelService", "Spring Service", "Parses Excel workbooks (Apache POI). Column indices are configurable via application.properties. Creates/updates document records per row.")
        Component(massImport, "MassImportService", "Spring Service — @Async", "Reads Excel files from /import mount. Delegates to ExcelService. Runs asynchronously so the HTTP response returns immediately.")
        Component(userSvc, "UserService", "Spring Service", "User CRUD. Encodes passwords with BCrypt. Assigns users to permission groups.")
        Component(dataInit, "DataInitializer", "CommandLineRunner", "On startup: creates default admin user and groups if none exist. Seeds test data (persons, documents) if DB is empty.")

        Component(docRepo, "DocumentRepository", "Spring Data JPA", "Queries documents. Supports Specification-based dynamic search, conversation thread queries (bidirectional sender/receiver), and filename lookups.")
        Component(docSpec, "DocumentSpecifications", "JPA Criteria API", "Factory for composable query predicates: hasText (full-text across title/filename/transcription/location), hasSender, hasReceiver (join), isBetween (date range), hasTags (subquery AND logic).")
        Component(personRepo, "PersonRepository", "Spring Data JPA", "Lists all persons sorted by last name. Supports name search for typeahead.")
        Component(userRepo, "AppUserRepository", "Spring Data JPA", "Finds users by username. Used by Spring Security and UserService.")
        Component(tagRepo, "TagRepository", "Spring Data JPA", "Finds or creates tags by name (case-insensitive).")
        Component(groupRepo, "UserGroupRepository", "Spring Data JPA", "Manages permission groups.")

        Component(minioConf, "MinioConfig", "Spring @Configuration", "Creates the S3Client bean with path-style access for MinIO. Validates MinIO connectivity on startup.")
        Component(secConf, "SecurityConfig", "Spring @Configuration", "Configures filter chain: all routes require authentication, CSRF disabled, BCrypt password encoder, DaoAuthenticationProvider with CustomUserDetailsService.")
        Component(userDetails, "CustomUserDetailsService", "Spring Security UserDetailsService", "Loads AppUser by username from DB. Converts group permissions to Spring GrantedAuthority objects.")
    }

    Rel(frontend, secFilter, "All requests", "HTTP / Basic Auth header")
    Rel(secFilter, permAspect, "Authenticated requests proceed", "")

    Rel(secFilter, docCtrl, "Routes to", "")
    Rel(secFilter, personCtrl, "Routes to", "")
    Rel(secFilter, userCtrl, "Routes to", "")
    Rel(secFilter, adminCtrl, "Routes to", "")

    Rel(permAspect, docCtrl, "Guards", "AOP @Around")
    Rel(permAspect, userCtrl, "Guards", "AOP @Around")
    Rel(permAspect, adminCtrl, "Guards", "AOP @Around")

    Rel(docCtrl, docSvc, "Delegates to", "")
    Rel(adminCtrl, massImport, "Triggers", "")
    Rel(userCtrl, userSvc, "Delegates to", "")

    Rel(docSvc, fileSvc, "Upload / download files", "")
    Rel(docSvc, docRepo, "Reads / writes documents", "")
    Rel(docSvc, docSpec, "Builds search predicates", "")
    Rel(docSvc, personRepo, "Resolves sender / receivers", "")
    Rel(docSvc, tagRepo, "Finds or creates tags", "")

    Rel(massImport, excelSvc, "Parses Excel file", "")
    Rel(excelSvc, docSvc, "Creates / updates documents", "")

    Rel(userSvc, userRepo, "Reads / writes users", "")
    Rel(userSvc, groupRepo, "Assigns groups", "")
    Rel(userDetails, userRepo, "Loads user by username", "")

    Rel(fileSvc, minio, "PUT / GET objects", "S3 API / HTTP")
    Rel(docRepo, db, "SQL queries", "JDBC")
    Rel(personRepo, db, "SQL queries", "JDBC")
    Rel(userRepo, db, "SQL queries", "JDBC")
    Rel(tagRepo, db, "SQL queries", "JDBC")
    Rel(groupRepo, db, "SQL queries", "JDBC")
    Rel(dataInit, db, "Seeds initial data", "JDBC")
    Rel(secConf, userDetails, "Wires", "")
    Rel(minioConf, fileSvc, "Provides S3Client bean", "")
```

---

## Level 3 — Components: Web Frontend

The internal structure of the SvelteKit frontend.

```mermaid
C4Component
    title Component Diagram: Web Frontend

    Person(user, "User")
    Container(backend, "API Backend", "Spring Boot")

    System_Boundary(frontend, "Web Frontend (SvelteKit / SSR)") {

        Component(hooks, "hooks.server.ts", "SvelteKit Server Hook", "Two responsibilities: (1) userGroup handle — reads auth_token cookie, fetches /api/users/me, stores user in event.locals. (2) handleFetch — intercepts all outgoing fetch() calls, injects Authorization header from cookie. Redirects to /login if token absent.")
        Component(i18n, "hooks.ts (Paraglide)", "SvelteKit Client Hook", "Client-side i18n middleware. Detects language from URL and sets the active locale for Paraglide.js translation functions.")

        Component(layout, "+layout.server.ts", "SvelteKit Layout Loader", "Passes event.locals.user down to all child pages so every route has access to the authenticated user.")

        Component(homePage, "/ (Home / Search)", "SvelteKit Route", "Loader: parses URL search params (q, from, to, senderId, receiverId, tags), fetches /api/documents/search and /api/persons, returns results. Page: renders search form with full-text, date range, sender/receiver typeahead, tag filters. Displays paginated document list.")
        Component(docDetail, "/documents/[id]", "SvelteKit Route", "Loader: fetches /api/documents/{id}. Handles 401 redirect to login, 404 error. Page: shows document metadata, file viewer (PDF/image inline), transcription, tags.")
        Component(docEdit, "/documents/[id]/edit", "SvelteKit Route", "Form with PersonTypeahead for sender/receiver, TagInput for tags, date/location fields. Submits PUT to /api/documents/{id}.")
        Component(persons, "/persons and /persons/[id]", "SvelteKit Routes", "Lists all persons. Detail page shows person metadata and all documents they sent.")
        Component(conversations, "/conversations", "SvelteKit Route", "Selects two persons via PersonTypeahead, fetches /api/documents/conversation, displays chronological exchange.")
        Component(loginPage, "/login", "SvelteKit Route", "Form action: encodes username:password as Base64 Basic Auth token, POSTs to /api/users/me to validate, sets auth_token httpOnly cookie (SameSite=strict, maxAge=86400), redirects to /.")
        Component(logoutPage, "/logout", "SvelteKit Route (server-only)", "Clears the auth_token cookie and redirects to /login.")
        Component(adminPage, "/admin", "SvelteKit Route", "User management UI (create/delete users). Excel import trigger button (calls /api/admin/trigger-import).")

        Component(apiPersons, "/api/persons (SvelteKit API)", "SvelteKit Server Route", "Proxies GET /api/persons?q=... to backend. Used by PersonTypeahead for typeahead suggestions.")
        Component(apiTags, "/api/tags (SvelteKit API)", "SvelteKit Server Route", "Proxies GET /api/tags to backend. Used by TagInput for autocomplete.")

        Component(typeahead, "PersonTypeahead.svelte", "Svelte Component", "Async autocomplete for selecting a person. Debounces input, calls /api/persons?q=.")
        Component(tagInput, "TagInput.svelte", "Svelte Component", "Multi-tag input. Supports free-text entry and selecting existing tags from /api/tags.")
    }

    Rel(user, hooks, "Every browser request", "HTTPS")
    Rel(hooks, backend, "GET /api/users/me (session check)", "HTTP / Basic Auth")
    Rel(hooks, loginPage, "Redirect if no token", "")

    Rel(layout, homePage, "Provides user context", "")
    Rel(layout, docDetail, "Provides user context", "")
    Rel(layout, adminPage, "Provides user context", "")

    Rel(homePage, backend, "GET /api/documents/search", "HTTP / JSON")
    Rel(homePage, backend, "GET /api/persons", "HTTP / JSON")
    Rel(docDetail, backend, "GET /api/documents/{id}", "HTTP / JSON")
    Rel(docDetail, backend, "GET /api/documents/{id}/file", "HTTP / Binary stream")
    Rel(docEdit, backend, "PUT /api/documents/{id}", "HTTP / Multipart")
    Rel(conversations, backend, "GET /api/documents/conversation", "HTTP / JSON")
    Rel(loginPage, backend, "POST /api/users/me (auth check)", "HTTP / Basic Auth")
    Rel(adminPage, backend, "GET/POST/DELETE /api/users", "HTTP / JSON")
    Rel(adminPage, backend, "POST /api/admin/trigger-import", "HTTP / JSON")

    Rel(apiPersons, backend, "GET /api/persons", "HTTP / JSON")
    Rel(apiTags, backend, "GET /api/tags", "HTTP / JSON")

    Rel(homePage, typeahead, "Uses for sender/receiver filter", "")
    Rel(docEdit, typeahead, "Uses for sender/receiver selection", "")
    Rel(docEdit, tagInput, "Uses for tag management", "")
    Rel(typeahead, apiPersons, "Fetches suggestions", "HTTP")
    Rel(tagInput, apiTags, "Fetches existing tags", "HTTP")
```

---

## Authentication Flow

How a user session is established and maintained across requests.

```mermaid
sequenceDiagram
    actor User
    participant Browser
    participant Frontend as Frontend (SvelteKit)
    participant Backend as Backend (Spring Boot)
    participant DB as PostgreSQL

    User->>Browser: Enter username + password
    Browser->>Frontend: POST /login (form action)
    Frontend->>Frontend: Base64 encode "user:password"
    Frontend->>Backend: GET /api/users/me<br/>Authorization: Basic <token>
    Backend->>Backend: Spring Security parses Basic Auth
    Backend->>DB: SELECT user WHERE username=?
    DB-->>Backend: AppUser + groups + permissions
    Backend->>Backend: BCrypt.matches(password, hash)
    Backend-->>Frontend: 200 OK — UserDTO
    Frontend->>Browser: Set-Cookie: auth_token=<base64><br/>(httpOnly, SameSite=strict, maxAge=86400)
    Browser->>Frontend: GET / (next request)
    Frontend->>Frontend: hooks.server.ts reads auth_token cookie
    Frontend->>Backend: GET /api/users/me<br/>Authorization: Basic <token>
    Backend-->>Frontend: 200 OK — user in event.locals
    Frontend-->>Browser: Render page with user context
```

---

## Document Upload Flow

How a document file moves from a user's browser to MinIO.

```mermaid
sequenceDiagram
    actor User
    participant Frontend as Frontend (SvelteKit)
    participant Backend as Backend (Spring Boot)
    participant Aspect as PermissionAspect (AOP)
    participant DocSvc as DocumentService
    participant FileSvc as FileService
    participant MinIO
    participant DB as PostgreSQL

    User->>Frontend: Submit edit form (file + metadata)
    Frontend->>Backend: PUT /api/documents/{id}<br/>multipart/form-data + Authorization header
    Backend->>Aspect: @RequirePermission(WRITE_ALL) check
    Aspect->>Aspect: Verify user has WRITE_ALL authority
    Aspect-->>Backend: Proceed
    Backend->>DocSvc: updateDocument(id, dto, file)
    DocSvc->>DocSvc: Resolve sender Person by ID
    DocSvc->>DocSvc: Resolve/create Tags
    DocSvc->>FileSvc: uploadFile(file, filename)
    FileSvc->>FileSvc: Generate key: documents/{UUID}_{filename}
    FileSvc->>MinIO: PutObject(bucket, key, stream)
    MinIO-->>FileSvc: Success
    FileSvc-->>DocSvc: S3 key
    DocSvc->>DB: UPDATE documents SET file_path=?, status='UPLOADED', ...
    DB-->>DocSvc: OK
    DocSvc-->>Backend: Updated Document entity
    Backend-->>Frontend: 200 OK — Document JSON
    Frontend-->>User: Refreshed document view
```
