Files
gw-triage/knowledge/zeus.md
2026-07-04 18:58:25 +02:00

168 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## System identity
Zeus is the .NET 8 multi-service platform powering the GrowingWay property-rental product.
Owner: Giuseppe. Area keys: `backend`, `backoffice`, `infra`.
Three deployable services:
- `zeus_auth` — Auth, User, Policy, Devices, Settings gRPC APIs (host `auth.{DOMAIN}`)
- `zeus_property` — Announcement, Booking, Chat, Review, Notification gRPC APIs + SignalR payment and chat hubs (host `property.{DOMAIN}`)
- `zeus_admin` — Blazor Server backoffice dashboard (host `admin.{DOMAIN}`)
Infrastructure: nginx edge (SNI/L4 passthrough on :443) → per-env nginx web (TLS termination, L7 host-header routing) → backend containers. Redis for SignalR session registries. PostgreSQL (remote, never touch locally). Google Cloud Storage for images. Stripe for payments. Ermes (external gRPC) for SMS.
## User-visible surfaces
### zeus_admin (backoffice) pages — URL : nav label : heading
- `/` : Home : **Panoramica** — summary: host count, annunci count, media annunci/host
- `/Users` : Users — backoffice account management
- `/support-levels` : Livelli Supporto : **Livelli di Supporto** — RBAC for support operators
- `/Policy` : Policy — policy types and versions
- `/amenities` : Servizi : **Gestione Servizi** — amenity categories and types
- `/tags` : Announcement Tags : **Gestione Tag Annunci**
- `/reviews` : Recensioni : **Recensioni** — review parameters
- `/Verifications` : Verifiche Utenti : **Richieste di Verifica** — KYC queue (fronte/retro documento + selfie)
- `/Hosts` : Host : **Host** — host list; columns: Nome, Cognome, Email, Telefono, Città, Annunci, Referral, Verifica, Registrato il, Azioni
- `/Hosts/{id}` — host detail: Dati Personali, Stripe onboarding, announcements, bookings, availability block, referrals
- `/Announcements/{id}` — annuncio detail: Titolo, Descrizione, Meta Descrizione, Indirizzo
- `/Bookings` : Prenotazioni : **Prenotazioni** — columns: Stato, Pagamento, Guest, Host, Check-in, Check-out, Importo, Prossima cattura, Creata
- `/Bookings/{id}` : **Prenotazione** — sections: Dettagli prenotazione, Sessione di pagamento, Tentativi; actions: Cattura ora / Ripianifica / Force Accept / Force Reject / Force Cancel
- `/Guests` : Guest — guest user list
- `/support/chats` : Chat Supporto : **Chat Supporto** — columns: Nome, Cognome, ID Utente, Ultima attività
- `/support/chats/{userId}` — live support chat conversation
- `/reports` : Segnalazioni : **Segnalazioni** — chat-report moderation; columns: Segnalante, Segnalato, Motivo, Stato (Open / Reviewed / Dismissed), Data, Azioni; "Bloccata" chip on blocked conversations; action "Blocca conversazione"
- `/support` : **Apri sessione di supporto** / **Sessione di supporto attiva** — operator enters user's Codice Supporto; session becomes active after user acceptance
- `/support/user` : Utente Target — data view for active support-session user
### zeus_property gRPC services (app-facing)
`GuestAnnouncementService` (browse/search listings), `GuestBookingService` (Book, RetryPayment, CancelBooking, GetSummary), `GuestChatService` (SendMessage, GetMessages, RequestSupportChat, ReportUser, BlockUser), `GuestReviewService`, `GuestNotificationService` (ListNotifications, MarkNotificationRead), `GuestReceiptService`, `GuestFeedbackService`, `HostPropertyService` (listing CRUD, image upload), `HostBookingService` (ConfirmReservation, ghost bookings), `HostOnboardingService` (Stripe Connect), `HostReferralService` (referral code get/set).
SignalR hubs: `/api/guest/payments` (PaymentHub — BindSession → IssuePayment → Success/Failed callbacks), `/api/guest/chat` (ChatHub — real-time delivery).
### zeus_auth gRPC services
`AuthAuthenticationService` (Register, Login, ConfirmPhoneNumber, Refresh, RecoverPassword, VerifyConfirmationCode), `AuthUserService` (profile, KYC upload + submit), `AuthPolicyService` (SubscribeToPolicy, streaming updates), `AuthDevicesService`, `AuthSettingsService`, `AuthLogService` (Flutter log ingest), `PrivacyService` (account deletion).
## Error fingerprints
Errors surface as gRPC status codes (and mapped HTTP codes for transcoding). The Flutter app displays the localized `message` field from the gRPC `Status` detail. HTTP-transcoded errors carry headers `X-Error: true` and `X-Error-Message: <KEY>` with `statusCode` 400/401/403/404/409/429/500/503.
### Localized error strings (it-IT / en-US) — from Zeus.Errors/errors.json
| Key | it-IT | en-US |
|-----|-------|-------|
| `UNHANDLED` | "Abbiamo riscontrato un errore inaspettato. Riprova più tardi." | "We encountered an unexpected error. Please try again later." |
| `INTERNAL_ERROR` | "Qualcosa è andato storto. Riprova più tardi." | "Something went wrong. Please try again later." |
| `WRONG_PASSWORD` | "La password inserita non è corretta." | "The password you entered is incorrect." |
| `INVALID_CONFIRMATION_CODE` | "Il codice di verifica inserito è incorretto o scaduto." | "The verification code you entered is incorrect or has expired." |
| `SMS_SERVICE_UNAVAILABLE` | "Stiamo avendo problemi nell'invio degli SMS. Riprova più tardi." | "We're having trouble sending text messages right now. Please try again later." |
| `INVALID_ACCESS_TOKEN` | "La tua sessione è scaduta. Effettua nuovamente l'accesso." | "Your session has expired. Please log in again." |
| `DUPLICATE_EMAIL` | "Questo indirizzo email è già associato a un altro account." | "This email address is already associated with another account." |
| `DUPLICATE_PHONE_NUMBER` | "Questo numero di telefono è già associato a un altro account." | "This phone number is already associated with another account." |
| `ANNOUCEMENT_NOT_FOUND` (sic) | "Abbiamo riscontrato un errore nel recuperarti questo annuncio" | "We're having trouble getting this announcement." |
| `APARTMENT_UNAVAILABLE` | "Questo appartamento non è più disponibile." | "This apartment is no longer available." |
| `PRICE_CHANGED` | "Il prezzo per questo appartamento è cambiato." | "The price for this apartment has changed." |
| `PAYMENT_FAILED` | "Il pagamento non è andato a buon fine. Riprova." | "The payment was unsuccessful. Please try again." |
| `UNBOUND_PAYMENT_SESSION` | "Qualcosa è andato storto. Riprova più tardi." | "Something went wrong. Please try again later." |
| `TOO_MANY_CODE_REQUESTED` | "Sono stati richiesti troppi codici per questo numero di telefono oggi." | "Too many codes were requested for this phone number today." |
| `TERMS_NOT_ACCEPTED` | "Devi accettare i termini e le condizioni per continuare." | "You must accept the terms and conditions to continue." |
| `NO_HOST_DATA_FOUND` | "Configurazione host non trovata. Completa il processo di registrazione." | "Host configuration not found. Please complete the onboarding process." |
| `HOST_ONLY_METHOD` | "Quest'operazione è accessibile solo ai nostri host" | "This operation can only be performed by our hosts" |
| `ANNOUNCEMENT_HAS_NO_IMAGES` | "Aggiungi almeno un'immagine a questo annuncio." | "Please add at least one image to this announcement." |
| `InvalidImage` | "L'immagine fornita non è valida." | "The image provided is not valid." |
| `CHAT_BLOCKED` | "Non puoi inviare messaggi in questa conversazione." | "You can't send messages in this conversation." |
| `CANNOT_CHAT_WITH_SELF` | "Non puoi avviare una chat con te stesso." | "You can't start a chat with yourself." |
| `BOOKING_NOT_COMPLETED` | "Puoi recensire una prenotazione solo dopo che il soggiorno è completato." | "You can only review a booking after the stay is completed." |
| `ANNOUNCEMENT_ALREADY_REVIEWED` | "Hai già recensito questo annuncio." | "You have already reviewed this stay." |
| `INVALID_REFERRAL_CODE` | "Questo codice invito non esiste. Controllalo e riprova." | "This referral code doesn't exist. Please check it and try again." |
| `INVALID_REFERRAL_CODE_FORMAT` | "Il codice invito deve contenere da 4 a 16 lettere o numeri." | "A referral code must be 4-16 letters or numbers." |
| `REFERRAL_CODE_TAKEN` | "Questo codice invito è già in uso. Scegline un altro." | "This referral code is already in use. Please choose another one." |
| `MISSING_RECAPTCHA_TOKEN` | "Completa il captcha e riprova." | "Please complete the captcha challenge and try again." |
| `INVALID_RECAPTCHA_TOKEN` | "La validazione del captcha non e riuscita. Riprova." (sic) | "Captcha validation failed. Please try again." |
Admin-only (Zeus.Admin/errors.json):
| Key | it | en |
|-----|----|-----|
| `INVALID_SUPPORT_CODE` | "Il codice di supporto inserito non è valido" | "The support code is not valid" |
| `UNAUTHORIZED_SUPPORT_REQUEST` | "Questa richiesta non è ancora stata accettata" | "This request has not yet been accepted" |
### gRPC status codes and infrastructure errors
| Scenario | gRPC status | Code |
|----------|------------|------|
| Backend container down / nginx upstream fail | `Unavailable` | 14 |
| DB unreachable (first call) | `Internal` | 13 |
| Unhandled exception | `Internal` | 13 |
| Auth failure / expired JWT | `Unauthenticated` | 16 |
| Validation errors | `InvalidArgument` | 3 |
| Rate limit (OTP flood / SMS flood) | `ResourceExhausted` | 8 |
| State-guard violations | `FailedPrecondition` | 9 |
| Stripe payment declined | `Aborted` | 10 |
Payment SignalR failure codes: `Failed(code)` where `code` is a Stripe string (`card_declined`, `insufficient_funds`, `amount_mismatch`) or a Zeus key (`PAYMENT_FAILED`, `UNBOUND_PAYMENT_SESSION`). These raw codes reach the Flutter app directly — they are NOT localized by the server.
TLS failure appears as `CERTIFICATE_VERIFY_FAILED` or `HandshakeException` in Flutter logs (not a Zeus error; see `infra`).
## Symptom → area heuristics
- payment stuck / perpetually "processing" after booking confirmation → `backend` (Stripe webhook not processed; check `StripeWebhookController` logs for signature failure)
- "Il pagamento non è andato a buon fine" before card entry → `backend` (`PAYMENT_SESSION_CLOSED` or `UNBOUND_PAYMENT_SESSION`; admin may have force-cancelled the session)
- payment `Failed` callback with Stripe code (`card_declined`, `insufficient_funds`) → `backend` (Stripe declined; retry flow in app is correct)
- chat messages sent but other party never receives → `backend` (check `SendMessage` gRPC response + Redis chat registry; `SetOnline` may have expired)
- "Non puoi inviare messaggi in questa conversazione" → `backend` (`CHAT_BLOCKED` — conversation blocked by moderation; check backoffice Segnalazioni for a "Bloccata" chip; unblock is an admin action)
- OTP / SMS login code not arriving → `backend` (Ermes SMS service; key `SMS_SERVICE_UNAVAILABLE`)
- OTP code rejected as wrong or expired → `backend` (`INVALID_CONFIRMATION_CODE`)
- registration rejected for existing email/phone → `backend` (`DUPLICATE_EMAIL` / `DUPLICATE_PHONE_NUMBER`)
- captcha error at login/registration → `backend` (missing/invalid `x-recaptcha-token`; frequent after app-side token expiry — if the challenge never appears, `app-shared`)
- referral code rejected ("Questo codice invito non esiste" / "già in uso") → `backend` (`HostReferralService` validation)
- wrong or missing data shown in app with correct layout → `backend` (data model or repository issue)
- announcement detail missing, showing error toast → `backend` (`ANNOUCEMENT_NOT_FOUND`)
- booking dates show as unavailable despite being free → `backend` (`APARTMENT_UNAVAILABLE` — availability row state)
- "La tua sessione è scaduta" in the middle of a session (not at launch) → `backend` (`INVALID_ACCESS_TOKEN` returned by server; JWT refresh failed)
- host onboarding "Completa il processo di registrazione" on every login → `backend` (`NO_HOST_DATA_FOUND` — HostData row missing)
- notification badge count never resets → `backend` (`MarkNotificationRead` failing server-side, e.g. stale notification ID)
- user cannot leave a review after a stay → `backend` (`BOOKING_NOT_COMPLETED` — booking not yet `Completed` — or `ANNOUNCEMENT_ALREADY_REVIEWED`)
- admin dashboard page errors, wrong data in grids, action buttons fail → `backoffice`
- admin support code rejected ("Il codice di supporto inserito non è valido") → `backoffice`
- Referral column in Hosts grid empty or wrong count → `backoffice` (grid query; referral data itself is `backend`)
- site / app entirely unreachable, no HTTP response at all → `infra` (nginx.edge or docker stack down; TCP connection refused on :443/:80)
- 502 from nginx, gRPC status 14 "Unavailable" → `infra` (upstream container crash)
- TLS certificate warning or `CERTIFICATE_VERIFY_FAILED``infra` (Let's Encrypt cert expired or nginx misconfigured)
- visual glitches, overflow stripes, layout overflow, misaligned widgets → `app-shared` (Flutter rendering; not Zeus)
- platform-specific crash on Android or iOS (no network call) → `app-android` / `app-ios`
## Domain glossary
| Italian term | System concept |
|--------------|----------------|
| Annuncio / Annunci | `Announcement` entity; listing published by a host |
| Host | A user who completed Stripe onboarding; can publish annunci |
| Ospite / Guest | A user making a booking |
| Prenotazione / Prenotazioni | `BookingOrder` entity |
| Prenotazione fantasma / ghost booking | Host-created date block for externally-sourced bookings (Airbnb, phone) |
| Check-in / Check-out | Arrival and departure dates on a booking |
| Sessione di pagamento | `PaymentSession` — Stripe PaymentIntent/SetupIntent lifecycle; statuses: Ongoing, AwaitingCapture, Succeeded, Failed, Cancelled |
| Segnalazione / Segnalazioni | `ChatReport` — chat moderation report; statuses: Open, Reviewed, Dismissed |
| Conversazione bloccata / Blocca conversazione | Admin moderation block on a chat; users get `CHAT_BLOCKED` |
| Verifica / Verifiche Utenti | `VerificationRequest` — KYC identity check; statuses: Draft, Pending, Approved, Rejected |
| Recensione / Recensioni | `AnnouncementReview` — post-stay review; only possible after booking `Completed` |
| Servizi | Amenities (wifi, parking, …); `GuestAmenityService` |
| Notifica | In-app notification; `GuestNotificationService` |
| Codice Supporto | Short-lived code user reads aloud to operator to start a support session |
| Sessione di supporto | Admin operator impersonation session for support |
| Livelli di Supporto | RBAC levels for admin operators |
| Codice invito / referral | Referral code; `HostReferralService`; 416 alphanumeric chars |
| Cattura / Cattura ora | Stripe delayed capture; admin action on booking detail |
| Panoramica | Admin dashboard home |
## Not mine
- Visual rendering artifacts, overflow widgets, clipped text, wrong font size → `app-shared`
- "Sessione scaduta" toast at app launch with no network request in logs → `app-shared` (Flutter JWT expiry check done locally — no server call)
- Search map shows no results despite listings existing (no gRPC error) → `app-shared` (Flutter map range / filter state)
- Chat messages in wrong order or duplicated → `app-shared` (Flutter merge of historical stream + SignalR without dedup)
- Announcement images load as blank (HTTP 200 from GCS) → `app-shared` (Flutter image widget render failure; HTTP 403 = expired GCS URL = `backend`)
- Host calendar shows wrong blocked dates after refresh → `app-shared` (stale local cache; re-calling `GetReservations` returns correct data)
- Android-specific crash, iOS-specific crash → `app-android` / `app-ios`
- Store deployment failures, app binary issues → `app-android` / `app-ios`
- Push notification delivery on device (FCM/APNs) → `app-shared` (not Zeus; Zeus only stores tokens via `AuthDevicesService`)