@oktis-works/web 0.1.3 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +8 -23
- package/dist/index.js.map +1 -1
- package/dist/tenant-resolver.d.ts +14 -0
- package/dist/tenant-resolver.d.ts.map +1 -0
- package/dist/tenant-resolver.js +49 -0
- package/dist/tenant-resolver.js.map +1 -0
- package/dist/tenant-resolver.test.d.ts +2 -0
- package/dist/tenant-resolver.test.d.ts.map +1 -0
- package/dist/tenant-resolver.test.js +92 -0
- package/dist/tenant-resolver.test.js.map +1 -0
- package/package.json +8 -8
package/dist/index.js
CHANGED
|
@@ -8,6 +8,7 @@ import { bootstrap } from '@oktis-works/core';
|
|
|
8
8
|
import { establishTenantContext } from '@oktis-works/core';
|
|
9
9
|
import { getConnection } from '@oktis-works/database';
|
|
10
10
|
import { createRouter, wireThemeHooks } from './rendering.js';
|
|
11
|
+
import { resolveTenantFromHost } from './tenant-resolver.js';
|
|
11
12
|
import { setCurrentContent } from '@oktis-works/theme-sdk';
|
|
12
13
|
async function main() {
|
|
13
14
|
const lifecycle = await bootstrap();
|
|
@@ -16,31 +17,15 @@ async function main() {
|
|
|
16
17
|
const activeTheme = process.env['ACTIVE_THEME'] ?? 'default';
|
|
17
18
|
const router = createRouter(themesRoot, activeTheme);
|
|
18
19
|
const app = new Hono();
|
|
19
|
-
// Tenant resolution middleware
|
|
20
|
+
// Tenant resolution middleware (loopback → default; senão subdomain/domain)
|
|
20
21
|
app.use('*', async (c, next) => {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
// Try subdomain resolution first
|
|
25
|
-
const subdomain = host.split('.')[0];
|
|
26
|
-
if (subdomain && subdomain !== 'www' && subdomain !== 'api') {
|
|
27
|
-
const tenants = await sql.unsafe('SELECT * FROM tenants WHERE subdomain = $1 AND status = $2', [subdomain, 'ACTIVE']);
|
|
28
|
-
if (tenants.length > 0) {
|
|
29
|
-
const tenant = tenants[0];
|
|
30
|
-
await establishTenantContext(tenant['id']);
|
|
31
|
-
c.set('tenant', tenant);
|
|
32
|
-
return next();
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
// Try domain resolution
|
|
36
|
-
const tenants = await sql.unsafe('SELECT * FROM tenants WHERE domain = $1 AND status = $2', [host, 'ACTIVE']);
|
|
37
|
-
if (tenants.length > 0) {
|
|
38
|
-
const tenant = tenants[0];
|
|
39
|
-
await establishTenantContext(tenant['id']);
|
|
40
|
-
c.set('tenant', tenant);
|
|
41
|
-
return next();
|
|
22
|
+
const tenant = await resolveTenantFromHost(c.req.header('host') ?? '');
|
|
23
|
+
if (!tenant) {
|
|
24
|
+
return c.json({ error: 'Tenant not found' }, 404);
|
|
42
25
|
}
|
|
43
|
-
|
|
26
|
+
await establishTenantContext(tenant['id']);
|
|
27
|
+
c.set('tenant', tenant);
|
|
28
|
+
return next();
|
|
44
29
|
});
|
|
45
30
|
// Health check
|
|
46
31
|
app.get('/health', async (c) => {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,0CAA0C;AAC1C,2CAA2C;AAE3C,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAM3D,KAAK,UAAU,IAAI;IACjB,MAAM,SAAS,GAAG,MAAM,SAAS,EAAE,CAAC;IAEpC,cAAc,EAAE,CAAC;IAEjB,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,QAAQ,CAAC,CAAC;IAC9E,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,SAAS,CAAC;IAC7D,MAAM,MAAM,GAAG,YAAY,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;IAErD,MAAM,GAAG,GAAG,IAAI,IAAI,EAA4B,CAAC;IAEjD
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,0CAA0C;AAC1C,2CAA2C;AAE3C,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAC9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAM3D,KAAK,UAAU,IAAI;IACjB,MAAM,SAAS,GAAG,MAAM,SAAS,EAAE,CAAC;IAEpC,cAAc,EAAE,CAAC;IAEjB,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,QAAQ,CAAC,CAAC;IAC9E,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,SAAS,CAAC;IAC7D,MAAM,MAAM,GAAG,YAAY,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;IAErD,MAAM,GAAG,GAAG,IAAI,IAAI,EAA4B,CAAC;IAEjD,4EAA4E;IAC5E,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE;QAC7B,MAAM,MAAM,GAAG,MAAM,qBAAqB,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;QAEvE,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,kBAAkB,EAAE,EAAE,GAAG,CAAC,CAAC;QACpD,CAAC;QAED,MAAM,sBAAsB,CAAC,MAAM,CAAC,IAAI,CAAW,CAAC,CAAC;QACrD,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QACxB,OAAO,IAAI,EAAE,CAAC;IAChB,CAAC,CAAC,CAAC;IAEH,eAAe;IACf,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QAC7B,OAAO,CAAC,CAAC,IAAI,CAAC;YACZ,MAAM,EAAE,SAAS;YACjB,OAAO,EAAE,KAAK;YACd,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACpC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,iEAAiE;IACjE,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QACvB,MAAM,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC;QAExB,IAAI,CAAC;YACH,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YAE9C,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC;gBACxB,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC;YACtB,CAAC;YAED,IAAI,UAAU,CAAC,IAAI,IAAI,UAAU,CAAC,WAAW,EAAE,CAAC;gBAC9C,MAAM,IAAI,GAAG,MAAM,aAAa,EAAE,CAAC,MAAM,CACvC,sFAAsF,EACtF,CAAC,UAAU,CAAC,IAAI,EAAE,UAAU,CAAC,WAAW,CAAC,CAC1C,CAAC;gBACF,MAAM,GAAG,GAAI,IAAkD,CAAC,CAAC,CAAC,CAAC;gBACnE,IAAI,GAAG;oBAAE,iBAAiB,CAAC,GAAG,CAAC,CAAC;YAClC,CAAC;YAED,MAAM,UAAU,GAAG,CAAC,KAAa,EAAU,EAAE,CAC3C,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YACnG,MAAM,QAAQ,GAAG,UAAU,CAAC,UAAU,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC;YACvD,MAAM,WAAW,GAAG,UAAU,CAAC,UAAU,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;YAC7D,MAAM,SAAS,GAAG,UAAU,CAAC,WAAW,CAAC,CAAC;YAE1C,OAAO,CAAC,CAAC,IAAI,CAAC;;;;;;yCAMqB,SAAS,sCAAsC,SAAS;;;sBAG3E,SAAS,oBAAoB,QAAQ,gBAAgB,WAAW;gEACtB,SAAS;;;QAGjE,CAAC,CAAC;QACN,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,+BAA+B,EAAE,KAAK,CAAC,CAAC;YACtD,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,uBAAuB,EAAE,EAAE,GAAG,CAAC,CAAC;QACzD,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,eAAe;IACf,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC,CAAC;IAErD,KAAK,CACH;QACE,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,IAAI;KACL,EACD,CAAC,IAAI,EAAE,EAAE;QACP,OAAO,CAAC,GAAG,CAAC,yCAAyC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IACpE,CAAC,CACF,CAAC;IAEF,oBAAoB;IACpB,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,KAAK,IAAI,EAAE;QAC/B,OAAO,CAAC,GAAG,CAAC,oCAAoC,CAAC,CAAC;QAClD,MAAM,SAAS,CAAC,IAAI,EAAE,CAAC;QACvB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;IAEH,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE;QAC9B,OAAO,CAAC,GAAG,CAAC,mCAAmC,CAAC,CAAC;QACjD,MAAM,SAAS,CAAC,IAAI,EAAE,CAAC;QACvB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,6BAA6B,EAAE,KAAK,CAAC,CAAC;IACpD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
type TenantRow = Record<string, unknown>;
|
|
2
|
+
/**
|
|
3
|
+
* Resolve o tenant ativo a partir do header Host.
|
|
4
|
+
*
|
|
5
|
+
* - Normaliza `host:porta` (localhost:3002 → localhost) — domínios nunca guardam porta.
|
|
6
|
+
* - Loopback (localhost / 127.x / ::1) usa o tenant `default`: dev zero-config
|
|
7
|
+
* sem configurar domínio no banco.
|
|
8
|
+
* - Demais hosts: subdomain (apenas host com ponto, ignorando www/api) → domain exato.
|
|
9
|
+
*
|
|
10
|
+
* Retorna `null` quando nenhum tenant casa (→ 404 "Tenant not found").
|
|
11
|
+
*/
|
|
12
|
+
export declare function resolveTenantFromHost(rawHost: string): Promise<TenantRow | null>;
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=tenant-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tenant-resolver.d.ts","sourceRoot":"","sources":["../src/tenant-resolver.ts"],"names":[],"mappings":"AAIA,KAAK,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEzC;;;;;;;;;GASG;AACH,wBAAsB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAsCtF"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// @oktis-works/web - Resolução de tenant a partir do header Host
|
|
2
|
+
import { getConnection } from '@oktis-works/database';
|
|
3
|
+
/**
|
|
4
|
+
* Resolve o tenant ativo a partir do header Host.
|
|
5
|
+
*
|
|
6
|
+
* - Normaliza `host:porta` (localhost:3002 → localhost) — domínios nunca guardam porta.
|
|
7
|
+
* - Loopback (localhost / 127.x / ::1) usa o tenant `default`: dev zero-config
|
|
8
|
+
* sem configurar domínio no banco.
|
|
9
|
+
* - Demais hosts: subdomain (apenas host com ponto, ignorando www/api) → domain exato.
|
|
10
|
+
*
|
|
11
|
+
* Retorna `null` quando nenhum tenant casa (→ 404 "Tenant not found").
|
|
12
|
+
*/
|
|
13
|
+
export async function resolveTenantFromHost(rawHost) {
|
|
14
|
+
const host = rawHost.replace(/:\d+$/, '');
|
|
15
|
+
const sql = getConnection();
|
|
16
|
+
const isLoopback = host === 'localhost' || host === '::1' || host === '[::1]' || /^127\./.test(host);
|
|
17
|
+
if (isLoopback) {
|
|
18
|
+
// Dev zero-config: tenant padrão criado no primeiro db:migrate
|
|
19
|
+
const defaults = await sql.unsafe('SELECT * FROM tenants WHERE slug = $1 AND status = $2', [
|
|
20
|
+
'default',
|
|
21
|
+
'ACTIVE',
|
|
22
|
+
]);
|
|
23
|
+
if (defaults.length > 0)
|
|
24
|
+
return defaults[0];
|
|
25
|
+
// Sem tenant default: aceita domain 'localhost' configurado manualmente
|
|
26
|
+
const byDomain = await sql.unsafe('SELECT * FROM tenants WHERE domain = $1 AND status = $2', [
|
|
27
|
+
host,
|
|
28
|
+
'ACTIVE',
|
|
29
|
+
]);
|
|
30
|
+
return byDomain.length > 0 ? byDomain[0] : null;
|
|
31
|
+
}
|
|
32
|
+
// Subdomain primeiro (apenas host com ponto: loja.exemplo.com)
|
|
33
|
+
const subdomain = host.split('.')[0];
|
|
34
|
+
if (host.includes('.') && subdomain && subdomain !== 'www' && subdomain !== 'api') {
|
|
35
|
+
const tenants = await sql.unsafe('SELECT * FROM tenants WHERE subdomain = $1 AND status = $2', [
|
|
36
|
+
subdomain,
|
|
37
|
+
'ACTIVE',
|
|
38
|
+
]);
|
|
39
|
+
if (tenants.length > 0)
|
|
40
|
+
return tenants[0];
|
|
41
|
+
}
|
|
42
|
+
// Domain exato (já sem porta)
|
|
43
|
+
const tenants = await sql.unsafe('SELECT * FROM tenants WHERE domain = $1 AND status = $2', [
|
|
44
|
+
host,
|
|
45
|
+
'ACTIVE',
|
|
46
|
+
]);
|
|
47
|
+
return tenants.length > 0 ? tenants[0] : null;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=tenant-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tenant-resolver.js","sourceRoot":"","sources":["../src/tenant-resolver.ts"],"names":[],"mappings":"AAAA,iEAAiE;AAEjE,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAItD;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,OAAe;IACzD,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAC1C,MAAM,GAAG,GAAG,aAAa,EAAE,CAAC;IAE5B,MAAM,UAAU,GAAG,IAAI,KAAK,WAAW,IAAI,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAErG,IAAI,UAAU,EAAE,CAAC;QACf,+DAA+D;QAC/D,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,uDAAuD,EAAE;YACzF,SAAS;YACT,QAAQ;SACT,CAAC,CAAC;QACH,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,QAAQ,CAAC,CAAC,CAAc,CAAC;QAEzD,wEAAwE;QACxE,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,yDAAyD,EAAE;YAC3F,IAAI;YACJ,QAAQ;SACT,CAAC,CAAC;QACH,OAAO,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAE,QAAQ,CAAC,CAAC,CAAe,CAAC,CAAC,CAAC,IAAI,CAAC;IACjE,CAAC;IAED,+DAA+D;IAC/D,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACrC,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,SAAS,IAAI,SAAS,KAAK,KAAK,IAAI,SAAS,KAAK,KAAK,EAAE,CAAC;QAClF,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,4DAA4D,EAAE;YAC7F,SAAS;YACT,QAAQ;SACT,CAAC,CAAC;QACH,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,OAAO,CAAC,CAAC,CAAc,CAAC;IACzD,CAAC;IAED,8BAA8B;IAC9B,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,yDAAyD,EAAE;QAC1F,IAAI;QACJ,QAAQ;KACT,CAAC,CAAC;IACH,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAE,OAAO,CAAC,CAAC,CAAe,CAAC,CAAC,CAAC,IAAI,CAAC;AAC/D,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tenant-resolver.test.d.ts","sourceRoot":"","sources":["../src/tenant-resolver.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// @oktis-works/web - Resolução de tenant a partir do Host
|
|
2
|
+
// localhost:PORTA precisa cair no tenant default (dev zero-config) e
|
|
3
|
+
// host:PORTA precisa casar com domain exato (produção atrás de porta).
|
|
4
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
5
|
+
let queue;
|
|
6
|
+
const calls = [];
|
|
7
|
+
vi.mock('@oktis-works/database', () => ({
|
|
8
|
+
getConnection: vi.fn(() => ({
|
|
9
|
+
unsafe: vi.fn(async (text, values) => {
|
|
10
|
+
calls.push({ text, values });
|
|
11
|
+
return queue.shift() ?? [];
|
|
12
|
+
}),
|
|
13
|
+
})),
|
|
14
|
+
}));
|
|
15
|
+
import { resolveTenantFromHost } from './tenant-resolver.js';
|
|
16
|
+
const defaultTenant = { id: 'eeb2ded0-14bb-4eb8-8505-f2a678943ebb', slug: 'default', status: 'ACTIVE' };
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
queue = [];
|
|
19
|
+
calls.length = 0;
|
|
20
|
+
});
|
|
21
|
+
describe('resolveTenantFromHost — loopback (dev zero-config)', () => {
|
|
22
|
+
it('localhost:PORTA usa o tenant default (porta é normalizada)', async () => {
|
|
23
|
+
queue = [[defaultTenant]];
|
|
24
|
+
const tenant = await resolveTenantFromHost('localhost:3002');
|
|
25
|
+
expect(tenant?.['slug']).toBe('default');
|
|
26
|
+
expect(calls[0]?.values).toEqual(['default', 'ACTIVE']);
|
|
27
|
+
expect(calls[0]?.text).toContain('WHERE slug =');
|
|
28
|
+
});
|
|
29
|
+
it('localhost sem porta também usa o default', async () => {
|
|
30
|
+
queue = [[defaultTenant]];
|
|
31
|
+
expect(await resolveTenantFromHost('localhost')).not.toBeNull();
|
|
32
|
+
});
|
|
33
|
+
it('127.0.0.1 é loopback (não tenta subdomain "127")', async () => {
|
|
34
|
+
queue = [[defaultTenant]];
|
|
35
|
+
await resolveTenantFromHost('127.0.0.1:3002');
|
|
36
|
+
expect(calls).toHaveLength(1);
|
|
37
|
+
expect(calls[0]?.text).toContain('WHERE slug =');
|
|
38
|
+
});
|
|
39
|
+
it('sem default: cai no domain exato configurado (localhost)', async () => {
|
|
40
|
+
queue = [[], [{ ...defaultTenant, domain: 'localhost' }]];
|
|
41
|
+
const tenant = await resolveTenantFromHost('localhost:3002');
|
|
42
|
+
expect(tenant?.['domain']).toBe('localhost');
|
|
43
|
+
expect(calls[1]?.values).toEqual(['localhost', 'ACTIVE']);
|
|
44
|
+
});
|
|
45
|
+
it('loopback sem nenhum tenant compatível retorna null (404)', async () => {
|
|
46
|
+
queue = [[], []];
|
|
47
|
+
expect(await resolveTenantFromHost('localhost:3002')).toBeNull();
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
describe('resolveTenantFromHost — produção (domain/subdomain)', () => {
|
|
51
|
+
it('host com porta casa com domain exato sem a porta', async () => {
|
|
52
|
+
const row = { id: 't1', domain: 'exemplo.com', status: 'ACTIVE' };
|
|
53
|
+
queue = [[], [row]];
|
|
54
|
+
const tenant = await resolveTenantFromHost('exemplo.com:8080');
|
|
55
|
+
expect(tenant?.['domain']).toBe('exemplo.com');
|
|
56
|
+
// subdomain lookup veio primeiro ('exemplo') e não casou; domain recebe host limpo
|
|
57
|
+
expect(calls[1]?.values).toEqual(['exemplo.com', 'ACTIVE']);
|
|
58
|
+
});
|
|
59
|
+
it('subdomain resolve antes do domain', async () => {
|
|
60
|
+
const row = { id: 't2', subdomain: 'loja', status: 'ACTIVE' };
|
|
61
|
+
queue = [[row]];
|
|
62
|
+
const tenant = await resolveTenantFromHost('loja.exemplo.com');
|
|
63
|
+
expect(tenant?.['subdomain']).toBe('loja');
|
|
64
|
+
expect(calls[0]?.text).toContain('WHERE subdomain =');
|
|
65
|
+
expect(calls[0]?.values).toEqual(['loja', 'ACTIVE']);
|
|
66
|
+
expect(calls).toHaveLength(1);
|
|
67
|
+
});
|
|
68
|
+
it('www e api não são subdomain (vão direto pro domain)', async () => {
|
|
69
|
+
queue = [[{ id: 't3', domain: 'www.exemplo.com' }], [{ id: 't4', domain: 'api.exemplo.com' }]];
|
|
70
|
+
const www = await resolveTenantFromHost('www.exemplo.com');
|
|
71
|
+
expect(www?.['domain']).toBe('www.exemplo.com');
|
|
72
|
+
expect(calls[0]?.text).toContain('WHERE domain =');
|
|
73
|
+
expect(calls[0]?.values).toEqual(['www.exemplo.com', 'ACTIVE']);
|
|
74
|
+
const api = await resolveTenantFromHost('api.exemplo.com');
|
|
75
|
+
expect(api?.['domain']).toBe('api.exemplo.com');
|
|
76
|
+
expect(calls[1]?.text).toContain('WHERE domain =');
|
|
77
|
+
expect(calls[1]?.values).toEqual(['api.exemplo.com', 'ACTIVE']);
|
|
78
|
+
expect(calls).toHaveLength(2); // nenhuma query de subdomain para www/api
|
|
79
|
+
});
|
|
80
|
+
it('host sem ponto não tenta subdomain', async () => {
|
|
81
|
+
queue = [[], []];
|
|
82
|
+
await resolveTenantFromHost('intranet');
|
|
83
|
+
expect(calls).toHaveLength(1);
|
|
84
|
+
expect(calls[0]?.text).toContain('WHERE domain =');
|
|
85
|
+
expect(calls[0]?.values).toEqual(['intranet', 'ACTIVE']);
|
|
86
|
+
});
|
|
87
|
+
it('nenhum match retorna null (404 Tenant not found)', async () => {
|
|
88
|
+
queue = [[], []];
|
|
89
|
+
expect(await resolveTenantFromHost('desconhecido.com')).toBeNull();
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
//# sourceMappingURL=tenant-resolver.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tenant-resolver.test.js","sourceRoot":"","sources":["../src/tenant-resolver.test.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oktis-works/web",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -18,15 +18,15 @@
|
|
|
18
18
|
"clean": "rm -rf dist tsconfig.tsbuildinfo"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@oktis-works/core": "0.1.
|
|
22
|
-
"@oktis-works/config": "0.1.
|
|
23
|
-
"@oktis-works/database": "0.1.
|
|
24
|
-
"@oktis-works/types": "0.1.
|
|
25
|
-
"@oktis-works/theme-sdk": "0.1.
|
|
26
|
-
"@oktis-works/theme-runtime": "0.1.
|
|
21
|
+
"@oktis-works/core": "0.1.10",
|
|
22
|
+
"@oktis-works/config": "0.1.10",
|
|
23
|
+
"@oktis-works/database": "0.1.10",
|
|
24
|
+
"@oktis-works/types": "0.1.10",
|
|
25
|
+
"@oktis-works/theme-sdk": "0.1.10",
|
|
26
|
+
"@oktis-works/theme-runtime": "0.1.10",
|
|
27
27
|
"hono": "4.0.0",
|
|
28
28
|
"@hono/node-server": "1.8.0",
|
|
29
|
-
"@oktis-works/plugin-runtime": "0.1.
|
|
29
|
+
"@oktis-works/plugin-runtime": "0.1.10"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"typescript": "5.7.3",
|