From f4ea4c7af8b5503d0cc066900561cc07fe4e67fd Mon Sep 17 00:00:00 2001 From: sdarbinyan Date: Thu, 20 Aug 2026 14:23:12 +0400 Subject: [PATCH] fix(api): route tenants through origin gateway --- docs/DEPLOYMENT.md | 6 +- docs/backend/BACKEND-HANDOFF.md | 9 +-- ...enant-api-through-a-same-origin-gateway.md | 51 +++++++++++++++ .../features/platform-vision/FACTS.jsonl | 1 + ngsw-config.json | 4 +- scripts/deploy/add-domain.sh | 10 +++ scripts/deploy/server-setup.sh | 12 ++++ scripts/deploy/setup-wildcard-tls.sh | 8 +++ scripts/deploy/sync-domains.sh | 8 +++ src/app/app.config.ts | 7 +- .../providers/api-bootstrap.provider.spec.ts | 37 +++++++++++ .../providers/api-bootstrap.provider.ts | 10 +-- .../core/config/api-config.service.spec.ts | 64 +++++++++++++++++++ src/app/core/config/api-config.service.ts | 51 +++++---------- .../core/config/tenant-resolver.service.ts | 6 +- src/environments/environment.production.ts | 13 ++-- src/environments/environment.ts | 9 +-- src/index.html | 4 -- 18 files changed, 241 insertions(+), 69 deletions(-) create mode 100644 docs/context/adrs/ADR-0004-route-every-tenant-api-through-a-same-origin-gateway.md create mode 100644 src/app/core/bootstrap/providers/api-bootstrap.provider.spec.ts create mode 100644 src/app/core/config/api-config.service.spec.ts diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md index 29e163a..e824e40 100644 --- a/docs/DEPLOYMENT.md +++ b/docs/DEPLOYMENT.md @@ -1,9 +1,13 @@ # Deployment — server provisioning, CD, TLS -Frontend only. The backend service (`:8080`) is a separate developer's responsibility; nginx already proxies `/api/` to it and will `502` until it exists. +Frontend only. The backend service (`:8080`) is a separate developer's responsibility; nginx proxies the same-origin `/backend/` gateway (and the legacy direct `/api/` route) to it and will `502` until it exists. **Multi-tenant, one bundle.** Every customer domain is served by the same build. The SPA resolves its tenant from the `Host` header ([BACKEND-HANDOFF §1a](backend/BACKEND-HANDOFF.md)). One deploy updates every domain simultaneously — there is no per-tenant build and no per-tenant deploy. +All browser API traffic uses `{origin}/backend/...`. nginx removes `/backend/` +before forwarding and preserves `Host`; do not replace this with a hardcoded API +domain or a direct backend port, because that breaks custom domains and CORS. + --- ## 1. Files diff --git a/docs/backend/BACKEND-HANDOFF.md b/docs/backend/BACKEND-HANDOFF.md index f3a53a5..06ac690 100644 --- a/docs/backend/BACKEND-HANDOFF.md +++ b/docs/backend/BACKEND-HANDOFF.md @@ -10,10 +10,11 @@ Single entry point for a backend developer picking this up cold. Written 2026-08 One deployed bundle serves **every customer domain**. There is no per-tenant build. The chain is: -1. [`TenantResolverService`](../../src/app/core/config/tenant-resolver.service.ts) derives a `tenantKey` from `window.location.hostname` (first label; `www.` skipped; localhost falls back to a configured key). -2. [`ApiConfigService`](../../src/app/core/config/api-config.service.ts) turns that key into the API base URL — via an explicit per-tenant map or a `{tenant}` URL template. -3. `ApiBootstrapProvider` fetches that tenant's **bootstrap config**, which drives branding, theme, locales, currencies, navigation, footer, and which pages exist. -4. nginx is `default_server` / `server_name _`, so any domain pointed at the server IP gets the same bundle and self-resolves. +1. [`TenantResolverService`](../../src/app/core/config/tenant-resolver.service.ts) reads the current browser hostname/origin (localhost still uses the development proxy). +2. [`ApiConfigService`](../../src/app/core/config/api-config.service.ts) resolves one same-origin API base: `{origin}/backend`. +3. `ApiBootstrapProvider`, auth, legacy API calls, and versioned `/api/...` calls all use that same base. +4. nginx owns `/backend/`, strips that prefix, proxies to the backend, and preserves the original `Host` so the backend can resolve the tenant server-side. +5. nginx is `default_server` / `server_name _`, so any domain pointed at the server IP gets the same bundle and self-resolves. **What this means for you:** the bootstrap endpoint is the single most important thing to build after auth. Every request must be tenant-scoped server-side, and a tenant must never be able to read another tenant's data — return `403`, not an empty result (see [TRACK-S §2](TRACK-S-SECURITY-RBAC-CONTRACT.md)). The frontend supplies the tenant identity from the hostname; the backend must treat that as an untrusted hint and derive real scope from the authenticated session. diff --git a/docs/context/adrs/ADR-0004-route-every-tenant-api-through-a-same-origin-gateway.md b/docs/context/adrs/ADR-0004-route-every-tenant-api-through-a-same-origin-gateway.md new file mode 100644 index 0000000..d962cdc --- /dev/null +++ b/docs/context/adrs/ADR-0004-route-every-tenant-api-through-a-same-origin-gateway.md @@ -0,0 +1,51 @@ +--- +id: ADR-0004 +title: Route every tenant API through a same-origin gateway +status: active +date: 2026-08-20 +supersedes: [] +tags: [architecture, multi-tenant, api, routing, nginx] +--- + +# ADR-0004: Route every tenant API through a same-origin gateway + +## Context + +The production bundle embedded `api.dexarmarket.ru` and constructed unknown tenant +URLs as `https://{tenant}.api.dexarmarket.ru:445`. Bootstrap used a different +route (`/bootstrap` on the storefront origin), while auth had its own fixed API +base. A custom domain could therefore use three different backend origins. + +Directly calling the backend port is not a safe fallback: production returned +`403` for both a bootstrap request carrying `Origin: https://gorbushka.market` +and the corresponding CORS preflight. Meanwhile an unmatched `/bootstrap` on +the storefront nginx server fell through to `index.html`, producing a misleading +HTTP 200 with HTML instead of bootstrap JSON. + +## Decision + +Every tenant frontend uses one API base derived at runtime from the browser +origin: `{origin}/backend`. + +- Bootstrap loads from `{origin}/backend/bootstrap`. +- Auth receives the same base through the `AUTH_API_URL` provider. +- Legacy endpoints append their existing paths to the same base. +- Versioned `/api/...` endpoints retain the `/api` prefix when routed. +- nginx owns `/backend/`, removes that prefix when proxying, and forwards the + original `Host`, `X-Forwarded-For`, and `X-Forwarded-Proto` headers upstream. + +The frontend contains no tenant/domain allowlist and no production backend +hostname. Tenant selection remains a server-side responsibility based on the +verified forwarded host. + +## Consequences + +Browser traffic is same-origin, so custom domains do not require per-tenant CORS +configuration and one bundle works for every attached domain. Bootstrap, auth, +legacy routes, and versioned routes cannot silently drift to different hosts. + +Every nginx tenant/catch-all configuration must include the `/backend/` gateway. +Deploy verification must check that `/backend/bootstrap` returns JSON rather +than accepting a generic HTTP 200 from the SPA fallback. The upstream must still +reject unknown hosts; the gateway preserves `Host` but does not authenticate a +tenant by itself. diff --git a/docs/context/features/platform-vision/FACTS.jsonl b/docs/context/features/platform-vision/FACTS.jsonl index e52d397..8a11ea7 100644 --- a/docs/context/features/platform-vision/FACTS.jsonl +++ b/docs/context/features/platform-vision/FACTS.jsonl @@ -11,3 +11,4 @@ {"id":"PV-20260818T104300Z-b3c4","subject":"RoutingContext","predicate":"is-required-on","object":"CheckoutSession, PaymentIntent, Payment, Refund and ReconciliationRecord; frozen at checkout-session creation and immutable thereafter, so a payment is always attributable to exactly one payment point","src":["docs/backend/PHASE-1-MONEY-FX-PAYMENTS-CONTRACT.md","docs/backend/PHASE-7-PAYMENTS-RECONCILIATION-CONTRACT.md"],"status":"active","kind":"constraint","updated_at":"2026-08-18T10:43:00Z","confidence":"high","tags":["payments","reconciliation","contract"]} {"id":"PV-20260818T104400Z-d9e2","subject":"partner-api-credentials","predicate":"are-scoped-by","object":"a single node whose subtree defines authority; we hold only the partner-generated public key, rotation runs on a bounded overlap window and revocation is immediate and irreversible","src":["docs/backend/PARTNER-PROVISIONING-API-CONTRACT.md","docs/backend/TRACK-S-SECURITY-RBAC-CONTRACT.md"],"status":"active","kind":"decision","updated_at":"2026-08-18T10:44:00Z","confidence":"high","tags":["security","credentials","partner"]} {"id":"PV-20260818T104500Z-a6f7","subject":"checkout-payment-methods","predicate":"already-support","object":"both qr and card end to end in src/app/pages/cart/cart.component.ts (separate create paths and separate status pollers); card is not an outstanding gap","src":["src/app/pages/cart/cart.component.ts","src/app/services/api.service.ts"],"status":"active","kind":"implemented","updated_at":"2026-08-18T10:45:00Z","confidence":"high","tags":["payments","frontend"]} +{"id":"PV-20260820T095500Z-b17e","subject":"tenant-api-routing","predicate":"is-decided-to-use","object":"one same-origin {origin}/backend gateway for bootstrap, auth, legacy endpoints, and versioned /api endpoints; nginx strips /backend and preserves the original Host for server-side tenant resolution","src":["docs/context/adrs/ADR-0004-route-every-tenant-api-through-a-same-origin-gateway.md","src/app/core/config/api-config.service.ts","scripts/deploy/server-setup.sh"],"status":"active","kind":"decision","updated_at":"2026-08-20T09:55:00Z","confidence":"high","tags":["architecture","multi-tenant","api","routing","nginx"]} diff --git a/ngsw-config.json b/ngsw-config.json index 169eb8b..a949b9a 100644 --- a/ngsw-config.json +++ b/ngsw-config.json @@ -30,9 +30,7 @@ { "name": "api-cache", "urls": [ - "/api/**", - "https://api.dexarmarket.ru:445/**", - "https://api.novo.market:444/**" + "/api/**" ], "cacheConfig": { "maxSize": 100, diff --git a/scripts/deploy/add-domain.sh b/scripts/deploy/add-domain.sh index 839be8e..606e18f 100755 --- a/scripts/deploy/add-domain.sh +++ b/scripts/deploy/add-domain.sh @@ -87,6 +87,16 @@ server { proxy_read_timeout 60s; } + location /backend/ { + proxy_pass http://127.0.0.1:8080/; + proxy_http_version 1.1; + proxy_set_header Host \$host; + proxy_set_header X-Real-IP \$remote_addr; + proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto \$scheme; + proxy_read_timeout 60s; + } + location / { try_files \$uri \$uri/ /index.html; } diff --git a/scripts/deploy/server-setup.sh b/scripts/deploy/server-setup.sh index b434655..10a3606 100755 --- a/scripts/deploy/server-setup.sh +++ b/scripts/deploy/server-setup.sh @@ -104,6 +104,18 @@ server { proxy_read_timeout 60s; } + # One same-origin gateway for bootstrap, auth, legacy and versioned APIs. + # The trailing slash removes /backend/ before forwarding upstream. + location /backend/ { + proxy_pass http://127.0.0.1:8080/; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_read_timeout 60s; + } + # SPA fallback. Must stay last: every unmatched path is a client route. location / { try_files $uri $uri/ /index.html; diff --git a/scripts/deploy/setup-wildcard-tls.sh b/scripts/deploy/setup-wildcard-tls.sh index 08cab62..be1e01a 100644 --- a/scripts/deploy/setup-wildcard-tls.sh +++ b/scripts/deploy/setup-wildcard-tls.sh @@ -119,6 +119,14 @@ server { proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto \$scheme; } + location /backend/ { + proxy_pass http://127.0.0.1:8080/; + proxy_http_version 1.1; + proxy_set_header Host \$host; + proxy_set_header X-Real-IP \$remote_addr; + proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto \$scheme; + } location / { try_files \$uri \$uri/ /index.html; } add_header Strict-Transport-Security "max-age=31536000" always; diff --git a/scripts/deploy/sync-domains.sh b/scripts/deploy/sync-domains.sh index 215f68a..ed6dfba 100644 --- a/scripts/deploy/sync-domains.sh +++ b/scripts/deploy/sync-domains.sh @@ -150,6 +150,14 @@ server { proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto \$scheme; } + location /backend/ { + proxy_pass http://127.0.0.1:8080/; + proxy_http_version 1.1; + proxy_set_header Host \$host; + proxy_set_header X-Real-IP \$remote_addr; + proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto \$scheme; + } location / { try_files \$uri \$uri/ /index.html; } add_header X-Content-Type-Options "nosniff" always; diff --git a/src/app/app.config.ts b/src/app/app.config.ts index e79fd57..45f82b7 100644 --- a/src/app/app.config.ts +++ b/src/app/app.config.ts @@ -12,6 +12,7 @@ import { adminAuthHeadersInterceptor, Ed25519VerificationService, NoopEd25519Ver import { provideServiceWorker } from '@angular/service-worker'; import { MediaRepository } from './core/media/media-repository'; import { MockMediaRepository } from './core/media/mock-media-repository.service'; +import { ApiConfigService } from './core/config/api-config.service'; import { environment } from '../environments/environment'; export const appConfig: ApplicationConfig = { @@ -27,7 +28,11 @@ export const appConfig: ApplicationConfig = { // other interceptor has run, and normalizes whatever actually came back. withInterceptors([mockDataInterceptor, apiBaseUrlInterceptor, apiHeadersInterceptor, adminAuthHeadersInterceptor, cacheInterceptor, apiErrorInterceptor]) ), - { provide: AUTH_API_URL, useValue: environment.authApiUrl }, + { + provide: AUTH_API_URL, + useFactory: (apiConfig: ApiConfigService) => apiConfig.getBaseUrl(), + deps: [ApiConfigService] + }, { provide: TELEGRAM_BOT_USERNAME, useValue: environment.telegramBot }, // useFactory, not useClass: @marketplaces/auth ships plain tsc output, not // Angular Package Format, so it carries no baked-in Ivy DI metadata for diff --git a/src/app/core/bootstrap/providers/api-bootstrap.provider.spec.ts b/src/app/core/bootstrap/providers/api-bootstrap.provider.spec.ts new file mode 100644 index 0000000..bef3a4a --- /dev/null +++ b/src/app/core/bootstrap/providers/api-bootstrap.provider.spec.ts @@ -0,0 +1,37 @@ +import { provideHttpClient } from '@angular/common/http'; +import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing'; +import { TestBed } from '@angular/core/testing'; +import { ApiConfigService } from '../../config/api-config.service'; +import { ApiBootstrapProvider } from './api-bootstrap.provider'; + +describe('ApiBootstrapProvider', () => { + let provider: ApiBootstrapProvider; + let httpTesting: HttpTestingController; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + ApiBootstrapProvider, + provideHttpClient(), + provideHttpClientTesting(), + { + provide: ApiConfigService, + useValue: { getBaseUrl: () => 'https://gorbushka.market/backend' } + } + ] + }); + + provider = TestBed.inject(ApiBootstrapProvider); + httpTesting = TestBed.inject(HttpTestingController); + }); + + afterEach(() => httpTesting.verify()); + + it('loads bootstrap from the same tenant API base as every other request', () => { + provider.loadBootstrap().subscribe(); + + const request = httpTesting.expectOne('https://gorbushka.market/backend/bootstrap'); + expect(request.request.method).toBe('GET'); + request.flush({}); + }); +}); diff --git a/src/app/core/bootstrap/providers/api-bootstrap.provider.ts b/src/app/core/bootstrap/providers/api-bootstrap.provider.ts index eaddd0b..1e1ecaf 100644 --- a/src/app/core/bootstrap/providers/api-bootstrap.provider.ts +++ b/src/app/core/bootstrap/providers/api-bootstrap.provider.ts @@ -3,14 +3,16 @@ import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; import { BootstrapConfig } from '../../../shared/models/config'; import { ConfigProvider } from '../../config/config-provider.interface'; +import { ApiConfigService } from '../../config/api-config.service'; @Injectable({ providedIn: 'root' }) export class ApiBootstrapProvider implements ConfigProvider { - private readonly bootstrapUrl = '/bootstrap'; - - constructor(private readonly http: HttpClient) {} + constructor( + private readonly http: HttpClient, + private readonly apiConfig: ApiConfigService + ) {} loadBootstrap(): Observable { - return this.http.get(this.bootstrapUrl); + return this.http.get(`${this.apiConfig.getBaseUrl()}/bootstrap`); } } diff --git a/src/app/core/config/api-config.service.spec.ts b/src/app/core/config/api-config.service.spec.ts new file mode 100644 index 0000000..4a1c18b --- /dev/null +++ b/src/app/core/config/api-config.service.spec.ts @@ -0,0 +1,64 @@ +import { TestBed } from '@angular/core/testing'; +import { ApiConfigService } from './api-config.service'; +import { TenantResolverService } from './tenant-resolver.service'; + +describe('ApiConfigService', () => { + let service: ApiConfigService; + let tenantResolver: jasmine.SpyObj; + + beforeEach(() => { + tenantResolver = jasmine.createSpyObj( + 'TenantResolverService', + ['getHostname', 'getOrigin', 'getTenantKey', 'isLocalhost'] + ); + tenantResolver.getTenantKey.and.returnValue('gorbushka'); + tenantResolver.getOrigin.and.returnValue('https://gorbushka.market'); + tenantResolver.isLocalhost.and.returnValue(false); + + TestBed.configureTestingModule({ + providers: [ + ApiConfigService, + { provide: TenantResolverService, useValue: tenantResolver } + ] + }); + + service = TestBed.inject(ApiConfigService); + }); + + it('uses the current customer hostname for the production API base URL', () => { + tenantResolver.getHostname.and.returnValue('gorbushka.market'); + + expect(service.getBaseUrl()).toBe('https://gorbushka.market/backend'); + }); + + it('keeps www traffic on the same browser origin', () => { + tenantResolver.getHostname.and.returnValue('www.gorbushka.market'); + tenantResolver.getOrigin.and.returnValue('https://www.gorbushka.market'); + + expect(service.getBaseUrl()).toBe('https://www.gorbushka.market/backend'); + }); + + it('preserves the API namespace when targeting a tenant backend', () => { + tenantResolver.getHostname.and.returnValue('gorbushka.market'); + + expect(service.toApiUrl('/api/v2/storefront/cart')) + .toBe('https://gorbushka.market/backend/api/v2/storefront/cart'); + }); + + it('does not duplicate the API prefix for localhost proxy requests', () => { + tenantResolver.getHostname.and.returnValue('localhost'); + tenantResolver.getOrigin.and.returnValue('http://localhost:4200'); + tenantResolver.getTenantKey.and.returnValue('default'); + tenantResolver.isLocalhost.and.returnValue(true); + + expect(service.toApiUrl('/api/v2/storefront/cart')).toBe('/api/v2/storefront/cart'); + }); + + it('leaves absolute and non-API URLs unchanged', () => { + tenantResolver.getHostname.and.returnValue('gorbushka.market'); + + expect(service.toApiUrl('https://cdn.example.com/image.png')) + .toBe('https://cdn.example.com/image.png'); + expect(service.toApiUrl('/assets/config.json')).toBe('/assets/config.json'); + }); +}); diff --git a/src/app/core/config/api-config.service.ts b/src/app/core/config/api-config.service.ts index 1bc3646..00dd4b9 100644 --- a/src/app/core/config/api-config.service.ts +++ b/src/app/core/config/api-config.service.ts @@ -1,16 +1,16 @@ import { Injectable, inject } from '@angular/core'; import { environment } from '../../../environments/environment'; -import { ConfigService } from './config.service'; import { TenantResolverService } from './tenant-resolver.service'; @Injectable({ providedIn: 'root' }) export class ApiConfigService { private readonly tenantResolver = inject(TenantResolverService); - private readonly configService = inject(ConfigService); getBaseUrl(): string { + const hostname = this.tenantResolver.getHostname(); + const apiHostname = hostname.startsWith('www.') ? hostname.slice(4) : hostname; + const origin = this.tenantResolver.getOrigin(); const tenantKey = this.tenantResolver.getTenantKey(); - const bootstrapUrl = this.resolveBootstrapApiBaseUrl(); const tenantMap = (environment as any).tenantApiBaseUrls as Record | undefined; const localhostUrl = (environment as any).localhostApiUrl as string | undefined; const apiTemplate = (environment as any).tenantApiTemplate as string | undefined; @@ -19,13 +19,15 @@ export class ApiConfigService { if (this.tenantResolver.isLocalhost() && localhostUrl) { url = localhostUrl; + } else if (tenantMap?.[hostname] || tenantMap?.[apiHostname]) { + url = tenantMap[hostname] ?? tenantMap[apiHostname]; } else if (tenantMap?.[tenantKey]) { url = tenantMap[tenantKey]; - } else if (apiTemplate) { - url = apiTemplate.replace('{tenant}', tenantKey); - } else if (bootstrapUrl) { - // Bootstrap API override is opt-in and only for absolute URLs. - url = bootstrapUrl; + } else if (apiTemplate && origin) { + url = apiTemplate + .replace('{origin}', origin) + .replace('{hostname}', apiHostname) + .replace('{tenant}', tenantKey); } return this.normalizeBaseUrl(url); @@ -54,36 +56,15 @@ export class ApiConfigService { } const baseUrl = this.getBaseUrl(); - const path = url.slice('/api'.length); - return `${baseUrl}${path.startsWith('/') ? path : `/${path}`}`; - } - - private resolveBootstrapApiBaseUrl(): string | null { - const allowBootstrapApiOverride = (environment as any).allowBootstrapApiOverride === true; - if (!allowBootstrapApiOverride) { - return null; + if (baseUrl === '/') { + return url; } - const bootstrap = this.configService.getBootstrapSnapshot() as any; - if (!bootstrap) { - return null; + if (baseUrl === '/api' || baseUrl.endsWith('/api')) { + return `${baseUrl}${url.slice('/api'.length)}`; } - const endpointBase = bootstrap?.apiEndpoints?.website?.baseUrl; - if (typeof endpointBase === 'string' && this.isAbsoluteHttpUrl(endpointBase)) { - return endpointBase; - } - - const tenantBase = bootstrap?.tenant?.apiBaseUrl; - if (typeof tenantBase === 'string' && this.isAbsoluteHttpUrl(tenantBase)) { - return tenantBase; - } - - return null; - } - - private isAbsoluteHttpUrl(url: string): boolean { - return /^https?:\/\//i.test(url.trim()); + return `${baseUrl}${url}`; } private normalizeBaseUrl(url: string): string { @@ -93,4 +74,4 @@ export class ApiConfigService { return url.replace(/\/+$/, ''); } -} \ No newline at end of file +} diff --git a/src/app/core/config/tenant-resolver.service.ts b/src/app/core/config/tenant-resolver.service.ts index dec81aa..9b5fc69 100644 --- a/src/app/core/config/tenant-resolver.service.ts +++ b/src/app/core/config/tenant-resolver.service.ts @@ -11,6 +11,10 @@ export class TenantResolverService { return host.toLowerCase(); } + getOrigin(): string { + return this.document?.location?.origin ?? ''; + } + isLocalhost(): boolean { const hostname = this.getHostname(); return hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '::1'; @@ -33,4 +37,4 @@ export class TenantResolverService { return segments[0]; } -} \ No newline at end of file +} diff --git a/src/environments/environment.production.ts b/src/environments/environment.production.ts index 9712a0f..792ce99 100644 --- a/src/environments/environment.production.ts +++ b/src/environments/environment.production.ts @@ -3,18 +3,13 @@ export const environment = { production: true, useMockBootstrapOnLocal: false, fallbackTenantKey: 'default', - allowBootstrapApiOverride: false, - localhostApiUrl: 'https://api.dexarmarket.ru:445', - tenantApiTemplate: 'https://{tenant}.api.dexarmarket.ru:445', - tenantApiBaseUrls: { - default: 'https://api.dexarmarket.ru:445', - dexarmarket: 'https://api.dexarmarket.ru:445' - }, + localhostApiUrl: '/api', + tenantApiTemplate: '{origin}/backend', + tenantApiBaseUrls: {}, brandName: 'Marketplace', brandFullName: 'Marketplace', theme: 'dexar', - apiUrl: 'https://api.dexarmarket.ru:445', - authApiUrl: 'https://api.dexarmarket.ru:445', + apiUrl: '/api', qrApiUrl: 'https://qr.vitanova.network/api', logo: '/icons/icon-192x192.png', contactEmail: 'info@dexarmarket.ru', diff --git a/src/environments/environment.ts b/src/environments/environment.ts index 59f5be7..d5642b1 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -4,18 +4,13 @@ export const environment = { useMockData: false, // Toggle to test with backOffice mock data useMockBootstrapOnLocal: true, fallbackTenantKey: 'default', - allowBootstrapApiOverride: false, localhostApiUrl: '/api', - tenantApiTemplate: 'https://{tenant}.api.dexarmarket.ru:445', - tenantApiBaseUrls: { - default: 'https://api.dexarmarket.ru:445', - dexarmarket: 'https://api.dexarmarket.ru:445' - }, + tenantApiTemplate: '{origin}/backend', + tenantApiBaseUrls: {}, brandName: 'Marketplace', brandFullName: 'Marketplace', theme: 'dexar', apiUrl: '/api', - authApiUrl: 'https://api.dexarmarket.ru:445', qrApiUrl: 'https://qr.vitanova.network/api', logo: '/icons/icon-192x192.png', contactEmail: 'info@dexarmarket.ru', diff --git a/src/index.html b/src/index.html index fe59c34..3f47041 100644 --- a/src/index.html +++ b/src/index.html @@ -43,10 +43,6 @@ - - - -