profileShare

rasmusjy / splitapp-frontend-vue

Read-only snapshot

No repository description.

main default branch 96 files Expires Sep 13, 2026, 9:06 AM
YLEVAADE.md 34,063 bytes

Projekti ülevaade — SplitApp (Reisikulude jagaja)

See dokument kirjeldab tervet süsteemi: frontendi (Vue 3) ja backendi (ASP.NET Core 10) ning seda, kuidas need omavahel suhtlevad.


1. Mis on SplitApp?

SplitApp on reisiplaneerimise ja ühiste kulude jagamise rakendus. Kasutajad saavad:

  • luua reise ja kutsuda kaaslasi
  • sisestada ühiseid kulusid ja jagada neid nelja erineva meetodi järgi
  • hallata eelarvekategooriaid ja jälgida kulutusi
  • pidada soovinimekirja tegevustest/kohtadest koos hääletusega
  • teha grupiotsuseid küsitluste kaudu
  • lõpuks arvestada, kes kellele võlgu on (arveldused)

Süsteem koosneb kahest eraldiseisvast projektist, mis suhtlevad REST API kaudu:

Projekt Kaust Tehnoloogia
Frontend rasmju-js-a7 Vue 3 + TypeScript + Vite
Backend rasmju-csweb-phase3 ASP.NET Core 10 + PostgreSQL

2. Frontend — rasmju-js-a7

2.1 Tehnoloogiad

  • Vue 3.5 (Composition API) + TypeScript 6
  • Vite 8 — build tool ja arendusserver
  • Vue Router 5 — lehekülgede ruutimine
  • Pinia 3 — olekuhaldus
  • Axios 1.14 — HTTP klient
  • Bootstrap 5.3 — stiilide raamistik
  • Vitest 4 — unit testid
  • ESLint + Oxlint + Prettier — koodi kvaliteet

2.2 Projekti struktuur

src/
├── components/          # Jagatud komponendid
│   ├── SplitMethodSelector.vue    # Kulu jagamise UI (4 meetodit)
│   └── ToastContainer.vue         # Teavituste kuvamine
├── composables/
│   └── useToast.ts                # Globaalne teavituste süsteem
├── directives/
│   └── vAnimate.ts                # Scroll-animatsiooni direktiiv
├── router/
│   └── index.ts                   # Kõik ruudid + autentimise valvur
├── services/                      # API kliendid
│   ├── httpClient.ts              # Axiose seadistus + interceptorid
│   ├── AccountService.ts          # Login, register, refresh, logout
│   ├── TripService.ts             # Reisid
│   ├── ExpenseService.ts          # Kulud
│   ├── BudgetCategoryService.ts   # Eelarvekategooriad
│   ├── CurrencyService.ts         # Valuutad
│   ├── PollService.ts             # Küsitlused
│   ├── WishlistService.ts         # Soovinimekiri
│   ├── SettlementService.ts       # Arveldused
│   └── InvitationService.ts       # Kutsed
├── stores/
│   └── auth.ts                    # JWT + refreshToken + userName
├── types/                         # TypeScript liidesed (DTO vastavalt API-le)
├── utils/
│   ├── formatCurrency.ts          # Valuuta vormindus
│   └── parseJwt.ts                # JWT dekodeerimine
├── views/                         # Leheküljed
│   ├── HomeView.vue
│   ├── LoginView.vue
│   ├── RegisterView.vue
│   ├── trips/                     # IndexView, DetailView, CreateView, EditView
│   ├── expenses/                  # Index, Create, Edit
│   ├── budget-categories/         # Index, Create, Edit
│   ├── wishlist/                  # Index, Create, Edit
│   ├── polls/                     # Index, Create, Detail
│   ├── invitations/               # AcceptView
│   ├── members/                   # MembersView
│   └── settlements/               # SettlementView
├── App.vue                        # Juurkomponent (navbar + router-view)
└── main.ts                        # Käivituspunkt

2.3 Ruutimine

Kogu rakendus kasutab pesastatud ruuteid — reis on parent-route, mille all asuvad kõik reisispetsiifilised vaated. trips/DetailView.vue pakub child-route'idele provide() kaudu konteksti (näiteks isOrganizer ja tripCurrencySymbol).

Avalikud ruudid: /, /login, /register Autentimist nõudvad ruudid: kõik ülejäänud (valvur router/index.ts suunab külalised /login peale)

Põhilised ruudid:

  • /trips — reiside loend
  • /trips/create — uus reis
  • /trips/:tripId — reisi detailvaade (parent)
    • expenses, expenses/create, expenses/:id/edit
    • budget, budget/create, budget/:id/edit
    • wishlist, wishlist/create, wishlist/:id/edit
    • polls, polls/create, polls/:id
    • members — reisikaaslased
    • settlement — arveldused
    • edit — reisi muutmine
  • /invitations/:token — kutse vastuvõtmine

