AUTH_API_URL was wired to ApiConfigService.getBaseUrl() (the per-tenant
content origin, e.g. api.gorbushka.market), so admin/customer QR-login
session creation POSTed to a host with no /users/sessions route.
Auth is a single shared cross-tenant service (like payment's
qr.vitanova.network), not tenant-scoped - confirmed against the
pre-tenant-refactor state (commit a59ffbca) where every brand's
environment file carried the identical fixed authApiUrl, and against a
live POST to users.vitanova.network:456/users/sessions.
Restores authApiUrl as a fixed constant in both environment files and
wires AUTH_API_URL/MARKETPLACES_AUTH_CONFIG.apiUrl to it, mirroring the
existing qrApiUrl/provideMarketplacesPayment pattern. Also provides
MARKETPLACES_AUTH_CONFIG.marketplaceDomain via TenantResolverService's
existing getBaseDomain() so the X-Marketplace-Domain header stays
normalized instead of falling back to raw, unnormalized
location.hostname (it was never provided before, so that fallback was
always in effect).
No package edits, no path/method/body change, no QR/Telegram flow
change. tenantApiTemplate/tenantApiBaseUrls/ApiConfigService/
TenantResolverService untouched - still drive content-API resolution
only.
Known remaining blocker (server-side, tracked in vitanovaPackages
BACKEND-TODO.md): users.vitanova.network:456 CORS-rejects the
admin.gorbushka.market origin outright (403 on preflight, no allow
headers) while novo.market gets a full grant - this fix alone will not
make browser login work on gorbushka domains until that origin
allowlist is updated.
- Removed invented adminAuthApiUrl endpoint and separate AdminLoginComponent.
Admin login now uses the exact same Telegram session backend
(TelegramSessionApiService, {authApiUrl}/users/sessions) and the exact
same TelegramLoginComponent (mode="customer" | "admin" input) as customer
login - only the storage (cookie/localStorage/signals) stays separate.
- Extracted the shared HTTP+normalization logic from AuthService into
TelegramSessionApiService so both AuthService and AdminAuthService call it
instead of duplicating request/parsing code.
- Documented the resulting backend gap in docs/Project-Editor.md: since the
session API has no concept of "admin", server-side role enforcement is
required when admin API calls are made - the frontend only decides where
to store the session, not whether the user is actually an admin.
- Project editor: persist draft to localStorage, restore on reload,
last-saved/draft-restored status indicators, section/whole-draft reset
with confirmation.
- Extract shared QR/polling/expiry engine from TelegramLoginComponent
(shared/qr-login) and reuse it for a new admin login flow.
- Admin authentication kept fully separate from customer session:
own cookie/localStorage keys, signals, guard, and header interceptor
(core/admin-auth).
- ?login=true / ?adminLogin=true open the respective login dialog for
manual testing.
- Ed25519 challenge/verify interfaces (fail-closed no-op binding) ready
for backend delivery.
- Document autosave/reset/admin-auth/QR-reuse/Ed25519 model and the
remaining full-field-coverage gap in docs/Project-Editor.md.