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
1 # Projekti ülevaade — SplitApp (Reisikulude jagaja)
2
3 See dokument kirjeldab tervet süsteemi: **frontendi** (Vue 3) ja **backendi** (ASP.NET Core 10) ning seda, kuidas need omavahel suhtlevad.
4
5 ---
6
7 ## 1. Mis on SplitApp?
8
9 SplitApp on reisiplaneerimise ja ühiste kulude jagamise rakendus. Kasutajad saavad:
10
11 - luua reise ja kutsuda kaaslasi
12 - sisestada ühiseid kulusid ja jagada neid nelja erineva meetodi järgi
13 - hallata eelarvekategooriaid ja jälgida kulutusi
14 - pidada soovinimekirja tegevustest/kohtadest koos hääletusega
15 - teha grupiotsuseid küsitluste kaudu
16 - lõpuks arvestada, kes kellele võlgu on (arveldused)
17
18 Süsteem koosneb kahest eraldiseisvast projektist, mis suhtlevad REST API kaudu:
19
20 | Projekt | Kaust | Tehnoloogia |
21 |---|---|---|
22 | **Frontend** | `rasmju-js-a7` | Vue 3 + TypeScript + Vite |
23 | **Backend** | `rasmju-csweb-phase3` | ASP.NET Core 10 + PostgreSQL |
24
25 ---
26
27 ## 2. Frontend — `rasmju-js-a7`
28
29 ### 2.1 Tehnoloogiad
30
31 - **Vue 3.5** (Composition API) + **TypeScript 6**
32 - **Vite 8** — build tool ja arendusserver
33 - **Vue Router 5** — lehekülgede ruutimine
34 - **Pinia 3** — olekuhaldus
35 - **Axios 1.14** — HTTP klient
36 - **Bootstrap 5.3** — stiilide raamistik
37 - **Vitest 4** — unit testid
38 - **ESLint + Oxlint + Prettier** — koodi kvaliteet
39
40 ### 2.2 Projekti struktuur
41
42 ```
43 src/
44 ├── components/ # Jagatud komponendid
45 │ ├── SplitMethodSelector.vue # Kulu jagamise UI (4 meetodit)
46 │ └── ToastContainer.vue # Teavituste kuvamine
47 ├── composables/
48 │ └── useToast.ts # Globaalne teavituste süsteem
49 ├── directives/
50 │ └── vAnimate.ts # Scroll-animatsiooni direktiiv
51 ├── router/
52 │ └── index.ts # Kõik ruudid + autentimise valvur
53 ├── services/ # API kliendid
54 │ ├── httpClient.ts # Axiose seadistus + interceptorid
55 │ ├── AccountService.ts # Login, register, refresh, logout
56 │ ├── TripService.ts # Reisid
57 │ ├── ExpenseService.ts # Kulud
58 │ ├── BudgetCategoryService.ts # Eelarvekategooriad
59 │ ├── CurrencyService.ts # Valuutad
60 │ ├── PollService.ts # Küsitlused
61 │ ├── WishlistService.ts # Soovinimekiri
62 │ ├── SettlementService.ts # Arveldused
63 │ └── InvitationService.ts # Kutsed
64 ├── stores/
65 │ └── auth.ts # JWT + refreshToken + userName
66 ├── types/ # TypeScript liidesed (DTO vastavalt API-le)
67 ├── utils/
68 │ ├── formatCurrency.ts # Valuuta vormindus
69 │ └── parseJwt.ts # JWT dekodeerimine
70 ├── views/ # Leheküljed
71 │ ├── HomeView.vue
72 │ ├── LoginView.vue
73 │ ├── RegisterView.vue
74 │ ├── trips/ # IndexView, DetailView, CreateView, EditView
75 │ ├── expenses/ # Index, Create, Edit
76 │ ├── budget-categories/ # Index, Create, Edit
77 │ ├── wishlist/ # Index, Create, Edit
78 │ ├── polls/ # Index, Create, Detail
79 │ ├── invitations/ # AcceptView
80 │ ├── members/ # MembersView
81 │ └── settlements/ # SettlementView
82 ├── App.vue # Juurkomponent (navbar + router-view)
83 └── main.ts # Käivituspunkt
84 ```
85
86 ### 2.3 Ruutimine
87
88 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`).
89
90 **Avalikud ruudid:** `/`, `/login`, `/register`
91 **Autentimist nõudvad ruudid:** kõik ülejäänud (valvur `router/index.ts` suunab külalised `/login` peale)
92
93 Põhilised ruudid:
94 - `/trips` — reiside loend
95 - `/trips/create` — uus reis
96 - `/trips/:tripId` — reisi detailvaade (parent)
97 - `expenses`, `expenses/create`, `expenses/:id/edit`
98 - `budget`, `budget/create`, `budget/:id/edit`
99 - `wishlist`, `wishlist/create`, `wishlist/:id/edit`
100 - `polls`, `polls/create`, `polls/:id`
101 - `members` — reisikaaslased
102 - `settlement` — arveldused
103 - `edit` — reisi muutmine
104 - `/invitations/:token` — kutse vastuvõtmine
105
106 ### 2.4 Autentimine
107
108 **Salvestus:** JWT ja refresh token hoitakse `localStorage`-s (`jwt`, `refreshToken`, `userName`). Pinia store `auth.ts` sünkroniseerib need automaatselt `watch`-i kaudu.
109
110 **Voog:**
111 1. Login/Register → `AccountService` saadab POST päringu → saab tagasi `{ jwt, refreshToken, firstName, lastName }`
112 2. `httpClient`-i **request interceptor** lisab iga päringule päise `Authorization: Bearer <jwt>`
113 3. **Response interceptor** püüab 401 vastused kinni:
114 - Kutsub `refreshTokenAsync()` → uuendab tokenid store'is → kordab algset päringut
115 - Kui refresh ebaõnnestub → logib kasutaja välja ja suunab `/login`-ile
116 4. Logout → tühistab refresh tokeni backendis + puhastab store'i
117
118 ### 2.5 Keskkonnamuutujad
119
120 Fail `.env` (gitignore'is):
121 ```env
122 VITE_API_BASE_URL=http://localhost:90/api/v1/
123 ```
124
125 Kasutatakse `httpClient.ts` ja `AccountService.ts` failides. Kui backend käib lokaalselt `dotnet run` kaudu, tuleb see vahetada `http://localhost:5086/api/v1/` vastu.
126
127 ### 2.6 Kulude jagamise loogika
128
129 `SplitMethodSelector.vue` toetab nelja meetodit — need vastavad täpselt backendi `ESplitMethod` enumile:
130
131 | Meetod | Kirjeldus |
132 |---|---|
133 | **EqualAll** | Kogu summa jagatakse võrdselt kõigi osalejate vahel |
134 | **EqualSubset** | Kasutaja valib alamhulga osalejatest, jagatakse võrdselt |
135 | **ExactAmounts** | Iga osaleja kohta sisestatakse täpne summa (peab võrduma kogusummaga) |
136 | **Percentages** | Iga osaleja kohta protsent (peavad kokku andma 100%) |
137
138 Komponent valideerib sisendit jooksvalt ja emiteerib `update:splits` + `update:valid`.
139
140 ### 2.7 Skriptid
141
142 ```sh
143 npm install # Installi sõltuvused
144 npm run dev # Arendusserver http://localhost:5173
145 npm run build # Type-check + production build
146 npm run preview # Eelvaade ehitatud rakendusest
147 npm run test:unit # Vitest testid
148 npm run lint # Oxlint + ESLint
149 npm run format # Prettier
150 ```
151
152 > **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.
153
154 ---
155
156 ## 3. Backend — `rasmju-csweb-phase3`
157
158 ### 3.1 Tehnoloogiad
159
160 - **.NET 10** (net10.0)
161 - **ASP.NET Core 10** Web API + Identity + MVC (Razor Views)
162 - **Entity Framework Core 10** — ORM
163 - **PostgreSQL 16** (Npgsql) — andmebaas, kolm schema-isoleeritud schema-t ühes andmebaasis
164 - **MediatR** — moodulitevaheline suhtlus (in-process queries + notifications)
165 - **JWT Bearer** autentimine + refresh token
166 - **Swashbuckle** — Swagger/OpenAPI UI
167 - **Asp.Versioning** — API versioonimine (`/api/v{version}/...`)
168
169 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`.
170
171 ### 3.2 Lahuse struktuur
172
173 `SplitApp.Modular/` lahus sisaldab kolme moodulit (kummalgi 4 projekti), kahte jagatud projekti ja ühte hosti:
174
175 ```
176 SplitApp.Modular/
177 ├── SplitApp.sln
178 ├── Directory.Build.props
179 ├── src/
180 │ ├── SplitApp.WebApp/ # Composition root + host
181 │ │ ├── Program.cs # AddXxxModule(...) wiring
182 │ │ ├── Application/ # Phase-2-st tõstetud BLL
183 │ │ │ ├── Services/ (+ Admin/, Identity/) # 12 admin + 9 klient + 1 identity teenust
184 │ │ │ ├── DTO/ # BllDto'd vaadetele
185 │ │ │ ├── Mappers/ # Domain ↔ BllDto factory mapperid
186 │ │ │ ├── Persistence/AppUnitOfWork.cs # Aggregib 3 mooduli DbContextid
187 │ │ │ └── Persistence/CrossModuleNavigationLoader.cs
188 │ │ ├── Areas/Admin/ # MVC admin haldusliides (13 kontrollerit)
189 │ │ ├── Areas/Identity/ # Razor Identity UI (Register jne)
190 │ │ ├── Controllers/, Views/ # Klient-MVC (parity phase 2-ga)
191 │ │ └── Resources/ # i18n resx (en, et)
192 │ ├── Shared/
193 │ │ ├── SplitApp.Shared.Kernel/ # BaseEntity, IBaseRepo, IUoW, LangStr
194 │ │ └── SplitApp.Shared.Contracts/ # MediatR IRequest / INotification
195 │ └── Modules/
196 │ ├── Users/ # 4 projekti, schema "users"
197 │ │ ├── ...Domain/ # AppUser, AppRole, AppRefreshToken
198 │ │ ├── ...Application/ # IIdentityService, JWT/refresh, MediatR handlerid
199 │ │ ├── ...Infrastructure/ # UsersDbContext, repod, migrations
200 │ │ └── ...Api/ # /api/v1/identity/...
201 │ ├── Trips/ # 4 projekti, schema "trips"
202 │ └── Expenses/ # 4 projekti, schema "expenses"
203 └── tests/
204 ├── SplitApp.Modules.{Users,Trips,Expenses}.Tests/ # Per-module unit testid
205 └── SplitApp.WebApp.IntegrationTests/ # Architecture invariant + smoke (25 testi kokku)
206 ```
207
208 Iga moodul = mini-Clean-Architecture (`Domain` ← `Application` ← `Infrastructure`, `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).
209
210 ### 3.3 Domeenimudel
211
212 Põhientiteedid ja nende seosed:
213
214 | Entiteet | Olulised väljad | Seosed |
215 |---|---|---|
216 | **Trip** | Name, Description, Destination, StartDate, EndDate, Status, DefaultCurrencyId, CreatedById | 1→many: Participants, Expenses, BudgetCategories, WishlistItems, Polls, Invitations, SettlementPlans |
217 | **TripParticipant** | TripId, UserId, Role (Organizer/Participant), Nickname, IsActive | ↔ Trip, AppUser |
218 | **Expense** | TripId, PaidByUserId, Amount, Description, ExpenseDate, SplitMethod, BudgetCategoryId, CurrencyId | 1→many: ExpenseSplits |
219 | **ExpenseSplit** | ExpenseId, UserId, Amount, Percentage | ↔ Expense, AppUser |
220 | **BudgetCategory** | TripId, Name (LangStr), IconName, PlannedAmount | 1→many: Expenses |
221 | **TripInvitation** | TripId, Token (unikaalne), Status, ExpiresAt | ↔ Trip |
222 | **TripPoll** | TripId, Question, AllowMultipleVotes, IsAnonymous, ClosedAt | 1→many: Options → Votes |
223 | **TripWishlistItem** | TripId, Title, Category, Priority, EstimatedCost, IsCompleted | 1→many: Votes |
224 | **SettlementPlan** | TripId, TotalAmount, Status | 1→many: Payments |
225 | **SettlementPayment** | FromUserId, ToUserId, Amount, Status (Pending/MarkedPaid/Confirmed) | ↔ SettlementPlan |
226 | **Currency** | Code (3 tähte), Name (LangStr), Symbol | — |
227
228 **Identity entiteedid (Users moodul, schema `users`):**
229 - `AppUser` (laiendab `IdentityUser<Guid>`) — lisab `FirstName`, `LastName`
230 - `AppRole` (laiendab `IdentityRole<Guid>`) — rollid: `user`, `admin`
231 - `AppRefreshToken` — refresh token + eelmine token + aegumised
232
233 **Baasinfrastruktuur (`SplitApp.Shared.Kernel`):**
234 - `BaseEntity` — abstraktne: `Id` (Guid), `CreatedAt`, `UpdatedAt`
235 - `LangStr` — mitmekeelne string (`Dictionary<string, string>`), salvestatakse andmebaasis JSON-ina
236
237 **Enumid:**
238 - `ETripStatus`: Active, **Finalizing**, Settled, Archived (Finalizing = arvelduskava lukus, oodatakse kinnitusi)
239 - `EParticipantRole`: Organizer, Participant
240 - `ESplitMethod`: **EqualAll, EqualSubset, ExactAmounts, Percentages** (vastab frontendi omale)
241 - `EInvitationStatus`: Pending, Accepted, Declined, Expired, Revoked
242 - `ESettlementStatus`: Pending, InProgress, Completed
243 - `EPaymentStatus`: Pending, MarkedPaid, Confirmed
244
245 **Schemade jaotus:**
246
247 | Schema | Moodul | Entiteedid |
248 |---|---|---|
249 | `users` | Users | AppUser, AppRole + ASP.NET Identity tabelid, AppRefreshToken |
250 | `trips` | Trips | Trip, TripParticipant, TripInvitation, TripPoll(+Option/Vote), TripWishlistItem(+Vote), BudgetCategory |
251 | `expenses` | Expenses | Currency, Expense, ExpenseSplit, SettlementPlan, SettlementPayment, SplitPreset(+Member) |
252
253 **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).
254
255 Referentsiaalne terviklikkus tagatakse kahel viisil:
256 1. **Eelnev validatsioon MediatR-päringutega** — näit enne kulu salvestamist saadab `ExpensesController` `IsTripParticipantQuery(tripId, userId)` Trips moodulile
257 2. **Domain-eventide koristus kustutamisel** — `UserDeletedEvent` / `TripDeletedEvent` ja teised moodulid tellivad need ning kustutavad sõltuvad read
258
259 ### 3.4 Andmebaas
260
261 **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.
262
263 | DbContext | Schema | Pärib | Asukoht |
264 |---|---|---|---|
265 | `UsersDbContext` | `users` | `IdentityDbContext<AppUser, AppRole, Guid>` (rakendab ka `IDataProtectionKeyContext`) | `Modules/Users/...Infrastructure/Persistence/` |
266 | `TripsDbContext` | `trips` | `DbContext` | `Modules/Trips/...Infrastructure/Persistence/` |
267 | `ExpensesDbContext` | `expenses` | `DbContext` | `Modules/Expenses/...Infrastructure/Persistence/` |
268
269 **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.
270
271 **Olulised piirangud (OnModelCreating, igas DbContext-is):**
272 - Kõik seosed mooduli sees: `DeleteBehavior.Restrict` (kustutamisel ei kustutata kaskaadis — välja arvatud cleanup eventidel)
273 - Unikaalne indeks: `TripInvitation.Token`
274 - Liit-unikaalne indeks: `(TripParticipant.TripId, UserId)`, `(TripWishlistVote.WishlistItemId, UserId)`, `(TripPollVote.PollOptionId, UserId)`
275 - DateTime väljad: UTC konverter (alati salvestatakse UTC-s) — `UtcDateTimeConverter` igas mooduli `Persistence/` kaustas
276 - `LangStr` väljad (`Currency.Name`, `BudgetCategory.Name`): JSON-serialiseeritud
277
278 **Migratsioonid:** 1 migratsioon mooduli kohta (`Init`), kokku 3:
279 - `Modules/Users/...Infrastructure/Persistence/Migrations/`
280 - `Modules/Trips/...Infrastructure/Persistence/Migrations/`
281 - `Modules/Expenses/...Infrastructure/Persistence/Migrations/`
282
283 Migratsioonid rakendatakse host'i käivitamisel automaatselt — iga mooduli `UseXxxModule()` extension call (`Program.cs`) teeb `db.Database.Migrate()`.
284
285 **Andmete lähtestamine** (seadistatakse `appsettings.json`-is):
286 ```json
287 "DataInitialization": {
288 "DropDatabase": false, // Kustuta andmebaas käivitamisel
289 "MigrateDatabase": true, // Rakenda migratsioonid
290 "SeedIdentity": true, // Loo kasutajad ja rollid (Users moodul)
291 "SeedData": true // Loo näidisandmed (host-tasandil, peale module init'e)
292 }
293 ```
294
295 **Seemneandmed:**
296 - **5 demokasutajat** (parool kõigil: `Kala.12345`) — seedib `UsersModuleExtensions.UseUsersModule()`:
297 - `user@taltech.ee`, `alice@taltech.ee`, `bob@taltech.ee`, `charlie@taltech.ee`, `diana@taltech.ee`
298 - admin luuakse ainult siis, kui `SEED_ADMIN_PASSWORD` on seatud
299 - **5 valuutat:** EUR, USD, GBP, SEK, NOK — seedib Expenses moodul
300 - **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
301
302 ### 3.5 API lõpp-punktid
303
304 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`).
305
306 **Identity / Account** (Users moodul) — `/api/v1/identity/account`
307
308 | Verb | Lõpp-punkt | Otstarve | Auth |
309 |---|---|---|---|
310 | POST | `/register` | Kasutaja registreerimine | — |
311 | POST | `/login` | Login → JWT + refresh token | — |
312 | POST | `/refreshtokendata` | Tokenite uuendamine | — |
313 | POST | `/logout` | Refresh tokeni tühistamine | JWT |
314
315 **Trips** (Trips moodul) — `/api/v1/trips`
316
317 | Verb | Lõpp-punkt | Otstarve |
318 |---|---|---|
319 | GET | `/` | Kasutaja reiside loend |
320 | POST | `/` | Uus reis |
321 | GET | `/{id}` | Reisi detail |
322 | PUT | `/{id}` | Muuda reisi |
323 | DELETE | `/{id}` | Kustuta reis |
324 | GET | `/{tripId}/participants` | Osalejate loend |
325 | DELETE | `/{tripId}/participants/{userId}` | Eemalda osaleja |
326
327 **Expenses** (Expenses moodul) — `/api/v1/expenses`
328 - GET `/trip/{tripId}`, POST `/`, GET `/{id}`, PUT `/{id}`, DELETE `/{id}`
329
330 **BudgetCategories** (Trips moodul) — `/api/v1/budgetcategories`
331 - GET `/trip/{tripId}`, POST `/`, PUT `/{id}`, DELETE `/{id}`
332
333 **Currencies** (Expenses moodul) — `/api/v1/currencies`
334 - GET `/` (avalik, ei vaja JWT-d)
335
336 **Invitations** (Trips moodul) — `/api/v1/invitations`
337 - POST `/`, GET `/{token}`, POST `/{token}/accept`, POST `/{token}/decline`, POST `/{token}/revoke`
338
339 **Polls** (Trips moodul) — `/api/v1/polls`
340 - GET `/trip/{tripId}`, POST `/`, GET `/{id}`, POST `/{id}/vote`, POST `/{id}/close`, DELETE `/{id}`
341
342 **Wishlist** (Trips moodul) — `/api/v1/wishlist`
343 - GET `/trip/{tripId}`, POST `/`, PUT `/{id}`, DELETE `/{id}`, POST `/{id}/vote`, POST `/{id}/complete`
344
345 **Settlements** (Expenses moodul) — `/api/v1/settlements`
346 - GET `/trip/{tripId}`, GET `/trip/{tripId}/summary`, GET `/trip/{tripId}/balances`
347 - POST `/trip/{tripId}/calculate` — arvutab ja loob uue arvelduskava
348 - POST `/payments/{paymentId}/mark-paid` — märgib makse tasutuks
349 - POST `/payments/{paymentId}/confirm` — kinnitab saamise
350
351 **SplitPresets** (Expenses moodul) — `/api/v1/splitpresets`
352 - GET `/trip/{tripId}`, GET `/{id}`, POST `/`, PUT `/{id}`, DELETE `/{id}`
353
354 ### 3.6 Autentimine ja turvalisus
355
356 **JWT seadistus (appsettings.json):**
357 ```json
358 "JWT": {
359 "Issuer": "itcollege.taltech.ee",
360 "Audience": "itcollege.taltech.ee",
361 "ExpiresInSeconds": 1800 // 30 minutit
362 }
363 ```
364
365 **Voog:**
366 1. `POST /login` → tagastab JWT (30 min) + `AppRefreshToken` (kehtib 7 päeva)
367 2. Frontend kasutab JWT-d iga päringul
368 3. Enne aegumist (või 401 korral) → `POST /refreshtokendata` → uus JWT + uus refresh token (vana märgitakse eelmiseks, kehtib veel 1 minut ühildumiseks)
369 4. `POST /logout` → kustutab refresh tokeni andmebaasist
370
371 **Autoriseerimine kontrollerites (IDOR-kaitse):**
372 - Enamik lõpp-punkte: `[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]`
373 - Kasutaja-id loetakse JWT `nameidentifier` claimist iga päringu alguses
374 - **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.
375 - Reisi-sisene loaja kontroll (organizer-only mutatsioon): `if (trip.CreatedById != userId.Value) return Forbid();`
376 - `List` endpointide nähtavus filtreeritud serveri pool — kasutaja ei näe kunagi reise/kulusid mille trip-osalemine puudub
377
378 ### 3.7 CORS
379
380 `Program.cs` seadistab poliitika `CorsAllowAll`:
381 ```csharp
382 .AllowAnyOrigin()
383 .AllowAnyMethod()
384 .AllowAnyHeader()
385 ```
386
387 See tähendab, et frontend võib olla mistahes pordil/domeenil — arenduseks mugav, tootmises tuleks piirata.
388
389 ### 3.8 Konfiguratsioon (appsettings.json)
390
391 ```json
392 "ConnectionStrings": {
393 "DefaultConnection": "Host=localhost;Port=5432;Database=splitapp;Username=postgres;Password=postgres"
394 },
395 "SupportedCultures": ["en", "et"],
396 "DefaultCulture": "en"
397 ```
398
399 **Lokaliseerimine:** toetab inglise ja eesti keelt. Kultuuri saab muuta query parameetriga `?culture=et` või küpsise kaudu.
400
401 ### 3.9 Käivitamine
402
403 **Lokaalselt (dotnet):**
404 ```sh
405 cd SplitApp.Modular/src/SplitApp.WebApp
406 dotnet run
407 ```
408 → `http://localhost:5086` (http) või `https://localhost:7040` (https)
409
410 **Dockeri kaudu (soovitatud):**
411 ```sh
412 docker compose up --build
413 ```
414 → backend: `http://localhost:90`
415 → Postgres: `localhost:5432`
416
417 ---
418
419 ## 4. Kuidas frontend ja backend koos töötavad
420
421 ### 4.1 Pordid
422
423 | Teenus | Port | Kus |
424 |---|---|---|
425 | Backend API | **90** | Docker (host) → 8080 (container) |
426 | Backend API (lokaalselt) | 5086 (http) / 7040 (https) | `dotnet run` |
427 | PostgreSQL | 5432 | Docker |
428 | Frontend (prod, Nginx) | **91** | Docker |
429 | Frontend (dev, Vite) | 5173 | `npm run dev` või Docker `dev` profiil |
430
431 **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**.
432
433 ### 4.2 API URL
434
435 Frontendi `.env`:
436 ```env
437 VITE_API_BASE_URL=http://localhost:90/api/v1/
438 ```
439
440 Kui backend käib lokaalselt (`dotnet run`):
441 ```env
442 VITE_API_BASE_URL=http://localhost:5086/api/v1/
443 ```
444
445 ### 4.3 Lõpp-punktide vastavustabel
446
447 | Frontend teenus | Frontendi kõne | Backendi kontroller |
448 |---|---|---|
449 | AccountService | `identity/Account/Login` | `AccountController.Login` (Users moodul) |
450 | AccountService | `identity/Account/Register` | `AccountController.Register` (Users moodul) |
451 | AccountService | `identity/Account/RefreshTokenData` | `AccountController.RefreshTokenData` (Users moodul) |
452 | AccountService | `identity/Account/Logout` | `AccountController.Logout` (Users moodul) |
453 | TripService | `Trips/*` | `TripsController` |
454 | ExpenseService | `Expenses/*` | `ExpensesController` |
455 | BudgetCategoryService | `BudgetCategories/*` | `BudgetCategoriesController` |
456 | CurrencyService | `Currencies` | `CurrenciesController` |
457 | PollService | `Polls/*`, `Polls/:id/vote`, `Polls/:id/close` | `PollsController` |
458 | WishlistService | `Wishlist/*`, `:id/vote`, `:id/complete` | `WishlistController` |
459 | SettlementService | `Settlements/trip/:id/*`, `payments/:id/mark-paid` | `SettlementsController` |
460 | InvitationService | `Invitations/:token/*` | `InvitationsController` |
461
462 ASP.NET Core ruutimine on **tõstutundetu**, seega erinevused nagu `Trips` vs `trips` ei tekita probleemi.
463
464 ### 4.4 Andmetüüpide vastavus
465
466 Frontendi TypeScript liidesed (`src/types/`) on loodud backendi DTO-de põhjal ning struktuurid vastavad 1:1. Näiteks:
467
468 **Frontend `IExpense`** ↔ **Backend `ExpenseDto`**
469 ```
470 id, tripId, paidByUserId, budgetCategoryId, currencyId,
471 amount, description, expenseDate, splitMethod, splits[]
472 ```
473
474 **Frontend `IJwtResponse`** ↔ **Backend `JWTResponse`**
475 ```
476 jwt, refreshToken, firstName, lastName
477 ```
478
479 Kulude jagamise enum `ESplitMethod` on mõlemas pooles identne.
480
481 ### 4.5 Autentimise koostöö
482
483 1. Frontend saadab `POST /api/v1/identity/account/login`
484 2. Backend valideerib ja tagastab `{ jwt, refreshToken, firstName, lastName }`
485 3. Frontend salvestab need `localStorage`-sse ja Pinia store'i
486 4. Iga järgmine päring → `httpClient` lisab `Authorization: Bearer <jwt>`
487 5. Kui backend tagastab 401 → frontend proovib automaatselt refresh'i
488 6. Backend valideerib refresh tokeni, loob uue JWT + uue refresh tokeni ja tagastab
489 7. Frontend uuendab store'i ja kordab algset päringut
490
491 ---
492
493 ## 5. Käivitamise juhend
494
495 ### 5.1 Täielik Docker-käivitus (soovitatud)
496
497 **1. Käivita backend + andmebaas:**
498 ```sh
499 cd C:\Users\rasmu\Documents\csharpweb\rasmju-csweb-phase3
500 docker compose up --build
501 ```
502 See käivitab:
503 - PostgreSQL pordil 5432
504 - Backend API pordil 90
505 - Rakendab migratsioonid ja laeb seemneandmed
506
507 **2. Kontrolli, et backend vastab:**
508 Avaga brauseris `http://localhost:90/swagger` — peaks kuvama Swagger UI.
509
510 **3. Käivita frontend:**
511
512 Arendusrežiimis (soovitatud koodi muutmiseks):
513 ```sh
514 cd C:\Users\rasmu\Documents\javascript\rasmju-js-a7
515 npm install
516 npm run dev
517 ```
518 → `http://localhost:5173`
519
520 Või Dockeri kaudu production build:
521 ```sh
522 docker compose up --build
523 ```
524 → `http://localhost:91`
525
526 ### 5.2 Kiire test
527
528 1. Ava `http://localhost:5173` (või `:91`)
529 2. Logi sisse seemneandmete kasutajaga:
530 - Email: `alice@taltech.ee`
531 - Parool: `Kala.12345`
532 3. Peaksid nägema reiside loendit (Barcelona Weekend, Summer Cabin Getaway, ...)
533 4. Ava üks reis → vaata kulusid, arveldusi, küsitlusi
534
535 ### 5.3 Lokaalne arendus (ilma Dockerita)
536
537 **Backend:**
538 ```sh
539 cd rasmju-csweb-phase3/SplitApp.Modular/src/SplitApp.WebApp
540 dotnet run
541 ```
542 → `http://localhost:5086`
543
544 **Frontend** (muuda `.env`):
545 ```env
546 VITE_API_BASE_URL=http://localhost:5086/api/v1/
547 ```
548 ```sh
549 cd rasmju-js-a7
550 npm run dev
551 ```
552
553 ---
554
555 ## 6. CI/CD pipeline
556
557 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.
558
559 ### 6.1 Deploy-etapp
560
561 Käivitub **ainult `main` harusse merge'imisel**. Kasutab VPS-il olevat self-hosted runner'it (`shared` tag):
562
563 ```sh
564 docker compose -p rasmju-js-a7 up --build --remove-orphans --detach
565 ```
566
567 - Ehitab uue multi-stage Docker image'i (Node build → Nginx)
568 - Käivitab uue konteineri pordil **91**
569 - Projektinimi `rasmju-js-a7` hoiab konteinerid backendi omadest eraldi
570 - `--remove-orphans` koristab vanad konteinerid
571
572 ### 6.2 Lokaalne kontrollnimekiri enne push'i
573
574 Pipeline ei tee neid samme automaatselt — käivita käsitsi:
575
576 ```sh
577 npm run lint # Oxlint + ESLint
578 npm run type-check # vue-tsc range tüübikontroll
579 npm run test:unit -- --run # Vitest unit + integration
580 npm run test:e2e # Playwright (vajab elavat backendi)
581 ```
582
583 Kui kõik on roheline, alles siis `git push`.
584
585 ### 6.3 Eraldi hosting ja CORS
586
587 Frontend on hostitud **eraldi URL-il ja pordil** backendist:
588 - Frontend: `https://travel.rasmusj.com` (Nginx, port 91)
589 - Backend: `https://travel.rasmusj.com` (ASP.NET, port 90)
590
591 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.
592
593 ---
594
595 ## 7. Testimine
596
597 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.
598
599 ### 7.1 Unit testid (Vitest + jsdom)
600
601 Kaust: `src/__tests__/unit/`. Iga fail testib ühte kitsalt piiritletud koodiosa puhaste sisendite-väljunditega.
602
603 | Testifail | Mida testib | Testide arv | Mis vea see püüaks |
604 |---|---|---|---|
605 | `formatCurrency.spec.ts` | märk-, sümbol-, kümnendkoha-, tuhandete-eraldaja-vormindus | **7** | Kuvatakse igal lehel — regression rikuks visuaalselt kogu äpi |
606 | `parseJwt.spec.ts` | JWT base64url dekodeerimine, ASP.NET `nameidentifier` claim + `sub` fallback | **9** | Vale user-id parsing seoks kulud vaikselt vale kasutajaga |
607 | `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 |
608 | `SplitMethodSelector.spec.ts` | kõik 4 jagamismeetodit, jooksev validatsioon, edit-režiim `existingSplits`-iga | **9** | Kõige keerulisem äriloogika frondis — invalid splittidega salvestatud kulu |
609
610 **Kokku:** 31 unit-testi, jooks ~0.4s.
611
612 ### 7.2 Integration test (Vitest + MSW)
613
614 Kaust: `src/__tests__/integration/`. Erinevalt unit-testist mockib MSW võrku, mitte axios funktsioone — niisiis axios käitub täpselt nagu prodis.
615
616 | Testifail | Mida testib | Testide arv |
617 |---|---|---|
618 | `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** |
619
620 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.
621
622 ### 7.3 End-to-end testid (Playwright + Chromium)
623
624 Kaust: `e2e/`. Päris brauser, päris backend. `webServer` config käivitab automaatselt `npm run dev`-i, niisiis vajalik on ainult backendi kättesaadavus.
625
626 | Testifail | Mida testib | Testide arv |
627 |---|---|---|
628 | `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** |
629 | `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** |
630
631 **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.
632
633 **Playwright seadistus** (`playwright.config.ts`):
634 - Brauser: ainult Chromium
635 - Käivitab automaatselt Vite arendusserveri
636 - `.env`-ist loetav `VITE_API_BASE_URL` määrab, mille vastu test jookseb (praegu: prod backend)
637 - Üks worker (jadatestid jagatud seemneandmete tõttu)
638 - Trace + screenshot ebaõnnestumisel
639
640 ### 7.4 Testiskriptid
641
642 ```sh
643 npm run test:unit # Vitest watch-režiim
644 npm run test:unit -- --run # Ühekordne jooks
645 npm run test:e2e # Playwright headless (vajab backendi kättesaadavust)
646 npm run test:e2e:ui # Playwright interaktiivne UI
647 ```
648
649 ### 7.5 Kuidas E2E käib phase 3 vastu
650
651 Frontendi `.env` osutab vaikimisi prod backendile:
652
653 ```env
654 VITE_API_BASE_URL=https://travel.rasmusj.com/api/v1/
655 ```
656
657 Niisiis `npm run test:e2e` lokaalselt:
658 1. Käivitab Vite dev-serveri pordil 5173
659 2. Vite serveerib Vue äppi, mis HTTP-päringutes osutab prod backendile
660 3. Playwright juhib Chromiumi → login `alice@taltech.ee` → CRUD operatsioonid → tulemused tulevad päris prod-DB-st
661
662 ⚠️ `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.
663
664 ### 7.6 Kokkuvõte
665
666 | Kiht | Raamistik | Failide arv | Testide arv | Vajab backendi? | Aeg |
667 |---|---|---|---|---|---|
668 | Unit | Vitest + jsdom | 4 | 31 | Ei | ~0.4s |
669 | Integration | Vitest + MSW | 1 | 4 | Ei | ~0.5s |
670 | E2E | Playwright + Chromium | 2 | 4 | Jah | ~11s |
671 | **Kokku** | | **7** | **39** | | ~12s |
672
673 ---
674
675 ## 8. Seemneandmete kasutajad
676
677 Demokasutajatel on parool **`Kala.12345`**. See on koodis teadlikult: tegu on
678 demoga ja andmed on välja mõeldud.
679
680 Admin on eraldi. Ta luuakse ainult siis, kui serveripoolel on seatud
681 `SEED_ADMIN_PASSWORD`, ja vaikeväärtust ei ole. Ilma selle muutujata ei ole
682 admin-kontot üldse olemas.
683
684 | Email | Roll |
685 |---|---|
686 | `user@taltech.ee` | user |
687 | `alice@taltech.ee` | user |
688 | `bob@taltech.ee` | user |
689 | `charlie@taltech.ee` | user |
690 | `diana@taltech.ee` | user |
691
692 ---
693
694 ## 9. Kokkuvõte — kas kõik töötab?
695
696 | Kontroll | Staatus |
697 |---|---|
698 | Frontendi `VITE_API_BASE_URL` viitab backendi Dockeri pordile (90) | ✅ |
699 | API versioon `v1` vastab backendi vaikeversioonile | ✅ |
700 | Kõik 9 frontendi teenust leiavad backendist vastava kontrolleri | ✅ |
701 | JWT vastuse struktuur on mõlemas pooles sama | ✅ |
702 | Refresh-tokeni voog (request → refresh → retry) | ✅ |
703 | CORS lubab frontendi origini (AllowAnyOrigin) | ✅ |
704 | `ESplitMethod` enum sama mõlemas pooles | ✅ |
705 | Pordikonfliktid puuduvad (90, 5432, 91, 5173) | ✅ |
706 | Seemneandmed (6 kasutajat, 4 reisi) laaditakse käivitamisel | ✅ |
707
708 ### Ülesande nõuete vastavus
709
710 | Nõue | Staatus | Kus |
711 |---|---|---|
712 | Eraldiseisev klientrakendus valitud tehnoloogiaga | ✅ | Vue 3 + TypeScript |
713 | Kasutab oma backendi REST API-t | ✅ | `VITE_API_BASE_URL`, 9 teenust |
714 | JWT + refresh token autentimine | ✅ | `httpClient.ts`, `AccountService.ts`, `auth.ts` |
715 | Login / logout | ✅ | `LoginView.vue`, `RegisterView.vue`, `App.vue` |
716 | CRUD vähemalt 3 entiteedil | ✅ (5) | Trips, Expenses, BudgetCategories, Wishlist, Polls |
717 | CI/CD deploy — klient eraldi URL-il | ✅ | `.gitlab-ci.yml`, eraldi Docker konteiner pordil 91 |
718 | CORS käsitlus | ✅ | Backend `CorsAllowAll` poliitika; frontend eraldi origin'il |
719 | Boonus: unit + integration + e2e testid | ✅ | Vitest + MSW + Playwright — 39 testi (kõik rohelised phase 3 vastu) |
720