2.4 Autentimine

Salvestus: JWT ja refresh token hoitakse localStorage-s (jwt, refreshToken, userName). Pinia store auth.ts sünkroniseerib need automaatselt watch-i kaudu.

Voog:

  1. Login/Register → AccountService saadab POST päringu → saab tagasi { jwt, refreshToken, firstName, lastName }
  2. httpClient-i request interceptor lisab iga päringule päise Authorization: Bearer <jwt>
  3. Response interceptor püüab 401 vastused kinni:
    • Kutsub refreshTokenAsync() → uuendab tokenid store'is → kordab algset päringut
    • Kui refresh ebaõnnestub → logib kasutaja välja ja suunab /login-ile
  4. Logout → tühistab refresh tokeni backendis + puhastab store'i

2.5 Keskkonnamuutujad

Fail .env (gitignore'is):

VITE_API_BASE_URL=http://localhost:90/api/v1/

Kasutatakse httpClient.ts ja AccountService.ts failides. Kui backend käib lokaalselt dotnet run kaudu, tuleb see vahetada http://localhost:5086/api/v1/ vastu.

2.6 Kulude jagamise loogika

SplitMethodSelector.vue toetab nelja meetodit — need vastavad täpselt backendi ESplitMethod enumile:

Meetod Kirjeldus
EqualAll Kogu summa jagatakse võrdselt kõigi osalejate vahel
EqualSubset Kasutaja valib alamhulga osalejatest, jagatakse võrdselt
ExactAmounts Iga osaleja kohta sisestatakse täpne summa (peab võrduma kogusummaga)
Percentages Iga osaleja kohta protsent (peavad kokku andma 100%)

Komponent valideerib sisendit jooksvalt ja emiteerib update:splits + update:valid.

2.7 Skriptid

npm install               # Installi sõltuvused
npm run dev               # Arendusserver http://localhost:5173
npm run build             # Type-check + production build
npm run preview           # Eelvaade ehitatud rakendusest
npm run test:unit         # Vitest testid
npm run lint              # Oxlint + ESLint
npm run format            # Prettier

Märkus: src/__tests__/App.spec.ts on aegunud (otsib teksti "You did it!", mida App.vue ei sisalda). npm run test:unit kukub seetõttu praegu läbi. Reaalset funktsionaalsust see ei mõjuta.


3. Backend — rasmju-csweb-phase3

3.1 Tehnoloogiad

  • .NET 10 (net10.0)
  • ASP.NET Core 10 Web API + Identity + MVC (Razor Views)
  • Entity Framework Core 10 — ORM
  • PostgreSQL 16 (Npgsql) — andmebaas, kolm schema-isoleeritud schema-t ühes andmebaasis
  • MediatR — moodulitevaheline suhtlus (in-process queries + notifications)
  • JWT Bearer autentimine + refresh token
  • Swashbuckle — Swagger/OpenAPI UI
  • Asp.Versioning — API versioonimine (/api/v{version}/...)

Backend on modulaarmonoliit — üks deployitav rakendus, mille sees on kolm sisemiselt isoleeritud moodulit (Users, Trips, Expenses). Moodulid ei tee otseseid ristviiteid Application/Infrastructure/Api projektide tasemel — kõik moodulitevahelised väljakutsed käivad MediatR-i kaudu, igal moodulil on oma Postgres-i schema ja oma DbContext.

3.2 Lahuse struktuur

SplitApp.Modular/ lahus sisaldab kolme moodulit (kummalgi 4 projekti), kahte jagatud projekti ja ühte hosti:

SplitApp.Modular/
├── SplitApp.sln
├── Directory.Build.props
├── src/
│   ├── SplitApp.WebApp/                                 # Composition root + host
│   │   ├── Program.cs                                   # AddXxxModule(...) wiring
│   │   ├── Application/                                 # Phase-2-st tõstetud BLL
│   │   │   ├── Services/  (+ Admin/, Identity/)        #   12 admin + 9 klient + 1 identity teenust
│   │   │   ├── DTO/                                     #   BllDto'd vaadetele
│   │   │   ├── Mappers/                                 #   Domain ↔ BllDto factory mapperid
│   │   │   ├── Persistence/AppUnitOfWork.cs             #   Aggregib 3 mooduli DbContextid
│   │   │   └── Persistence/CrossModuleNavigationLoader.cs
│   │   ├── Areas/Admin/                                 # MVC admin haldusliides (13 kontrollerit)
│   │   ├── Areas/Identity/                              # Razor Identity UI (Register jne)
│   │   ├── Controllers/, Views/                         # Klient-MVC (parity phase 2-ga)
│   │   └── Resources/                                   # i18n resx (en, et)
│   ├── Shared/
│   │   ├── SplitApp.Shared.Kernel/                      # BaseEntity, IBaseRepo, IUoW, LangStr
│   │   └── SplitApp.Shared.Contracts/                   # MediatR IRequest / INotification
│   └── Modules/
│       ├── Users/                                       # 4 projekti, schema "users"
│       │   ├── ...Domain/                               #   AppUser, AppRole, AppRefreshToken
│       │   ├── ...Application/                          #   IIdentityService, JWT/refresh, MediatR handlerid
│       │   ├── ...Infrastructure/                       #   UsersDbContext, repod, migrations
│       │   └── ...Api/                                  #   /api/v1/identity/...
│       ├── Trips/                                       # 4 projekti, schema "trips"
│       └── Expenses/                                    # 4 projekti, schema "expenses"
└── tests/
    ├── SplitApp.Modules.{Users,Trips,Expenses}.Tests/   # Per-module unit testid
    └── SplitApp.WebApp.IntegrationTests/                # Architecture invariant + smoke (25 testi kokku)

Iga moodul = mini-Clean-Architecture (DomainApplicationInfrastructure, Api REST jaoks). Application/Infrastructure/Api projektidel ei ole <ProjectReference>-i teiste moodulite samanimelistele projektidele — seda kontrollivad tests/SplitApp.WebApp.IntegrationTests/Architecture/ arhitektuuritestid (kukkumine = ehitus kukub).

3.3 Domeenimudel

Põhientiteedid ja nende seosed:

Entiteet Olulised väljad Seosed
Trip Name, Description, Destination, StartDate, EndDate, Status, DefaultCurrencyId, CreatedById 1→many: Participants, Expenses, BudgetCategories, WishlistItems, Polls, Invitations, SettlementPlans
TripParticipant TripId, UserId, Role (Organizer/Participant), Nickname, IsActive ↔ Trip, AppUser
Expense TripId, PaidByUserId, Amount, Description, ExpenseDate, SplitMethod, BudgetCategoryId, CurrencyId 1→many: ExpenseSplits
ExpenseSplit ExpenseId, UserId, Amount, Percentage ↔ Expense, AppUser
BudgetCategory TripId, Name (LangStr), IconName, PlannedAmount 1→many: Expenses
TripInvitation TripId, Token (unikaalne), Status, ExpiresAt ↔ Trip
TripPoll TripId, Question, AllowMultipleVotes, IsAnonymous, ClosedAt 1→many: Options → Votes
TripWishlistItem TripId, Title, Category, Priority, EstimatedCost, IsCompleted 1→many: Votes
SettlementPlan TripId, TotalAmount, Status 1→many: Payments
SettlementPayment FromUserId, ToUserId, Amount, Status (Pending/MarkedPaid/Confirmed) ↔ SettlementPlan
Currency Code (3 tähte), Name (LangStr), Symbol

Identity entiteedid (Users moodul, schema users):

  • AppUser (laiendab IdentityUser<Guid>) — lisab FirstName, LastName
  • AppRole (laiendab IdentityRole<Guid>) — rollid: user, admin
  • AppRefreshToken — refresh token + eelmine token + aegumised

Baasinfrastruktuur (SplitApp.Shared.Kernel):

  • BaseEntity — abstraktne: Id (Guid), CreatedAt, UpdatedAt
  • LangStr — mitmekeelne string (Dictionary<string, string>), salvestatakse andmebaasis JSON-ina

Enumid:

  • ETripStatus: Active, Finalizing, Settled, Archived (Finalizing = arvelduskava lukus, oodatakse kinnitusi)
  • EParticipantRole: Organizer, Participant
  • ESplitMethod: EqualAll, EqualSubset, ExactAmounts, Percentages (vastab frontendi omale)
  • EInvitationStatus: Pending, Accepted, Declined, Expired, Revoked
  • ESettlementStatus: Pending, InProgress, Completed
  • EPaymentStatus: Pending, MarkedPaid, Confirmed

Schemade jaotus:

Schema Moodul Entiteedid
users Users AppUser, AppRole + ASP.NET Identity tabelid, AppRefreshToken
trips Trips Trip, TripParticipant, TripInvitation, TripPoll(+Option/Vote), TripWishlistItem(+Vote), BudgetCategory
expenses Expenses Currency, Expense, ExpenseSplit, SettlementPlan, SettlementPayment, SplitPreset(+Member)

Cross-module entiteedi-viited (näit Trip.CreatedById, Expense.PaidByUserId, BudgetCategory.TripId) on lihtsalt Guid väljad — ei mingit EF foreign key piirangut üle schema-de, sest EF-l ei tohi lasta kogemata schema piiri ületada. Navigeerimispropertid (näit Trip.DefaultCurrency, Trip.CreatedBy, Expense.PaidByUser) on [NotMapped] ja täidetakse käsitsi WebApp/Application/Persistence/CrossModuleNavigationLoader-i kaudu (eraldi batch-päringud teise mooduli DbContext-i vastu).

Referentsiaalne terviklikkus tagatakse kahel viisil:

  1. Eelnev validatsioon MediatR-päringutega — näit enne kulu salvestamist saadab ExpensesController IsTripParticipantQuery(tripId, userId) Trips moodulile
  2. Domain-eventide koristus kustutamiselUserDeletedEvent / TripDeletedEvent ja teised moodulid tellivad need ning kustutavad sõltuvad read

3.4 Andmebaas

Kolm DbContext-i, üks andmebaas: kõik kolm moodulit ühenduvad samasse Postgres-i andmebaasi, kuid igaüks oma schema kaudu. Kogu konfiguratsioon on per-moodul mooduli enda Infrastructure/Persistence/-s.

DbContext Schema Pärib Asukoht
UsersDbContext users IdentityDbContext<AppUser, AppRole, Guid> (rakendab ka IDataProtectionKeyContext) Modules/Users/...Infrastructure/Persistence/
TripsDbContext trips DbContext Modules/Trips/...Infrastructure/Persistence/
ExpensesDbContext expenses DbContext Modules/Expenses/...Infrastructure/Persistence/

Cross-schema SQL JOIN-id on keelatud — kompositsioon toimub rakenduskihis MediatR-i või CrossModuleNavigationLoader-i kaudu. Et Trips.Domain.Trip.DefaultCurrency (Currency on Expenses moodulis) tüüpi ikkagi näha saaks, on Trips.Domain ja Expenses.Domain projektidel kolm Domain-to-Domain <ProjectReference>-i — aga kõik cross-module navigeerimispropertid on [NotMapped], nii et EF ei lähe kunagi üle schema-piiri.

Olulised piirangud (OnModelCreating, igas DbContext-is):

  • Kõik seosed mooduli sees: DeleteBehavior.Restrict (kustutamisel ei kustutata kaskaadis — välja arvatud cleanup eventidel)
  • Unikaalne indeks: TripInvitation.Token
  • Liit-unikaalne indeks: (TripParticipant.TripId, UserId), (TripWishlistVote.WishlistItemId, UserId), (TripPollVote.PollOptionId, UserId)
  • DateTime väljad: UTC konverter (alati salvestatakse UTC-s) — UtcDateTimeConverter igas mooduli Persistence/ kaustas
  • LangStr väljad (Currency.Name, BudgetCategory.Name): JSON-serialiseeritud

Migratsioonid: 1 migratsioon mooduli kohta (Init), kokku 3:

  • Modules/Users/...Infrastructure/Persistence/Migrations/
  • Modules/Trips/...Infrastructure/Persistence/Migrations/
  • Modules/Expenses/...Infrastructure/Persistence/Migrations/

Migratsioonid rakendatakse host'i käivitamisel automaatselt — iga mooduli UseXxxModule() extension call (Program.cs) teeb db.Database.Migrate().

Andmete lähtestamine (seadistatakse appsettings.json-is):

"DataInitialization": {
  "DropDatabase": false,      // Kustuta andmebaas käivitamisel
  "MigrateDatabase": true,    // Rakenda migratsioonid
  "SeedIdentity": true,       // Loo kasutajad ja rollid (Users moodul)
  "SeedData": true            // Loo näidisandmed (host-tasandil, peale module init'e)
}

Seemneandmed:

  • 5 demokasutajat (parool kõigil: Kala.12345) — seedib UsersModuleExtensions.UseUsersModule():
    • user@taltech.ee, alice@taltech.ee, bob@taltech.ee, charlie@taltech.ee, diana@taltech.ee
    • admin luuakse ainult siis, kui SEED_ADMIN_PASSWORD on seatud
  • 5 valuutat: EUR, USD, GBP, SEK, NOK — seedib Expenses moodul
  • 4 näidisreisi: Barcelona Weekend (Active), London Business Trip (Settled), Summer Cabin Getaway (Active), NYC Adventure (Archived) — host-tasandi cross-module seed (SplitApp.WebApp/Hosting/AppDataInit.cs), kuna sisaldab andmeid kõigist kolmest moodulist

3.5 API lõpp-punktid

Kõik kontrollerid on versioonitud: /api/v{version:apiVersion}/ (vaikimisi v1.0). Kontrollerid elavad iga mooduli oma Api/ projektis — host (WebApp) avastab need läbi AddApplicationPart(...) (Program.cs).

Identity / Account (Users moodul) — /api/v1/identity/account

Verb Lõpp-punkt Otstarve Auth
POST /register Kasutaja registreerimine
POST /login Login → JWT + refresh token
POST /refreshtokendata Tokenite uuendamine
POST /logout Refresh tokeni tühistamine JWT

Trips (Trips moodul) — /api/v1/trips

Verb Lõpp-punkt Otstarve
GET / Kasutaja reiside loend
POST / Uus reis
GET /{id} Reisi detail
PUT /{id} Muuda reisi
DELETE /{id} Kustuta reis
GET /{tripId}/participants Osalejate loend
DELETE /{tripId}/participants/{userId} Eemalda osaleja

Expenses (Expenses moodul) — /api/v1/expenses

  • GET /trip/{tripId}, POST /, GET /{id}, PUT /{id}, DELETE /{id}

BudgetCategories (Trips moodul) — /api/v1/budgetcategories

  • GET /trip/{tripId}, POST /, PUT /{id}, DELETE /{id}

Currencies (Expenses moodul) — /api/v1/currencies

  • GET / (avalik, ei vaja JWT-d)

Invitations (Trips moodul) — /api/v1/invitations

  • POST /, GET /{token}, POST /{token}/accept, POST /{token}/decline, POST /{token}/revoke

Polls (Trips moodul) — /api/v1/polls

  • GET /trip/{tripId}, POST /, GET /{id}, POST /{id}/vote, POST /{id}/close, DELETE /{id}

Wishlist (Trips moodul) — /api/v1/wishlist

  • GET /trip/{tripId}, POST /, PUT /{id}, DELETE /{id}, POST /{id}/vote, POST /{id}/complete

Settlements (Expenses moodul) — /api/v1/settlements

  • GET /trip/{tripId}, GET /trip/{tripId}/summary, GET /trip/{tripId}/balances
  • POST /trip/{tripId}/calculate — arvutab ja loob uue arvelduskava
  • POST /payments/{paymentId}/mark-paid — märgib makse tasutuks
  • POST /payments/{paymentId}/confirm — kinnitab saamise

SplitPresets (Expenses moodul) — /api/v1/splitpresets

  • GET /trip/{tripId}, GET /{id}, POST /, PUT /{id}, DELETE /{id}

3.6 Autentimine ja turvalisus

JWT seadistus (appsettings.json):

"JWT": {
  "Issuer": "itcollege.taltech.ee",
  "Audience": "itcollege.taltech.ee",
  "ExpiresInSeconds": 1800     // 30 minutit
}

Voog:

  1. POST /login → tagastab JWT (30 min) + AppRefreshToken (kehtib 7 päeva)
  2. Frontend kasutab JWT-d iga päringul
  3. Enne aegumist (või 401 korral) → POST /refreshtokendata → uus JWT + uus refresh token (vana märgitakse eelmiseks, kehtib veel 1 minut ühildumiseks)
  4. POST /logout → kustutab refresh tokeni andmebaasist

Autoriseerimine kontrollerites (IDOR-kaitse):

  • Enamik lõpp-punkte: [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
  • Kasutaja-id loetakse JWT nameidentifier claimist iga päringu alguses
  • Cross-module osaleja-kontroll käib MediatR-i kaudu — näit ExpensesController saadab IsTripParticipantQuery(tripId, userId) Trips moodulile, sest Expenses moodul ei tohi TripsDbContext-i otse näha. Tagastab Forbid() kui false.
  • Reisi-sisene loaja kontroll (organizer-only mutatsioon): if (trip.CreatedById != userId.Value) return Forbid();
  • List endpointide nähtavus filtreeritud serveri pool — kasutaja ei näe kunagi reise/kulusid mille trip-osalemine puudub

3.7 CORS

Program.cs seadistab poliitika CorsAllowAll:

.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()

See tähendab, et frontend võib olla mistahes pordil/domeenil — arenduseks mugav, tootmises tuleks piirata.

3.8 Konfiguratsioon (appsettings.json)

"ConnectionStrings": {
  "DefaultConnection": "Host=localhost;Port=5432;Database=splitapp;Username=postgres;Password=postgres"
},
"SupportedCultures": ["en", "et"],
"DefaultCulture": "en"

Lokaliseerimine: toetab inglise ja eesti keelt. Kultuuri saab muuta query parameetriga ?culture=et või küpsise kaudu.

3.9 Käivitamine

Lokaalselt (dotnet):

cd SplitApp.Modular/src/SplitApp.WebApp
dotnet run

http://localhost:5086 (http) või https://localhost:7040 (https)

Dockeri kaudu (soovitatud):

docker compose up --build

→ backend: http://localhost:90 → Postgres: localhost:5432


4. Kuidas frontend ja backend koos töötavad

4.1 Pordid

Teenus Port Kus
Backend API 90 Docker (host) → 8080 (container)
Backend API (lokaalselt) 5086 (http) / 7040 (https) dotnet run
PostgreSQL 5432 Docker
Frontend (prod, Nginx) 91 Docker
Frontend (dev, Vite) 5173 npm run dev või Docker dev profiil

Oluline: frontend kuulab vaikimisi 8080-le sisemist konteineri porti, kuid Docker Compose avaldab selle hoopis 91-le, et vältida konflikti lokaalse backendiga. Backend on host'il pordil 90.

4.2 API URL

Frontendi .env:

VITE_API_BASE_URL=http://localhost:90/api/v1/

Kui backend käib lokaalselt (dotnet run):

VITE_API_BASE_URL=http://localhost:5086/api/v1/

4.3 Lõpp-punktide vastavustabel

Frontend teenus Frontendi kõne Backendi kontroller
AccountService identity/Account/Login AccountController.Login (Users moodul)
AccountService identity/Account/Register AccountController.Register (Users moodul)
AccountService identity/Account/RefreshTokenData AccountController.RefreshTokenData (Users moodul)
AccountService identity/Account/Logout AccountController.Logout (Users moodul)
TripService Trips/* TripsController
ExpenseService Expenses/* ExpensesController
BudgetCategoryService BudgetCategories/* BudgetCategoriesController
CurrencyService Currencies CurrenciesController
PollService Polls/*, Polls/:id/vote, Polls/:id/close PollsController
WishlistService Wishlist/*, :id/vote, :id/complete WishlistController
SettlementService Settlements/trip/:id/*, payments/:id/mark-paid SettlementsController
InvitationService Invitations/:token/* InvitationsController

ASP.NET Core ruutimine on tõstutundetu, seega erinevused nagu Trips vs trips ei tekita probleemi.

4.4 Andmetüüpide vastavus

Frontendi TypeScript liidesed (src/types/) on loodud backendi DTO-de põhjal ning struktuurid vastavad 1:1. Näiteks:

Frontend IExpenseBackend ExpenseDto

id, tripId, paidByUserId, budgetCategoryId, currencyId,
amount, description, expenseDate, splitMethod, splits[]

Frontend IJwtResponseBackend JWTResponse

jwt, refreshToken, firstName, lastName

Kulude jagamise enum ESplitMethod on mõlemas pooles identne.

4.5 Autentimise koostöö

  1. Frontend saadab POST /api/v1/identity/account/login
  2. Backend valideerib ja tagastab { jwt, refreshToken, firstName, lastName }
  3. Frontend salvestab need localStorage-sse ja Pinia store'i
  4. Iga järgmine päring → httpClient lisab Authorization: Bearer <jwt>
  5. Kui backend tagastab 401 → frontend proovib automaatselt refresh'i
  6. Backend valideerib refresh tokeni, loob uue JWT + uue refresh tokeni ja tagastab
  7. Frontend uuendab store'i ja kordab algset päringut

5. Käivitamise juhend

5.1 Täielik Docker-käivitus (soovitatud)

1. Käivita backend + andmebaas:

cd C:\Users\rasmu\Documents\csharpweb\rasmju-csweb-phase3
docker compose up --build

See käivitab:

  • PostgreSQL pordil 5432
  • Backend API pordil 90
  • Rakendab migratsioonid ja laeb seemneandmed

2. Kontrolli, et backend vastab: Avaga brauseris http://localhost:90/swagger — peaks kuvama Swagger UI.

3. Käivita frontend:

Arendusrežiimis (soovitatud koodi muutmiseks):

cd C:\Users\rasmu\Documents\javascript\rasmju-js-a7
npm install
npm run dev

http://localhost:5173

Või Dockeri kaudu production build:

docker compose up --build

http://localhost:91

5.2 Kiire test

  1. Ava http://localhost:5173 (või :91)
  2. Logi sisse seemneandmete kasutajaga:
    • Email: alice@taltech.ee
    • Parool: Kala.12345
  3. Peaksid nägema reiside loendit (Barcelona Weekend, Summer Cabin Getaway, ...)
  4. Ava üks reis → vaata kulusid, arveldusi, küsitlusi

5.3 Lokaalne arendus (ilma Dockerita)

Backend:

cd rasmju-csweb-phase3/SplitApp.Modular/src/SplitApp.WebApp
dotnet run

http://localhost:5086

Frontend (muuda .env):

VITE_API_BASE_URL=http://localhost:5086/api/v1/
cd rasmju-js-a7
npm run dev

6. CI/CD pipeline

Fail .gitlab-ci.yml defineerib ühe etapi: deploy. Testid jooksevad lokaalselt enne push'i (vt jaotis 7) — pipeline'i sisse on test-etapp teadlikult lisamata, sest varasem versioon kukus runneril ja blokeeris deploy'd.

6.1 Deploy-etapp

Käivitub ainult main harusse merge'imisel. Kasutab VPS-il olevat self-hosted runner'it (shared tag):

docker compose -p rasmju-js-a7 up --build --remove-orphans --detach
  • Ehitab uue multi-stage Docker image'i (Node build → Nginx)
  • Käivitab uue konteineri pordil 91
  • Projektinimi rasmju-js-a7 hoiab konteinerid backendi omadest eraldi
  • --remove-orphans koristab vanad konteinerid

6.2 Lokaalne kontrollnimekiri enne push'i

Pipeline ei tee neid samme automaatselt — käivita käsitsi:

npm run lint                    # Oxlint + ESLint
npm run type-check              # vue-tsc range tüübikontroll
npm run test:unit -- --run      # Vitest unit + integration
npm run test:e2e                # Playwright (vajab elavat backendi)

Kui kõik on roheline, alles siis git push.

6.3 Eraldi hosting ja CORS

Frontend on hostitud eraldi URL-il ja pordil backendist:

  • Frontend: https://travel.rasmusj.com (Nginx, port 91)
  • Backend: https://travel.rasmusj.com (ASP.NET, port 90)

Kuna tegemist on eri origin'itega, on backend seadistatud lubama päringuid kõigilt domeenidelt (AllowAnyOrigin CORS-poliitika Program.cs-is). Frontend ei vaja CORS-i seadistamist — see on puhtalt backendi vastutus.


7. Testimine

Projektil on kolm testimise kihti — 39 testi 7 failis, kõik rohelised phase 3 modulaarmonoliit-backendi vastu. Unit ja integration testid ei vaja backendi (MSW mockib võrku); E2E testid käivad päris brauseris päris backendi vastu.

7.1 Unit testid (Vitest + jsdom)

Kaust: src/__tests__/unit/. Iga fail testib ühte kitsalt piiritletud koodiosa puhaste sisendite-väljunditega.

Testifail Mida testib Testide arv Mis vea see püüaks
formatCurrency.spec.ts märk-, sümbol-, kümnendkoha-, tuhandete-eraldaja-vormindus 7 Kuvatakse igal lehel — regression rikuks visuaalselt kogu äpi
parseJwt.spec.ts JWT base64url dekodeerimine, ASP.NET nameidentifier claim + sub fallback 9 Vale user-id parsing seoks kulud vaikselt vale kasutajaga
auth-store.spec.ts Pinia auth store: localStorage hüdratsioon, isAuthenticated reaktiivsus, logout(), watcher-based sync 6 Sünkroonimise lõhe → kasutaja näeks reload-i järel vana identiteeti
SplitMethodSelector.spec.ts kõik 4 jagamismeetodit, jooksev validatsioon, edit-režiim existingSplits-iga 9 Kõige keerulisem äriloogika frondis — invalid splittidega salvestatud kulu

Kokku: 31 unit-testi, jooks ~0.4s.

7.2 Integration test (Vitest + MSW)

Kaust: src/__tests__/integration/. Erinevalt unit-testist mockib MSW võrku, mitte axios funktsioone — niisiis axios käitub täpselt nagu prodis.

Testifail Mida testib Testide arv
token-refresh.spec.ts httpClient-i 401 → refresh → retry pipeline, kogu otsast otsani: (1) request interceptor lisab Bearer; (2) edukas refresh + originaalpäringu kordamine; (3) refresh kukub → logout → redirect /login-ile; (4) refresh token puudub → otse logout 4

See on app-i kõige väärtuslikum test — katab turvalisuse-tundlikuima glue-i (axios interceptors + Pinia store + router). Käsitsi seda flow-d katsetada nõuaks JWT aegumise ootamist või manuaalset katki tegemist.

7.3 End-to-end testid (Playwright + Chromium)

Kaust: e2e/. Päris brauser, päris backend. webServer config käivitab automaatselt npm run dev-i, niisiis vajalik on ainult backendi kättesaadavus.

Testifail Mida testib Testide arv
auth.spec.ts (1) login seemnekasutajaga alice@taltech.ee → logout; (2) vale parool jätab /login-ile; (3) kaitstud /trips redirectib anonüümse /login-ile 3
trip-crud.spec.ts Positive happy flow — login → loo unikaalse nimega reis → näe seda nimekirjas → ava Edit → muuda nime → kontrolli et muudatus on nähtav 1

Märkus trip-crud kohta: Delete on teadlikult välja jäetud, sest backend DeleteBehavior.Restrict blokeerib reisi kustutamise, kui sel on ükskõik milline TripParticipant (alati on vähemalt loojast Organizer). See on backend-i probleem, mitte frondi oma — niisiis test katsetab Update-i selle asemel, et pipeline jääks roheline.

Playwright seadistus (playwright.config.ts):

  • Brauser: ainult Chromium
  • Käivitab automaatselt Vite arendusserveri
  • .env-ist loetav VITE_API_BASE_URL määrab, mille vastu test jookseb (praegu: prod backend)
  • Üks worker (jadatestid jagatud seemneandmete tõttu)
  • Trace + screenshot ebaõnnestumisel

7.4 Testiskriptid

npm run test:unit              # Vitest watch-režiim
npm run test:unit -- --run     # Ühekordne jooks
npm run test:e2e               # Playwright headless (vajab backendi kättesaadavust)
npm run test:e2e:ui            # Playwright interaktiivne UI

7.5 Kuidas E2E käib phase 3 vastu

Frontendi .env osutab vaikimisi prod backendile:

VITE_API_BASE_URL=https://travel.rasmusj.com/api/v1/

Niisiis npm run test:e2e lokaalselt:

  1. Käivitab Vite dev-serveri pordil 5173
  2. Vite serveerib Vue äppi, mis HTTP-päringutes osutab prod backendile
  3. Playwright juhib Chromiumi → login alice@taltech.ee → CRUD operatsioonid → tulemused tulevad päris prod-DB-st

⚠️ trip-crud test loob päris reisi prod-andmebaasi ja ei kustuta seda (vt 7.3 märkust). Kui taht olla puhtam, siis vaheta .env.local-iga lokaalse Dockeri vastu.

7.6 Kokkuvõte

Kiht Raamistik Failide arv Testide arv Vajab backendi? Aeg
Unit Vitest + jsdom 4 31 Ei ~0.4s
Integration Vitest + MSW 1 4 Ei ~0.5s
E2E Playwright + Chromium 2 4 Jah ~11s
Kokku 7 39 ~12s

8. Seemneandmete kasutajad

Demokasutajatel on parool Kala.12345. See on koodis teadlikult: tegu on demoga ja andmed on välja mõeldud.

Admin on eraldi. Ta luuakse ainult siis, kui serveripoolel on seatud SEED_ADMIN_PASSWORD, ja vaikeväärtust ei ole. Ilma selle muutujata ei ole admin-kontot üldse olemas.

Email Roll
user@taltech.ee user
alice@taltech.ee user
bob@taltech.ee user
charlie@taltech.ee user
diana@taltech.ee user

9. Kokkuvõte — kas kõik töötab?

Kontroll Staatus
Frontendi VITE_API_BASE_URL viitab backendi Dockeri pordile (90)
API versioon v1 vastab backendi vaikeversioonile
Kõik 9 frontendi teenust leiavad backendist vastava kontrolleri
JWT vastuse struktuur on mõlemas pooles sama
Refresh-tokeni voog (request → refresh → retry)
CORS lubab frontendi origini (AllowAnyOrigin)
ESplitMethod enum sama mõlemas pooles
Pordikonfliktid puuduvad (90, 5432, 91, 5173)
Seemneandmed (6 kasutajat, 4 reisi) laaditakse käivitamisel

Ülesande nõuete vastavus

Nõue Staatus Kus
Eraldiseisev klientrakendus valitud tehnoloogiaga Vue 3 + TypeScript
Kasutab oma backendi REST API-t VITE_API_BASE_URL, 9 teenust
JWT + refresh token autentimine httpClient.ts, AccountService.ts, auth.ts
Login / logout LoginView.vue, RegisterView.vue, App.vue
CRUD vähemalt 3 entiteedil ✅ (5) Trips, Expenses, BudgetCategories, Wishlist, Polls
CI/CD deploy — klient eraldi URL-il .gitlab-ci.yml, eraldi Docker konteiner pordil 91
CORS käsitlus Backend CorsAllowAll poliitika; frontend eraldi origin'il
Boonus: unit + integration + e2e testid Vitest + MSW + Playwright — 39 testi (kõik rohelised phase 3 vastu)