Gå till innehållet

Arkitektur och teknisk plattform

Systemarkitektur

Översiktsbild över hur systemets komponenter kommunicerar och var de driftas.

flowchart TD
    Users([Användare]) -->|Webbläsare| SWA

    subgraph Azure["Azure — swedencentral"]
        direction TB

        SWA["Azure Static Web App<br/>salieri.se<br/>Blazor WebAssembly SPA"]
        DocsUG["Azure Static Web App<br/>anvandarhandledning.salieri.se<br/>Användarhandledning"]
        DocsTR["Azure Static Web App<br/>tekniskdokumentation.salieri.se<br/>Teknisk dokumentation"]

        SWA -->|HTTPS / REST / JSON| API

        API["Azure Container App<br/>api.salieri.se<br/>ASP.NET Core Web API"]
        API -.->|WebSocket<br/>BookingChanged| SWA

        API -->|TCP 1433| SQL

        API -->|REST| ACS

        ACS["Azure Communication Services<br/>E-post<br/>&lpar;no-reply@smtp.salieri.se&rpar;"]

        SQL[("Azure SQL Serverless<br/>SalieriDb<br/>&lpar;GP_S_Gen5, 8 h auto-paus&rpar;")]
    end

Driftsmiljöer

Resurs Produktion Lokal utveckling
Klient Azure Static Web App (salieri.se) dotnet run (port 5299)
API Azure Container App (api.salieri.se) dotnet run (port 5183)
Databas Azure SQL Serverless Docker — SQL Server 2022 (port 1433)
Realtid SignalR (in-process) SignalR (in-process)
CI/CD GitHub Actions —

Teknisk plattform

Internt bygge av backend- och frontend-lagren.

Backend — ASP.NET Core Web API

flowchart TD
    Client([Blazor WASM-klient]) -->|"HTTPS / JSON"| Pipeline

    subgraph Pipeline["ASP.NET Core-pipeline"]
        direction LR
        Cors[CORS] --> Auth[Authentication<br/>JWT Bearer] --> AuthZ[Authorization] --> Routing[Routing]
    end

    Routing --> Controllers

    subgraph Controllers["Kontroller"]
        direction TB
        AccountCtrl["Account<br/>Auth + användarhantering"]
        BookingsCtrl["Bookings<br/>Boknings-CRUD"]
        ResourceCtrl["Buildings · Floors<br/>Rooms · Locations<br/>Lookups · UserRoles"]
        DataCtrl["Data<br/>Import / Export"]
    end

    Controllers --> DbContext

    subgraph Data["Datalager"]
        direction TB
        DbContext["ApplicationDbContext<br/>&lpar;IdentityDbContext<br/>&lt;ApplicationUser&gt;&rpar;"]
        DbContext --> EF["EF Core ORM"]
        EF --> Entities["Entiteter<br/>Location → Building<br/> → Floor → Room<br/>Booking<br/>UserRole<br/>Lookup-entiteter"]
        EF --> Migrations["Migreringar"]
    end

    Controllers --> Services

    subgraph Services["Tjänster"]
        direction TB
        TokenService["TokenService<br/>JWT-utstedning<br/>&lpar;HMAC-SHA256, 8 h&rpar;"]
        Startup["DbInitializer → AdminSeeder<br/>Migreringar + bootstrap-admin"]
        Email["IEmailSender<br/>AzureEmail / Smtp / File / Noop"]
    end

    Controllers --> Hub

    subgraph SignalR["Realtid"]
        Hub["NotificationHub<br/>/hubs/notifications<br/>BookingChanged vid boknings-CRUD"]
    end

Frontend — Blazor WebAssembly

