@baruk/core 0.1.0
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/README.md +6 -0
- package/dist/audit/audit.d.ts +22 -0
- package/dist/audit/audit.d.ts.map +1 -0
- package/dist/audit/audit.js +28 -0
- package/dist/audit/audit.js.map +1 -0
- package/dist/audit/repository.d.ts +22 -0
- package/dist/audit/repository.d.ts.map +1 -0
- package/dist/audit/repository.js +29 -0
- package/dist/audit/repository.js.map +1 -0
- package/dist/auth/password-reset.d.ts +20 -0
- package/dist/auth/password-reset.d.ts.map +1 -0
- package/dist/auth/password-reset.js +56 -0
- package/dist/auth/password-reset.js.map +1 -0
- package/dist/auth/password.d.ts +21 -0
- package/dist/auth/password.d.ts.map +1 -0
- package/dist/auth/password.js +67 -0
- package/dist/auth/password.js.map +1 -0
- package/dist/auth/service.d.ts +82 -0
- package/dist/auth/service.d.ts.map +1 -0
- package/dist/auth/service.js +214 -0
- package/dist/auth/service.js.map +1 -0
- package/dist/auth/session-token.d.ts +8 -0
- package/dist/auth/session-token.d.ts.map +1 -0
- package/dist/auth/session-token.js +19 -0
- package/dist/auth/session-token.js.map +1 -0
- package/dist/clients/repository.d.ts +53 -0
- package/dist/clients/repository.d.ts.map +1 -0
- package/dist/clients/repository.js +113 -0
- package/dist/clients/repository.js.map +1 -0
- package/dist/dashboard/repository.d.ts +19 -0
- package/dist/dashboard/repository.d.ts.map +1 -0
- package/dist/dashboard/repository.js +36 -0
- package/dist/dashboard/repository.js.map +1 -0
- package/dist/db/client.d.ts +19 -0
- package/dist/db/client.d.ts.map +1 -0
- package/dist/db/client.js +21 -0
- package/dist/db/client.js.map +1 -0
- package/dist/db/context.d.ts +27 -0
- package/dist/db/context.d.ts.map +1 -0
- package/dist/db/context.js +36 -0
- package/dist/db/context.js.map +1 -0
- package/dist/db/load-env.d.ts +8 -0
- package/dist/db/load-env.d.ts.map +1 -0
- package/dist/db/load-env.js +44 -0
- package/dist/db/load-env.js.map +1 -0
- package/dist/db/migrate.d.ts +2 -0
- package/dist/db/migrate.d.ts.map +1 -0
- package/dist/db/migrate.js +139 -0
- package/dist/db/migrate.js.map +1 -0
- package/dist/db/seed.d.ts +2 -0
- package/dist/db/seed.d.ts.map +1 -0
- package/dist/db/seed.js +64 -0
- package/dist/db/seed.js.map +1 -0
- package/dist/import/clients-import.d.ts +39 -0
- package/dist/import/clients-import.d.ts.map +1 -0
- package/dist/import/clients-import.js +142 -0
- package/dist/import/clients-import.js.map +1 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +40 -0
- package/dist/index.js.map +1 -0
- package/dist/mail/resend.d.ts +13 -0
- package/dist/mail/resend.d.ts.map +1 -0
- package/dist/mail/resend.js +16 -0
- package/dist/mail/resend.js.map +1 -0
- package/dist/notifications/repository.d.ts +23 -0
- package/dist/notifications/repository.d.ts.map +1 -0
- package/dist/notifications/repository.js +43 -0
- package/dist/notifications/repository.js.map +1 -0
- package/dist/portal/repository.d.ts +11 -0
- package/dist/portal/repository.d.ts.map +1 -0
- package/dist/portal/repository.js +32 -0
- package/dist/portal/repository.js.map +1 -0
- package/dist/products/catalog.d.ts +14 -0
- package/dist/products/catalog.d.ts.map +1 -0
- package/dist/products/catalog.js +7 -0
- package/dist/products/catalog.js.map +1 -0
- package/dist/products/repository.d.ts +22 -0
- package/dist/products/repository.d.ts.map +1 -0
- package/dist/products/repository.js +56 -0
- package/dist/products/repository.js.map +1 -0
- package/dist/rbac/authorize.d.ts +18 -0
- package/dist/rbac/authorize.d.ts.map +1 -0
- package/dist/rbac/authorize.js +35 -0
- package/dist/rbac/authorize.js.map +1 -0
- package/dist/rbac/catalog.d.ts +21 -0
- package/dist/rbac/catalog.d.ts.map +1 -0
- package/dist/rbac/catalog.js +59 -0
- package/dist/rbac/catalog.js.map +1 -0
- package/dist/requests/repository.d.ts +66 -0
- package/dist/requests/repository.d.ts.map +1 -0
- package/dist/requests/repository.js +106 -0
- package/dist/requests/repository.js.map +1 -0
- package/dist/tenancy/provisioning.d.ts +25 -0
- package/dist/tenancy/provisioning.d.ts.map +1 -0
- package/dist/tenancy/provisioning.js +119 -0
- package/dist/tenancy/provisioning.js.map +1 -0
- package/dist/users/repository.d.ts +19 -0
- package/dist/users/repository.d.ts.map +1 -0
- package/dist/users/repository.js +78 -0
- package/dist/users/repository.js.map +1 -0
- package/package.json +41 -0
- package/src/db/migrations/0001_extensions.sql +5 -0
- package/src/db/migrations/0002_core_identity.sql +78 -0
- package/src/db/migrations/0003_rbac.sql +68 -0
- package/src/db/migrations/0004_directory.sql +35 -0
- package/src/db/migrations/0005_audit.sql +35 -0
- package/src/db/migrations/0006_rls_policies.sql +69 -0
- package/src/db/migrations/0007_requests_notifications_portal.sql +103 -0
- package/src/db/migrations/0008_client_deleted_by.sql +5 -0
- package/src/db/migrations/0009_client_scope_rls.sql +50 -0
- package/src/db/migrations/0010_product_entitlements.sql +43 -0
- package/src/db/migrations/0011_password_reset.sql +18 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-token.d.ts","sourceRoot":"","sources":["../../src/auth/session-token.ts"],"names":[],"mappings":"AAUA,uFAAuF;AACvF,wBAAgB,oBAAoB,IAAI;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAG3E;AAED,0EAA0E;AAC1E,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE/C"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sessão opaca do @baruk/core — CONVERGIDO sobre @baruk/identity (RFC-IDENTITY-003 §3.3).
|
|
3
|
+
* ---------------------------------------------------------------------------
|
|
4
|
+
* Consome a primitiva canônica de sessão opaca (@baruk/identity/opaque-session):
|
|
5
|
+
* token de 32B base64url + hash SHA-256 hex — idêntico byte-a-byte ao anterior,
|
|
6
|
+
* portanto zero mudança de formato ou de schema/DB. Mantém a assinatura pública
|
|
7
|
+
* `generateSessionToken` (thin wrapper `{ token, tokenHash }`) e `hashToken`.
|
|
8
|
+
*/
|
|
9
|
+
import { generateOpaqueToken, hashToken as canonicalHashToken } from '@baruk/identity';
|
|
10
|
+
/** Gera o token opaco entregue ao cliente + o hash a persistir (nunca o token cru). */
|
|
11
|
+
export function generateSessionToken() {
|
|
12
|
+
const token = generateOpaqueToken();
|
|
13
|
+
return { token, tokenHash: canonicalHashToken(token) };
|
|
14
|
+
}
|
|
15
|
+
/** Hash determinístico (SHA-256 hex) para armazenar/resolver a sessão. */
|
|
16
|
+
export function hashToken(token) {
|
|
17
|
+
return canonicalHashToken(token);
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=session-token.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-token.js","sourceRoot":"","sources":["../../src/auth/session-token.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,mBAAmB,EAAE,SAAS,IAAI,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAEvF,uFAAuF;AACvF,MAAM,UAAU,oBAAoB;IAClC,MAAM,KAAK,GAAG,mBAAmB,EAAE,CAAC;IACpC,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,kBAAkB,CAAC,KAAK,CAAC,EAAE,CAAC;AACzD,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,SAAS,CAAC,KAAa;IACrC,OAAO,kBAAkB,CAAC,KAAK,CAAC,CAAC;AACnC,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { Tx } from '../db/client.js';
|
|
2
|
+
/**
|
|
3
|
+
* Repositório de `client` — SEMPRE tenant-scoped. Note que NENHUM método recebe
|
|
4
|
+
* ou filtra por `tenant_id`: o isolamento vem 100% da RLS + do contexto de
|
|
5
|
+
* transação (`withContext`). Uma query aqui jamais pode ver outro tenant, mesmo
|
|
6
|
+
* que o código tentasse — a rede de segurança final é o PostgreSQL.
|
|
7
|
+
*/
|
|
8
|
+
export interface ClientRecord {
|
|
9
|
+
id: string;
|
|
10
|
+
tenantId: string;
|
|
11
|
+
clientType: 'pf' | 'pj';
|
|
12
|
+
displayName: string;
|
|
13
|
+
documentNumber: string | null;
|
|
14
|
+
status: string;
|
|
15
|
+
createdAt: string;
|
|
16
|
+
}
|
|
17
|
+
export interface CreateClientInput {
|
|
18
|
+
displayName: string;
|
|
19
|
+
clientType?: 'pf' | 'pj';
|
|
20
|
+
documentNumber?: string | null;
|
|
21
|
+
createdBy?: string | null;
|
|
22
|
+
}
|
|
23
|
+
export declare function createClient(tx: Tx, input: CreateClientInput): Promise<ClientRecord>;
|
|
24
|
+
export declare function listClients(tx: Tx): Promise<ClientRecord[]>;
|
|
25
|
+
export declare function getClient(tx: Tx, id: string): Promise<ClientRecord>;
|
|
26
|
+
/**
|
|
27
|
+
* Renomeia um cliente por id. Retorna a quantidade de linhas afetadas — sob RLS,
|
|
28
|
+
* uma tentativa cross-tenant afeta 0 linhas (o id "não existe" para este tenant).
|
|
29
|
+
*/
|
|
30
|
+
export declare function renameClient(tx: Tx, id: string, displayName: string): Promise<number>;
|
|
31
|
+
export interface ListClientsFilter {
|
|
32
|
+
search?: string;
|
|
33
|
+
status?: string;
|
|
34
|
+
limit?: number;
|
|
35
|
+
offset?: number;
|
|
36
|
+
}
|
|
37
|
+
export declare function listClientsPaged(tx: Tx, filter?: ListClientsFilter): Promise<{
|
|
38
|
+
items: ClientRecord[];
|
|
39
|
+
total: number;
|
|
40
|
+
}>;
|
|
41
|
+
export interface UpdateClientInput {
|
|
42
|
+
displayName?: string;
|
|
43
|
+
legalName?: string | null;
|
|
44
|
+
email?: string | null;
|
|
45
|
+
phone?: string | null;
|
|
46
|
+
status?: string;
|
|
47
|
+
updatedBy?: string | null;
|
|
48
|
+
}
|
|
49
|
+
export declare function updateClient(tx: Tx, id: string, input: UpdateClientInput): Promise<number>;
|
|
50
|
+
export declare function softDeleteClient(tx: Tx, id: string, deletedBy?: string | null): Promise<number>;
|
|
51
|
+
/** Busca por documento normalizado (dedup de importação). Retorna id ou null. */
|
|
52
|
+
export declare function findClientIdByDocument(tx: Tx, documentNumber: string): Promise<string | null>;
|
|
53
|
+
//# sourceMappingURL=repository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repository.d.ts","sourceRoot":"","sources":["../../src/clients/repository.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,iBAAiB,CAAC;AAE1C;;;;;GAKG;AAEH,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,IAAI,GAAG,IAAI,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAsBD,wBAAsB,YAAY,CAAC,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,YAAY,CAAC,CAgB1F;AAED,wBAAsB,WAAW,CAAC,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAQjE;AAED,wBAAsB,SAAS,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAQzE;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAQ3F;AAID,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,gBAAgB,CACpC,EAAE,EAAE,EAAE,EACN,MAAM,GAAE,iBAAsB,GAC7B,OAAO,CAAC;IAAE,KAAK,EAAE,YAAY,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAgBnD;AAED,MAAM,WAAW,iBAAiB;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,wBAAsB,YAAY,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,CAiBhG;AAED,wBAAsB,gBAAgB,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAOrG;AAED,iFAAiF;AACjF,wBAAsB,sBAAsB,CAAC,EAAE,EAAE,EAAE,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAMnG"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { NotFoundError, uuidv7 } from '@baruk/shared';
|
|
2
|
+
function mapRow(r) {
|
|
3
|
+
return {
|
|
4
|
+
id: r.id,
|
|
5
|
+
tenantId: r.tenant_id,
|
|
6
|
+
clientType: r.client_type,
|
|
7
|
+
displayName: r.display_name,
|
|
8
|
+
documentNumber: r.document_number,
|
|
9
|
+
status: r.status,
|
|
10
|
+
createdAt: r.created_at.toISOString(),
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export async function createClient(tx, input) {
|
|
14
|
+
// tenant_id vem da GUC — nunca do input. A WITH CHECK da RLS recusa qualquer
|
|
15
|
+
// tentativa de gravar fora do tenant corrente.
|
|
16
|
+
const rows = await tx `
|
|
17
|
+
INSERT INTO client (id, tenant_id, client_type, display_name, document_number, created_by)
|
|
18
|
+
VALUES (
|
|
19
|
+
${uuidv7()},
|
|
20
|
+
current_setting('app.current_tenant')::uuid,
|
|
21
|
+
${input.clientType ?? 'pj'},
|
|
22
|
+
${input.displayName},
|
|
23
|
+
${input.documentNumber ?? null},
|
|
24
|
+
${input.createdBy ?? null}
|
|
25
|
+
)
|
|
26
|
+
RETURNING id, tenant_id, client_type, display_name, document_number, status, created_at
|
|
27
|
+
`;
|
|
28
|
+
return mapRow(rows[0]);
|
|
29
|
+
}
|
|
30
|
+
export async function listClients(tx) {
|
|
31
|
+
const rows = await tx `
|
|
32
|
+
SELECT id, tenant_id, client_type, display_name, document_number, status, created_at
|
|
33
|
+
FROM client
|
|
34
|
+
WHERE deleted_at IS NULL
|
|
35
|
+
ORDER BY created_at DESC
|
|
36
|
+
`;
|
|
37
|
+
return rows.map((r) => mapRow(r));
|
|
38
|
+
}
|
|
39
|
+
export async function getClient(tx, id) {
|
|
40
|
+
const rows = await tx `
|
|
41
|
+
SELECT id, tenant_id, client_type, display_name, document_number, status, created_at
|
|
42
|
+
FROM client
|
|
43
|
+
WHERE id = ${id} AND deleted_at IS NULL
|
|
44
|
+
`;
|
|
45
|
+
if (!rows[0])
|
|
46
|
+
throw new NotFoundError('Cliente não encontrado');
|
|
47
|
+
return mapRow(rows[0]);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Renomeia um cliente por id. Retorna a quantidade de linhas afetadas — sob RLS,
|
|
51
|
+
* uma tentativa cross-tenant afeta 0 linhas (o id "não existe" para este tenant).
|
|
52
|
+
*/
|
|
53
|
+
export async function renameClient(tx, id, displayName) {
|
|
54
|
+
const rows = await tx `
|
|
55
|
+
UPDATE client
|
|
56
|
+
SET display_name = ${displayName}, updated_at = now(), version = version + 1
|
|
57
|
+
WHERE id = ${id} AND deleted_at IS NULL
|
|
58
|
+
RETURNING id
|
|
59
|
+
`;
|
|
60
|
+
return rows.count;
|
|
61
|
+
}
|
|
62
|
+
export async function listClientsPaged(tx, filter = {}) {
|
|
63
|
+
const limit = Math.min(filter.limit ?? 20, 100);
|
|
64
|
+
const offset = filter.offset ?? 0;
|
|
65
|
+
const like = filter.search ? `%${filter.search}%` : null;
|
|
66
|
+
const rows = await tx `
|
|
67
|
+
SELECT id, tenant_id, client_type, display_name, document_number, status, created_at,
|
|
68
|
+
count(*) OVER() AS total
|
|
69
|
+
FROM client
|
|
70
|
+
WHERE deleted_at IS NULL
|
|
71
|
+
AND (${filter.status ?? null}::text IS NULL OR status = ${filter.status ?? null})
|
|
72
|
+
AND (${like}::text IS NULL OR display_name ILIKE ${like} OR document_number ILIKE ${like})
|
|
73
|
+
ORDER BY created_at DESC
|
|
74
|
+
LIMIT ${limit} OFFSET ${offset}
|
|
75
|
+
`;
|
|
76
|
+
const total = rows[0] ? Number(rows[0].total) : 0;
|
|
77
|
+
return { items: rows.map((r) => mapRow(r)), total };
|
|
78
|
+
}
|
|
79
|
+
export async function updateClient(tx, id, input) {
|
|
80
|
+
// Casts explícitos: parâmetros NULL sem tipo quebram COALESCE ("could not
|
|
81
|
+
// determine data type of parameter") no PostgreSQL.
|
|
82
|
+
const rows = await tx `
|
|
83
|
+
UPDATE client SET
|
|
84
|
+
display_name = COALESCE(${input.displayName ?? null}::text, display_name),
|
|
85
|
+
legal_name = COALESCE(${input.legalName ?? null}::text, legal_name),
|
|
86
|
+
email = COALESCE(${input.email ?? null}::citext, email),
|
|
87
|
+
phone = COALESCE(${input.phone ?? null}::text, phone),
|
|
88
|
+
status = COALESCE(${input.status ?? null}::text, status),
|
|
89
|
+
updated_by = ${input.updatedBy ?? null}::uuid,
|
|
90
|
+
updated_at = now(),
|
|
91
|
+
version = version + 1
|
|
92
|
+
WHERE id = ${id} AND deleted_at IS NULL
|
|
93
|
+
RETURNING id
|
|
94
|
+
`;
|
|
95
|
+
return rows.count;
|
|
96
|
+
}
|
|
97
|
+
export async function softDeleteClient(tx, id, deletedBy) {
|
|
98
|
+
const rows = await tx `
|
|
99
|
+
UPDATE client SET deleted_at = now(), deleted_by = ${deletedBy ?? null}
|
|
100
|
+
WHERE id = ${id} AND deleted_at IS NULL
|
|
101
|
+
RETURNING id
|
|
102
|
+
`;
|
|
103
|
+
return rows.count;
|
|
104
|
+
}
|
|
105
|
+
/** Busca por documento normalizado (dedup de importação). Retorna id ou null. */
|
|
106
|
+
export async function findClientIdByDocument(tx, documentNumber) {
|
|
107
|
+
const rows = await tx `
|
|
108
|
+
SELECT id FROM client
|
|
109
|
+
WHERE document_number = ${documentNumber} AND deleted_at IS NULL LIMIT 1
|
|
110
|
+
`;
|
|
111
|
+
return rows[0]?.id ?? null;
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=repository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repository.js","sourceRoot":"","sources":["../../src/clients/repository.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Tx } from '../db/client.js';
|
|
2
|
+
/**
|
|
3
|
+
* Estatísticas AGNÓSTICAS do tenant (Core). Métricas específicas de contabilidade
|
|
4
|
+
* (ex.: clientes por regime tributário) vivem na vertical `@baruk/contabil` e são
|
|
5
|
+
* compostas na borda (API), preservando a fronteira Core→Vertical.
|
|
6
|
+
*/
|
|
7
|
+
export interface CoreDashboardStats {
|
|
8
|
+
clients: number;
|
|
9
|
+
activeUsers: number;
|
|
10
|
+
openRequests: number;
|
|
11
|
+
overdueRequests: number;
|
|
12
|
+
recentActivity: {
|
|
13
|
+
action: string;
|
|
14
|
+
resourceType: string;
|
|
15
|
+
occurredAt: string;
|
|
16
|
+
}[];
|
|
17
|
+
}
|
|
18
|
+
export declare function getCoreStats(tx: Tx): Promise<CoreDashboardStats>;
|
|
19
|
+
//# sourceMappingURL=repository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repository.d.ts","sourceRoot":"","sources":["../../src/dashboard/repository.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,iBAAiB,CAAC;AAE1C;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAChF;AAED,wBAAsB,YAAY,CAAC,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAkCtE"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export async function getCoreStats(tx) {
|
|
2
|
+
const [clients] = await tx `
|
|
3
|
+
SELECT count(*)::int AS n FROM client WHERE deleted_at IS NULL
|
|
4
|
+
`;
|
|
5
|
+
const [users] = await tx `
|
|
6
|
+
SELECT count(*)::int AS n FROM membership WHERE status = 'active' AND deleted_at IS NULL
|
|
7
|
+
`;
|
|
8
|
+
const [open] = await tx `
|
|
9
|
+
SELECT count(*)::int AS n FROM service_request
|
|
10
|
+
WHERE deleted_at IS NULL
|
|
11
|
+
AND status IN ('sent','viewed','awaiting_client','answered','in_review')
|
|
12
|
+
`;
|
|
13
|
+
const [overdue] = await tx `
|
|
14
|
+
SELECT count(*)::int AS n FROM service_request
|
|
15
|
+
WHERE deleted_at IS NULL
|
|
16
|
+
AND (status = 'overdue'
|
|
17
|
+
OR (due_date IS NOT NULL AND due_date < current_date
|
|
18
|
+
AND status NOT IN ('done','canceled')))
|
|
19
|
+
`;
|
|
20
|
+
const recent = await tx `
|
|
21
|
+
SELECT action, resource_type, occurred_at
|
|
22
|
+
FROM audit_log ORDER BY occurred_at DESC LIMIT 8
|
|
23
|
+
`;
|
|
24
|
+
return {
|
|
25
|
+
clients: clients?.n ?? 0,
|
|
26
|
+
activeUsers: users?.n ?? 0,
|
|
27
|
+
openRequests: open?.n ?? 0,
|
|
28
|
+
overdueRequests: overdue?.n ?? 0,
|
|
29
|
+
recentActivity: recent.map((r) => ({
|
|
30
|
+
action: r.action,
|
|
31
|
+
resourceType: r.resource_type,
|
|
32
|
+
occurredAt: r.occurred_at.toISOString(),
|
|
33
|
+
})),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=repository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repository.js","sourceRoot":"","sources":["../../src/dashboard/repository.ts"],"names":[],"mappings":"AAeA,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,EAAM;IACvC,MAAM,CAAC,OAAO,CAAC,GAAG,MAAM,EAAE,CAAiB;;GAE1C,CAAC;IACF,MAAM,CAAC,KAAK,CAAC,GAAG,MAAM,EAAE,CAAiB;;GAExC,CAAC;IACF,MAAM,CAAC,IAAI,CAAC,GAAG,MAAM,EAAE,CAAiB;;;;GAIvC,CAAC;IACF,MAAM,CAAC,OAAO,CAAC,GAAG,MAAM,EAAE,CAAiB;;;;;;GAM1C,CAAC;IACF,MAAM,MAAM,GAAG,MAAM,EAAE,CAAgE;;;GAGtF,CAAC;IACF,OAAO;QACL,OAAO,EAAE,OAAO,EAAE,CAAC,IAAI,CAAC;QACxB,WAAW,EAAE,KAAK,EAAE,CAAC,IAAI,CAAC;QAC1B,YAAY,EAAE,IAAI,EAAE,CAAC,IAAI,CAAC;QAC1B,eAAe,EAAE,OAAO,EAAE,CAAC,IAAI,CAAC;QAChC,cAAc,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACjC,MAAM,EAAE,CAAC,CAAC,MAAM;YAChB,YAAY,EAAE,CAAC,CAAC,aAAa;YAC7B,UAAU,EAAE,CAAC,CAAC,WAAW,CAAC,WAAW,EAAE;SACxC,CAAC,CAAC;KACJ,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import postgres from 'postgres';
|
|
2
|
+
export type Db = postgres.Sql<Record<string, never>>;
|
|
3
|
+
export type Tx = postgres.TransactionSql<Record<string, never>>;
|
|
4
|
+
/** Qualquer executor de query: conexão de pool ou transação. */
|
|
5
|
+
export type Queryable = Db | Tx;
|
|
6
|
+
export interface DbOptions {
|
|
7
|
+
/** Máximo de conexões no pool. */
|
|
8
|
+
max?: number;
|
|
9
|
+
/** Rótulo do papel (owner/app) — só para diagnóstico. */
|
|
10
|
+
role?: 'owner' | 'app';
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Cria um cliente Postgres (postgres.js). Duas instâncias são usadas no
|
|
14
|
+
* ecossistema (05-security §7.1, defesa em profundidade):
|
|
15
|
+
* - OWNER: roda migrations/seed (cria schema).
|
|
16
|
+
* - APP (baruk_app): runtime da aplicação — NOSUPERUSER, NOBYPASSRLS, sujeito à RLS.
|
|
17
|
+
*/
|
|
18
|
+
export declare function createDb(connectionString: string, opts?: DbOptions): Db;
|
|
19
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/db/client.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,UAAU,CAAC;AAEhC,MAAM,MAAM,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;AACrD,MAAM,MAAM,EAAE,GAAG,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;AAChE,gEAAgE;AAChE,MAAM,MAAM,SAAS,GAAG,EAAE,GAAG,EAAE,CAAC;AAEhC,MAAM,WAAW,SAAS;IACxB,kCAAkC;IAClC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,yDAAyD;IACzD,IAAI,CAAC,EAAE,OAAO,GAAG,KAAK,CAAC;CACxB;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,gBAAgB,EAAE,MAAM,EAAE,IAAI,GAAE,SAAc,GAAG,EAAE,CAgB3E"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import postgres from 'postgres';
|
|
2
|
+
/**
|
|
3
|
+
* Cria um cliente Postgres (postgres.js). Duas instâncias são usadas no
|
|
4
|
+
* ecossistema (05-security §7.1, defesa em profundidade):
|
|
5
|
+
* - OWNER: roda migrations/seed (cria schema).
|
|
6
|
+
* - APP (baruk_app): runtime da aplicação — NOSUPERUSER, NOBYPASSRLS, sujeito à RLS.
|
|
7
|
+
*/
|
|
8
|
+
export function createDb(connectionString, opts = {}) {
|
|
9
|
+
// SSL obrigatório em provedores gerenciados (Render/Neon/etc.); desligado em
|
|
10
|
+
// Postgres local. Detecta pelo host/flag da URL.
|
|
11
|
+
const isLocal = /@(localhost|127\.0\.0\.1|host\.docker\.internal|\[?::1\]?)[:/]/.test(connectionString);
|
|
12
|
+
const wantsSsl = !isLocal && /sslmode=require|render\.com|neon\.tech|supabase|amazonaws|\.cloud/.test(connectionString);
|
|
13
|
+
return postgres(connectionString, {
|
|
14
|
+
max: opts.max ?? 10,
|
|
15
|
+
// UUIDs são gerados na aplicação (uuidv7); datas em UTC.
|
|
16
|
+
prepare: true,
|
|
17
|
+
ssl: wantsSsl ? { rejectUnauthorized: false } : undefined,
|
|
18
|
+
onnotice: () => { }, // silencia NOTICEs verbosos de DO-blocks/IF EXISTS
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/db/client.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,UAAU,CAAC;AAchC;;;;;GAKG;AACH,MAAM,UAAU,QAAQ,CAAC,gBAAwB,EAAE,OAAkB,EAAE;IACrE,6EAA6E;IAC7E,iDAAiD;IACjD,MAAM,OAAO,GAAG,gEAAgE,CAAC,IAAI,CACnF,gBAAgB,CACjB,CAAC;IACF,MAAM,QAAQ,GACZ,CAAC,OAAO,IAAI,mEAAmE,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IAEzG,OAAO,QAAQ,CAAC,gBAAgB,EAAE;QAChC,GAAG,EAAE,IAAI,CAAC,GAAG,IAAI,EAAE;QACnB,yDAAyD;QACzD,OAAO,EAAE,IAAI;QACb,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,kBAAkB,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS;QACzD,QAAQ,EAAE,GAAG,EAAE,GAAE,CAAC,EAAE,mDAAmD;KACxE,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Db, Tx } from './client.js';
|
|
2
|
+
/**
|
|
3
|
+
* Contexto de banco por request. `tenantId` alimenta a RLS via a GUC
|
|
4
|
+
* `app.current_tenant`; `userId` alimenta `app.current_user` (usado pela policy
|
|
5
|
+
* de `membership` para o usuário enxergar os próprios vínculos antes de escolher
|
|
6
|
+
* um tenant — seletor de tenant do SSO, 01-multi-tenancy §3).
|
|
7
|
+
*/
|
|
8
|
+
export interface DbContext {
|
|
9
|
+
readonly tenantId?: string;
|
|
10
|
+
readonly userId?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Escopo de cliente (portal). Quando setado, a RLS restringe entidades
|
|
13
|
+
* client-scoped (ex.: service_request) a ESTE client — mesmo que a camada de
|
|
14
|
+
* aplicação esquecesse o filtro. Não setado (staff) = vê todos os clientes do tenant.
|
|
15
|
+
*/
|
|
16
|
+
readonly clientId?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Executa `fn` dentro de uma transação com as GUCs de contexto setadas via
|
|
20
|
+
* `set_config(..., is_local => true)` — ou seja, escopo de transação (equivalente
|
|
21
|
+
* a `SET LOCAL`). Toda leitura/escrita tenant-scoped DEVE passar por aqui: é o
|
|
22
|
+
* único ponto onde o `tenant_id` chega ao PostgreSQL, e ele nunca vem do cliente.
|
|
23
|
+
*/
|
|
24
|
+
export declare function withContext<T>(db: Db, ctx: DbContext, fn: (tx: Tx) => Promise<T>): Promise<T>;
|
|
25
|
+
/** Açúcar sintático: contexto apenas de tenant. */
|
|
26
|
+
export declare function withTenant<T>(db: Db, tenantId: string, fn: (tx: Tx) => Promise<T>): Promise<T>;
|
|
27
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/db/context.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAE1C;;;;;GAKG;AACH,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,CAAC,EACjC,EAAE,EAAE,EAAE,EACN,GAAG,EAAE,SAAS,EACd,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,CAAC,CAAC,GACzB,OAAO,CAAC,CAAC,CAAC,CAyBZ;AAED,mDAAmD;AACnD,wBAAgB,UAAU,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAE9F"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { isUuid, TenantContextError } from '@baruk/shared';
|
|
2
|
+
/**
|
|
3
|
+
* Executa `fn` dentro de uma transação com as GUCs de contexto setadas via
|
|
4
|
+
* `set_config(..., is_local => true)` — ou seja, escopo de transação (equivalente
|
|
5
|
+
* a `SET LOCAL`). Toda leitura/escrita tenant-scoped DEVE passar por aqui: é o
|
|
6
|
+
* único ponto onde o `tenant_id` chega ao PostgreSQL, e ele nunca vem do cliente.
|
|
7
|
+
*/
|
|
8
|
+
export async function withContext(db, ctx, fn) {
|
|
9
|
+
if (ctx.tenantId !== undefined && !isUuid(ctx.tenantId)) {
|
|
10
|
+
throw new TenantContextError(`tenantId inválido no contexto: ${ctx.tenantId}`);
|
|
11
|
+
}
|
|
12
|
+
if (ctx.userId !== undefined && !isUuid(ctx.userId)) {
|
|
13
|
+
throw new TenantContextError(`userId inválido no contexto: ${ctx.userId}`);
|
|
14
|
+
}
|
|
15
|
+
if (ctx.clientId !== undefined && !isUuid(ctx.clientId)) {
|
|
16
|
+
throw new TenantContextError(`clientId inválido no contexto: ${ctx.clientId}`);
|
|
17
|
+
}
|
|
18
|
+
const result = await db.begin(async (tx) => {
|
|
19
|
+
if (ctx.tenantId !== undefined) {
|
|
20
|
+
await tx `select set_config('app.current_tenant', ${ctx.tenantId}, true)`;
|
|
21
|
+
}
|
|
22
|
+
if (ctx.userId !== undefined) {
|
|
23
|
+
await tx `select set_config('app.current_user', ${ctx.userId}, true)`;
|
|
24
|
+
}
|
|
25
|
+
if (ctx.clientId !== undefined) {
|
|
26
|
+
await tx `select set_config('app.current_client', ${ctx.clientId}, true)`;
|
|
27
|
+
}
|
|
28
|
+
return fn(tx);
|
|
29
|
+
});
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
32
|
+
/** Açúcar sintático: contexto apenas de tenant. */
|
|
33
|
+
export function withTenant(db, tenantId, fn) {
|
|
34
|
+
return withContext(db, { tenantId }, fn);
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/db/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAoB3D;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,EAAM,EACN,GAAc,EACd,EAA0B;IAE1B,IAAI,GAAG,CAAC,QAAQ,KAAK,SAAS,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;QACxD,MAAM,IAAI,kBAAkB,CAAC,kCAAkC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;IACjF,CAAC;IACD,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QACpD,MAAM,IAAI,kBAAkB,CAAC,gCAAgC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;IAC7E,CAAC;IACD,IAAI,GAAG,CAAC,QAAQ,KAAK,SAAS,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;QACxD,MAAM,IAAI,kBAAkB,CAAC,kCAAkC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;IACjF,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;QACzC,IAAI,GAAG,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC/B,MAAM,EAAE,CAAA,2CAA2C,GAAG,CAAC,QAAQ,SAAS,CAAC;QAC3E,CAAC;QACD,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAC7B,MAAM,EAAE,CAAA,yCAAyC,GAAG,CAAC,MAAM,SAAS,CAAC;QACvE,CAAC;QACD,IAAI,GAAG,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC/B,MAAM,EAAE,CAAA,2CAA2C,GAAG,CAAC,QAAQ,SAAS,CAAC;QAC3E,CAAC;QACD,OAAO,EAAE,CAAC,EAAmB,CAAC,CAAC;IACjC,CAAC,CAAC,CAAC;IAEH,OAAO,MAAW,CAAC;AACrB,CAAC;AAED,mDAAmD;AACnD,MAAM,UAAU,UAAU,CAAI,EAAM,EAAE,QAAgB,EAAE,EAA0B;IAChF,OAAO,WAAW,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;AAC3C,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Carregador mínimo de `.env` (sem dependência externa). Procura o arquivo na
|
|
3
|
+
* raiz do monorepo e popula `process.env` sem sobrescrever variáveis já
|
|
4
|
+
* definidas (o ambiente real tem prioridade sobre o arquivo). Usado pelos
|
|
5
|
+
* scripts CLI (migrate/seed) e pela API em desenvolvimento.
|
|
6
|
+
*/
|
|
7
|
+
export declare function loadEnv(fileName?: string): void;
|
|
8
|
+
//# sourceMappingURL=load-env.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load-env.d.ts","sourceRoot":"","sources":["../../src/db/load-env.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,QAAQ,SAAS,GAAG,IAAI,CAa/C"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
/**
|
|
4
|
+
* Carregador mínimo de `.env` (sem dependência externa). Procura o arquivo na
|
|
5
|
+
* raiz do monorepo e popula `process.env` sem sobrescrever variáveis já
|
|
6
|
+
* definidas (o ambiente real tem prioridade sobre o arquivo). Usado pelos
|
|
7
|
+
* scripts CLI (migrate/seed) e pela API em desenvolvimento.
|
|
8
|
+
*/
|
|
9
|
+
export function loadEnv(fileName = '.env') {
|
|
10
|
+
// Sobe diretórios a partir do cwd até achar o arquivo (raiz do monorepo).
|
|
11
|
+
let dir = process.cwd();
|
|
12
|
+
for (let i = 0; i < 6; i++) {
|
|
13
|
+
const candidate = resolve(dir, fileName);
|
|
14
|
+
if (existsSync(candidate)) {
|
|
15
|
+
applyEnvFile(candidate);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const parent = resolve(dir, '..');
|
|
19
|
+
if (parent === dir)
|
|
20
|
+
break;
|
|
21
|
+
dir = parent;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function applyEnvFile(path) {
|
|
25
|
+
const content = readFileSync(path, 'utf8');
|
|
26
|
+
for (const rawLine of content.split('\n')) {
|
|
27
|
+
const line = rawLine.trim();
|
|
28
|
+
if (!line || line.startsWith('#'))
|
|
29
|
+
continue;
|
|
30
|
+
const eq = line.indexOf('=');
|
|
31
|
+
if (eq === -1)
|
|
32
|
+
continue;
|
|
33
|
+
const key = line.slice(0, eq).trim();
|
|
34
|
+
if (key in process.env)
|
|
35
|
+
continue;
|
|
36
|
+
let value = line.slice(eq + 1).trim();
|
|
37
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
38
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
39
|
+
value = value.slice(1, -1);
|
|
40
|
+
}
|
|
41
|
+
process.env[key] = value;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=load-env.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load-env.js","sourceRoot":"","sources":["../../src/db/load-env.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC;;;;;GAKG;AACH,MAAM,UAAU,OAAO,CAAC,QAAQ,GAAG,MAAM;IACvC,0EAA0E;IAC1E,IAAI,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IACxB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3B,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QACzC,IAAI,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAC1B,YAAY,CAAC,SAAS,CAAC,CAAC;YACxB,OAAO;QACT,CAAC;QACD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAClC,IAAI,MAAM,KAAK,GAAG;YAAE,MAAM;QAC1B,GAAG,GAAG,MAAM,CAAC;IACf,CAAC;AACH,CAAC;AAED,SAAS,YAAY,CAAC,IAAY;IAChC,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC3C,KAAK,MAAM,OAAO,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1C,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS;QAC5C,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC7B,IAAI,EAAE,KAAK,CAAC,CAAC;YAAE,SAAS;QACxB,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QACrC,IAAI,GAAG,IAAI,OAAO,CAAC,GAAG;YAAE,SAAS;QACjC,IAAI,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACtC,IACE,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;YAC9C,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAC9C,CAAC;YACD,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC7B,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IAC3B,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../src/db/migrate.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runner de migrations — fonte única de verdade do schema.
|
|
3
|
+
*
|
|
4
|
+
* Aplica arquivos `.sql` numerados em ordem (core + verticais), registrando cada
|
|
5
|
+
* um em `_migration` com checksum. Roda com o papel OWNER (DATABASE_URL). Antes
|
|
6
|
+
* das migrations, garante o papel de aplicação `baruk_app` (NOSUPERUSER,
|
|
7
|
+
* NOBYPASSRLS) e, ao final, concede os privilégios de DML a ele.
|
|
8
|
+
*
|
|
9
|
+
* Uso: `pnpm db:migrate` (ou `tsx src/db/migrate.ts`)
|
|
10
|
+
*/
|
|
11
|
+
import { createHash } from 'node:crypto';
|
|
12
|
+
import { readdirSync, readFileSync, existsSync } from 'node:fs';
|
|
13
|
+
import { dirname, join, resolve } from 'node:path';
|
|
14
|
+
import { fileURLToPath } from 'node:url';
|
|
15
|
+
import { createDb } from './client.js';
|
|
16
|
+
import { loadEnv } from './load-env.js';
|
|
17
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
18
|
+
function findRepoRoot(start) {
|
|
19
|
+
let dir = start;
|
|
20
|
+
for (let i = 0; i < 8; i++) {
|
|
21
|
+
if (existsSync(join(dir, 'pnpm-workspace.yaml')))
|
|
22
|
+
return dir;
|
|
23
|
+
const parent = resolve(dir, '..');
|
|
24
|
+
if (parent === dir)
|
|
25
|
+
break;
|
|
26
|
+
dir = parent;
|
|
27
|
+
}
|
|
28
|
+
return start;
|
|
29
|
+
}
|
|
30
|
+
function collectDir(dir) {
|
|
31
|
+
if (!existsSync(dir))
|
|
32
|
+
return [];
|
|
33
|
+
return readdirSync(dir)
|
|
34
|
+
.filter((f) => f.endsWith('.sql'))
|
|
35
|
+
.map((f) => {
|
|
36
|
+
const path = join(dir, f);
|
|
37
|
+
const sql = readFileSync(path, 'utf8');
|
|
38
|
+
return { name: f, path, sql, checksum: createHash('sha256').update(sql).digest('hex') };
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
function collectMigrations() {
|
|
42
|
+
// Este arquivo vive em packages/core/src/db → as migrations do core em ./migrations.
|
|
43
|
+
const coreMigrations = join(__dirname, 'migrations');
|
|
44
|
+
const repoRoot = findRepoRoot(__dirname);
|
|
45
|
+
const verticalsRoot = join(repoRoot, 'packages', 'verticals');
|
|
46
|
+
const all = [...collectDir(coreMigrations)];
|
|
47
|
+
if (existsSync(verticalsRoot)) {
|
|
48
|
+
for (const vertical of readdirSync(verticalsRoot)) {
|
|
49
|
+
all.push(...collectDir(join(verticalsRoot, vertical, 'db', 'migrations')));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// Ordena por nome (prefixos numéricos zero-padded garantem ordem correta).
|
|
53
|
+
return all.sort((a, b) => a.name.localeCompare(b.name, 'en'));
|
|
54
|
+
}
|
|
55
|
+
/** Valida a senha do papel de app para poder embutir com segurança no DDL. */
|
|
56
|
+
function safePassword(pw) {
|
|
57
|
+
if (!/^[A-Za-z0-9!@#%^*_\-+=.]{8,}$/.test(pw)) {
|
|
58
|
+
throw new Error('APP_DB_PASSWORD ausente ou com caracteres não suportados (use 8+ de [A-Za-z0-9!@#%^*_-+=.]).');
|
|
59
|
+
}
|
|
60
|
+
return pw;
|
|
61
|
+
}
|
|
62
|
+
async function main() {
|
|
63
|
+
loadEnv();
|
|
64
|
+
const ownerUrl = process.env.DATABASE_URL;
|
|
65
|
+
if (!ownerUrl)
|
|
66
|
+
throw new Error('DATABASE_URL (papel owner) não definida.');
|
|
67
|
+
const appPassword = safePassword(process.env.APP_DB_PASSWORD ?? '');
|
|
68
|
+
const sql = createDb(ownerUrl, { role: 'owner', max: 1 });
|
|
69
|
+
try {
|
|
70
|
+
// 1) Papel de aplicação. Roles novas já nascem NOSUPERUSER, NOBYPASSRLS,
|
|
71
|
+
// NOCREATEDB, NOCREATEROLE — que é exatamente o que queremos. Definir
|
|
72
|
+
// esses atributos EXPLICITAMENTE exige superuser (indisponível em Postgres
|
|
73
|
+
// gerenciado como Render/Neon), então só criamos com defaults e setamos
|
|
74
|
+
// LOGIN + PASSWORD. A ausência de BYPASSRLS + FORCE RLS garante o isolamento.
|
|
75
|
+
await sql.unsafe(`
|
|
76
|
+
DO $$ BEGIN
|
|
77
|
+
IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'baruk_app') THEN
|
|
78
|
+
CREATE ROLE baruk_app LOGIN PASSWORD '${appPassword}';
|
|
79
|
+
END IF;
|
|
80
|
+
END $$;
|
|
81
|
+
`);
|
|
82
|
+
await sql.unsafe(`ALTER ROLE baruk_app WITH LOGIN PASSWORD '${appPassword}';`);
|
|
83
|
+
// 2) Tabela de controle.
|
|
84
|
+
await sql.unsafe(`
|
|
85
|
+
CREATE TABLE IF NOT EXISTS _migration (
|
|
86
|
+
name text PRIMARY KEY,
|
|
87
|
+
checksum text NOT NULL,
|
|
88
|
+
applied_at timestamptz NOT NULL DEFAULT now()
|
|
89
|
+
);
|
|
90
|
+
`);
|
|
91
|
+
const applied = new Map();
|
|
92
|
+
for (const row of await sql `SELECT name, checksum FROM _migration`) {
|
|
93
|
+
applied.set(row.name, row.checksum);
|
|
94
|
+
}
|
|
95
|
+
const migrations = collectMigrations();
|
|
96
|
+
let count = 0;
|
|
97
|
+
for (const m of migrations) {
|
|
98
|
+
const prev = applied.get(m.name);
|
|
99
|
+
if (prev) {
|
|
100
|
+
if (prev !== m.checksum) {
|
|
101
|
+
throw new Error(`Migration já aplicada foi modificada: ${m.name}. Migrations são imutáveis — crie uma nova.`);
|
|
102
|
+
}
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
process.stdout.write(`→ aplicando ${m.name} … `);
|
|
106
|
+
await sql.begin(async (tx) => {
|
|
107
|
+
await tx.unsafe(m.sql);
|
|
108
|
+
await tx `INSERT INTO _migration (name, checksum) VALUES (${m.name}, ${m.checksum})`;
|
|
109
|
+
});
|
|
110
|
+
console.log('ok');
|
|
111
|
+
count++;
|
|
112
|
+
}
|
|
113
|
+
// 3) Grants ao papel de app (idempotente; cobre também tabelas de verticais).
|
|
114
|
+
await sql.unsafe(`
|
|
115
|
+
GRANT USAGE ON SCHEMA public TO baruk_app;
|
|
116
|
+
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO baruk_app;
|
|
117
|
+
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO baruk_app;
|
|
118
|
+
ALTER DEFAULT PRIVILEGES IN SCHEMA public
|
|
119
|
+
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO baruk_app;
|
|
120
|
+
-- Append-only: app não pode alterar/apagar auditoria.
|
|
121
|
+
REVOKE UPDATE, DELETE ON audit_log FROM baruk_app;
|
|
122
|
+
-- Segredos de auth: app só lê (seed/owner escreve).
|
|
123
|
+
REVOKE INSERT, UPDATE, DELETE ON user_credential FROM baruk_app;
|
|
124
|
+
-- Controle de migrations é privado do owner.
|
|
125
|
+
REVOKE ALL ON _migration FROM baruk_app;
|
|
126
|
+
`);
|
|
127
|
+
console.log(count === 0
|
|
128
|
+
? '✓ Nenhuma migration pendente. Schema já atualizado.'
|
|
129
|
+
: `✓ ${count} migration(s) aplicada(s). Papel baruk_app garantido e privilegiado.`);
|
|
130
|
+
}
|
|
131
|
+
finally {
|
|
132
|
+
await sql.end({ timeout: 5 });
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
main().catch((err) => {
|
|
136
|
+
console.error('✗ Falha na migração:', err instanceof Error ? err.message : err);
|
|
137
|
+
process.exit(1);
|
|
138
|
+
});
|
|
139
|
+
//# sourceMappingURL=migrate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrate.js","sourceRoot":"","sources":["../../src/db/migrate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seed.d.ts","sourceRoot":"","sources":["../../src/db/seed.ts"],"names":[],"mappings":""}
|