feat(portal): finish PMS-25 story (contact session, ticket endpoints, billing/kb placeholders) #24
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/portal-story-pms-25"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Implements YouTrack story PMS-25 (customer portal API). Seven commits, one per sub-task.
Stacked on top of #21 (PMS-11 tickets story), which itself stacks on #20 (PMS-4 auth story). Targets
feat/tickets-story-pms-11so the portal can reuse the JOINedget_ticket_responsefrom PMS-11 and the host-crateMailerfrom PMS-4. Retarget tomainas the lower PRs merge.Sub-tasks
mokosh-server::modules::portalmodule withPortalAuthService, HS256 JWT taggedtyp = "portal_access"so portal and agent tokens are not cross-replayable,portal_auth_middleware, and aRequirePortalAuthextractor. Login takes(tenant_slug, email, password). Schema reusescontacts.{is_portal_user, portal_password_hash, portal_last_login_at}; no migration.POST /api/v1/portal/tickets: scoped to the contact's company;source = Portal; newTicketService::create_portal_ticketpicks an admin/manager user in the tenant ascreated_by_id(NOT NULL FK).GET /api/v1/portal/tickets: paginated list of all tickets at the contact's company. Company-scope matches the typical helpdesk model.GET /api/v1/portal/tickets/:id: cross-company returns 404 (not 403) so we don't leak the existence of sibling-company tickets.GET /api/v1/portal/invoices[/:id]: 401-vs-empty-200 / 401-vs-404 placeholders pending the billing module (PMS-33 story).GET /api/v1/portal/kb: empty-page placeholder pending the KB module (PMS-79 story).Behaviour-visible changes
/api/v1/portal/*no longer 501s. Login + ticket endpoints do real work; invoice / kb endpoints return contract-stable empty results.typvalue:"portal_access". Portal middleware rejects everything else.ticketswithsource = portaland a real admin's id increated_by_id(no nil-UUID FK hack).Test plan
cargo check --bin mokosh-serverclean (verified locally).POST /api/v1/portal/auth/loginwith a known(tenant_slug, email, password)returns an access token; reusing it on/api/v1/portal/auth/mereturns the contact.POST /api/v1/portal/ticketscreates a ticket visible at/api/v1/portal/ticketsand/api/v1/portal/tickets/:id./api/v1/portal/invoicesreturns{"data": [], "total": 0, ...};/api/v1/portal/invoices/some-uuidreturns 404; same for/api/v1/portal/kb.Closes #PMS-25
Reads `contact.tenant_id` + `contact.company_id` from the extractor and pages through `TicketService::list_portal_tickets`, which under the hood builds a `TicketFilter { company_id: Some(...), .. }` and shares the same JOINed SELECT as the agent-side list. Pagination piggybacks on `PaginationParams`. Company-wide visibility (not just `contact_id = self`) matches the typical helpdesk model where employees of company X can follow each other's tickets without admin glue. #PMS-28 State DonePull request closed