flowchart TD
    Browser([Webbläsare]) --> App

    subgraph App["Blazor WASM SPA"]
        direction TB

        subgraph Pages["Sidor"]
            direction TB
            Home["Home — Kalendervy<br/>Dag · Vecka<br/>Månad · Lista &lpar;stub&rpar;"]
            Admin["Admin — Administrationsvyer<br/>Locations · Buildings<br/>Floors · Rooms<br/>Users · Lookups"]
        end

        subgraph Components["Komponenter"]
            direction TB
            CalViews["DayView · WeekView<br/>MonthView · ListView<br/>BookingBlock · DetailsPane"]
            Dialogs["BookingForm · LoginDialog<br/>ChangePasswordDialog<br/>ConfirmDialog<br/>ImportExportDialog<br/>RecurringChoiceDialog"]
            Nav["NavigationHeader<br/>CommandBar<br/>ToastContainer"]
        end

        subgraph ClientServices["Tjänster"]
            direction TB
            ApiClient["ApiClient<br/>Typad HTTP-klient"]
            AppState["AppState<br/>Central state store"]
            AuthService["AuthService<br/>JWT + localStorage"]
            SignalRClient["SignalRService<br/>Prenumeration på BookingChanged"]
            UiSupport["ThemeService<br/>NotificationService<br/>JsInteropService<br/>DateUtils"]
        end

        Pages --> Components
        Components --> ClientServices
    end

    ClientServices -->|"HTTP / REST / JSON"| API([ASP.NET Core API])
    ClientServices -->|"WebSocket (SignalR)"| Hub([SignalR Hub])

Autentiseringsflöde

sequenceDiagram
    actor User as Användare
    participant Client as Blazor WASM
    participant API as ASP.NET Core API
    participant DB as Azure SQL

    User->>Client: Logga in (e-post + lösenord)
    Client->>API: POST /api/account/login
    API->>DB: UserManager — lösenordsvalidering
    DB-->>API: OK
    API->>API: Generera JWT (HMAC-SHA256, 8 h, role-claims)
    API-->>Client: { token, userProfile }
    Client->>Client: Spara i localStorage (nyckel salieri-auth)

    alt Nytt konto
        User->>Client: Skapa konto (e-post + minst en användarroll)
        Client->>API: POST /api/account/register
        API-->>Client: 201 (väntar på godkännande)
        User->>Client: Hanterare godkänner kontot
        Client->>API: POST /api/account/users/{id}/approve
        API-->>Client: OK
    end

    loop Varje API-anrop
        Client->>API: Authorization: Bearer {token}
        API->>API: Validera JWT + claims (ev. policy ManageUsers/ManageData/ManageSystem)
    end

    User->>Client: Skapa bokning
    Client->>API: POST /api/bookings
    API->>API: [Authorize] — giltig JWT krävs
    API->>DB: EF Core — spara bokning
    API-->>Client: 201 Created

    Note over API,Client: Efter sparad bokning sänder API:t BookingChanged via<br/>SignalR-hubben /hubs/notifications — övriga klienter hämtar då om synligt intervall.

E-post och import/export

  • E-post — IEmailSender med leverantörerna AzureEmail (produktion, Azure Communication Services från no-reply@smtp.salieri.se), Smtp (lokal smtp4dev), File (.eml-filer) och Noop (CI). Används för tillfälliga lösenord samt notiser vid registrering, godkännande och avslag. E-postfel stoppar aldrig kontooperationen.
  • Import/export — DataController (/api/data/export?includeBookings, /api/data/import?clear&includeBookings, policy ManageData). Import valideras mot JSON-schemat (schemas/salieri-export.schema.json, även via GET /api/data/schema) och främmande nycklar, körs i transaktion med UPSERT (IDENTITY_INSERT) i beroendeföljd.
  • Listning — GET /api/bookings stödjer filtrering (from, to, roomId, contact, buildingId) och sidindelning (page, pageSize, svarshuvuden X-Total-Count/X-Page/X-Page-Size).

Teknisk plattform — sammanfattning

Lager Komponent Teknologi
Presentation SPA-klient Blazor WebAssembly (.NET 10)
Presentation Dokumentation Zensical (statiska webbsidor)
API Webbutvecklingsserver ASP.NET Core Web API (.NET 10)
API Autentisering ASP.NET Core Identity + JWT Bearer
API Realtid SignalR (in-process, BookingChanged vid boknings-CRUD)
API E-post Azure Communication Services via IEmailSender
API Import/export JSON-schema + transaktionell UPSERT
Data ORM Entity Framework Core 10
Data Databas SQL Server 2022 (Azure SQL Serverless)
Infra Container Docker (lokal) / Azure Container App (produktion)
Infra Frontend-hosting Azure Static Web Apps
Infra DNS Azure DNS (salieri.se)
Infra CI/CD GitHub Actions