@mybuildex/pkg 0.2.1 → 0.3.1
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/firebase/admin.d.ts +9 -0
- package/dist/firebase/admin.js +17 -3
- package/dist/firebase/admin.js.map +1 -1
- package/dist/firebase/client-plugin.d.ts +8 -0
- package/dist/firebase/client-plugin.js +32 -11
- package/dist/firebase/client-plugin.js.map +1 -1
- package/dist/http/error-handler.d.ts +2 -0
- package/dist/http/error-handler.js +19 -5
- package/dist/http/error-handler.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/prisma/multitenant.d.ts +1 -0
- package/dist/prisma/multitenant.js +6 -0
- package/dist/prisma/multitenant.js.map +1 -1
- package/dist/prisma/raw-tenant.d.ts +7 -0
- package/dist/prisma/raw-tenant.js +41 -0
- package/dist/prisma/raw-tenant.js.map +1 -0
- package/dist/swagger/plugin.d.ts +2 -0
- package/dist/swagger/plugin.js +1 -1
- package/dist/swagger/plugin.js.map +1 -1
- package/dist/tenant/auth.d.ts +4 -1
- package/dist/tenant/auth.js +14 -4
- package/dist/tenant/auth.js.map +1 -1
- package/dist/tenant/middleware.d.ts +4 -0
- package/dist/tenant/middleware.js +19 -3
- package/dist/tenant/middleware.js.map +1 -1
- package/package.json +1 -1
package/dist/firebase/admin.d.ts
CHANGED
|
@@ -2,6 +2,15 @@ export interface FirebaseAdminOptions {
|
|
|
2
2
|
/** Caminho do arquivo de service account para dev local (fallback:
|
|
3
3
|
* env FIREBASE_CREDENTIALS_FILE). Ignorado quando ADC está disponível. */
|
|
4
4
|
credentialsFile?: string;
|
|
5
|
+
/** Bucket default do admin.storage() (ex: magazines usa para upload de
|
|
6
|
+
* páginas). Fallback: env STORAGEBUCKET quando getBucket() é usado. */
|
|
7
|
+
storageBucket?: string;
|
|
5
8
|
}
|
|
6
9
|
export declare function initFirebaseAdmin(options?: FirebaseAdminOptions): any;
|
|
10
|
+
/** Auth default (sem tenant) — para APIs single-tenant como o core:
|
|
11
|
+
* createAuthMiddleware({tenantAware: false, getTenantAuth: () => getDefaultAuth()}) */
|
|
12
|
+
export declare function getDefaultAuth(): any;
|
|
13
|
+
/** Bucket default do admin.storage() (requer storageBucket na init ou env
|
|
14
|
+
* STORAGEBUCKET) — magazines usa para upload de páginas de revista. */
|
|
15
|
+
export declare function getBucket(): any;
|
|
7
16
|
export declare function getTenantAuth(tenantId: string | undefined): any;
|
package/dist/firebase/admin.js
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
// NUNCA embute credencial. `firebase-admin` é peer dep.
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
exports.initFirebaseAdmin = initFirebaseAdmin;
|
|
8
|
+
exports.getDefaultAuth = getDefaultAuth;
|
|
9
|
+
exports.getBucket = getBucket;
|
|
8
10
|
exports.getTenantAuth = getTenantAuth;
|
|
9
11
|
let adminInstance = null;
|
|
10
12
|
function initFirebaseAdmin(options = {}) {
|
|
@@ -15,8 +17,10 @@ function initFirebaseAdmin(options = {}) {
|
|
|
15
17
|
if (!admin.apps.length) {
|
|
16
18
|
const useADC = process.env.USE_ADC === "true" ||
|
|
17
19
|
!!process.env.GOOGLE_APPLICATION_CREDENTIALS;
|
|
20
|
+
const storageBucket = options.storageBucket || process.env.STORAGEBUCKET;
|
|
21
|
+
const base = storageBucket ? { storageBucket } : {};
|
|
18
22
|
if (useADC) {
|
|
19
|
-
admin.initializeApp(); // ADC: credencial do ambiente
|
|
23
|
+
admin.initializeApp(base); // ADC: credencial do ambiente
|
|
20
24
|
}
|
|
21
25
|
else {
|
|
22
26
|
const file = options.credentialsFile || process.env.FIREBASE_CREDENTIALS_FILE;
|
|
@@ -24,17 +28,27 @@ function initFirebaseAdmin(options = {}) {
|
|
|
24
28
|
// Sem ADC e sem arquivo: inicializa via ADC mesmo assim — em
|
|
25
29
|
// ambientes GCP funciona; fora, falha alto na primeira chamada
|
|
26
30
|
// (melhor que embutir credencial).
|
|
27
|
-
admin.initializeApp();
|
|
31
|
+
admin.initializeApp(base);
|
|
28
32
|
}
|
|
29
33
|
else {
|
|
30
34
|
const serviceAccount = require(file);
|
|
31
|
-
admin.initializeApp({ credential: admin.credential.cert(serviceAccount) });
|
|
35
|
+
admin.initializeApp({ ...base, credential: admin.credential.cert(serviceAccount) });
|
|
32
36
|
}
|
|
33
37
|
}
|
|
34
38
|
}
|
|
35
39
|
adminInstance = admin;
|
|
36
40
|
return admin;
|
|
37
41
|
}
|
|
42
|
+
/** Auth default (sem tenant) — para APIs single-tenant como o core:
|
|
43
|
+
* createAuthMiddleware({tenantAware: false, getTenantAuth: () => getDefaultAuth()}) */
|
|
44
|
+
function getDefaultAuth() {
|
|
45
|
+
return initFirebaseAdmin().auth();
|
|
46
|
+
}
|
|
47
|
+
/** Bucket default do admin.storage() (requer storageBucket na init ou env
|
|
48
|
+
* STORAGEBUCKET) — magazines usa para upload de páginas de revista. */
|
|
49
|
+
function getBucket() {
|
|
50
|
+
return initFirebaseAdmin().storage().bucket();
|
|
51
|
+
}
|
|
38
52
|
// --- helper multi-tenant cacheado (Firebase Auth: 1 tenant por escola) ---
|
|
39
53
|
const tenantAuthCache = new Map();
|
|
40
54
|
function getTenantAuth(tenantId) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"admin.js","sourceRoot":"","sources":["../../src/firebase/admin.ts"],"names":[],"mappings":";AAAA,4EAA4E;AAC5E,4EAA4E;AAC5E,6EAA6E;AAC7E,wDAAwD;;
|
|
1
|
+
{"version":3,"file":"admin.js","sourceRoot":"","sources":["../../src/firebase/admin.ts"],"names":[],"mappings":";AAAA,4EAA4E;AAC5E,4EAA4E;AAC5E,6EAA6E;AAC7E,wDAAwD;;AAaxD,8CA8BC;AAID,wCAEC;AAID,8BAEC;AAKD,sCAQC;AAlED,IAAI,aAAa,GAAQ,IAAI,CAAC;AAW9B,SAAgB,iBAAiB,CAAC,UAAgC,EAAE;IAChE,IAAI,aAAa;QAAE,OAAO,aAAa,CAAC;IACxC,uDAAuD;IACvD,MAAM,KAAK,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAExC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;QACrB,MAAM,MAAM,GACR,OAAO,CAAC,GAAG,CAAC,OAAO,KAAK,MAAM;YAC9B,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC;QAEjD,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC;QACzE,MAAM,IAAI,GAAG,aAAa,CAAC,CAAC,CAAC,EAAC,aAAa,EAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAElD,IAAI,MAAM,EAAE,CAAC;YACT,KAAK,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,8BAA8B;QAC7D,CAAC;aAAM,CAAC;YACJ,MAAM,IAAI,GAAG,OAAO,CAAC,eAAe,IAAI,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC;YAC9E,IAAI,CAAC,IAAI,EAAE,CAAC;gBACR,6DAA6D;gBAC7D,+DAA+D;gBAC/D,mCAAmC;gBACnC,KAAK,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;YAC9B,CAAC;iBAAM,CAAC;gBACJ,MAAM,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;gBACrC,KAAK,CAAC,aAAa,CAAC,EAAC,GAAG,IAAI,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,cAAc,CAAC,EAAC,CAAC,CAAC;YACtF,CAAC;QACL,CAAC;IACL,CAAC;IACD,aAAa,GAAG,KAAK,CAAC;IACtB,OAAO,KAAK,CAAC;AACjB,CAAC;AAED;wFACwF;AACxF,SAAgB,cAAc;IAC1B,OAAO,iBAAiB,EAAE,CAAC,IAAI,EAAE,CAAC;AACtC,CAAC;AAED;wEACwE;AACxE,SAAgB,SAAS;IACrB,OAAO,iBAAiB,EAAE,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,CAAC;AAClD,CAAC;AAED,4EAA4E;AAC5E,MAAM,eAAe,GAAG,IAAI,GAAG,EAAe,CAAC;AAE/C,SAAgB,aAAa,CAAC,QAA4B;IACtD,IAAI,CAAC,QAAQ;QAAE,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;IACrE,IAAI,CAAC,GAAG,eAAe,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACtC,IAAI,CAAC,CAAC,EAAE,CAAC;QACL,CAAC,GAAG,iBAAiB,EAAE,CAAC,IAAI,EAAE,CAAC,aAAa,EAAE,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QACvE,eAAe,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;IACrC,CAAC;IACD,OAAO,CAAC,CAAC;AACb,CAAC"}
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
export interface FirebaseClientPluginOptions {
|
|
2
2
|
initializeApp?: (config: any) => any;
|
|
3
3
|
getStorage?: (app: any) => any;
|
|
4
|
+
/** Decorators extras além de firebase/storage (ex: acls/payments decoram
|
|
5
|
+
* authfirebase, firebasep, services próprios). */
|
|
6
|
+
decorate?: (fastify: any, ctx: {
|
|
7
|
+
app: any;
|
|
8
|
+
storage: any;
|
|
9
|
+
}) => void | Promise<void>;
|
|
4
10
|
}
|
|
11
|
+
export declare function getFirebaseClientApp(): any;
|
|
12
|
+
export declare function getFirebaseStorage(): any;
|
|
5
13
|
export declare function createFirebaseClientPlugin(options?: FirebaseClientPluginOptions): (fastify: any) => Promise<void>;
|
|
@@ -2,14 +2,22 @@
|
|
|
2
2
|
// Firebase CLIENT SDK (storage) das APIs. Config 100% via env — as chaves de
|
|
3
3
|
// client do Firebase (apiKey etc.) não são segredo, mas nada fica hardcoded.
|
|
4
4
|
// `firebase` é peer dep; injeção opcional dos módulos para teste.
|
|
5
|
+
//
|
|
6
|
+
// getFirebaseStorage()/getFirebaseClientApp(): uso STANDALONE (fora do
|
|
7
|
+
// Fastify) — ex: worker Pub/Sub do templates. Singleton compartilhado com o
|
|
8
|
+
// plugin (uma instância só por processo).
|
|
5
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.getFirebaseClientApp = getFirebaseClientApp;
|
|
11
|
+
exports.getFirebaseStorage = getFirebaseStorage;
|
|
6
12
|
exports.createFirebaseClientPlugin = createFirebaseClientPlugin;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
+
let clientApp = null;
|
|
14
|
+
let clientStorage = null;
|
|
15
|
+
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
16
|
+
function initClient(inject = {}) {
|
|
17
|
+
if (!clientApp) {
|
|
18
|
+
const initializeApp = inject.initializeApp ?? require("firebase/app").initializeApp;
|
|
19
|
+
const getStorage = inject.getStorage ?? require("firebase/storage").getStorage;
|
|
20
|
+
clientApp = initializeApp({
|
|
13
21
|
apiKey: process.env.APIKEY,
|
|
14
22
|
authDomain: process.env.AUTHDOMAIN,
|
|
15
23
|
projectId: process.env.PROJECTID,
|
|
@@ -17,11 +25,24 @@ function createFirebaseClientPlugin(options = {}) {
|
|
|
17
25
|
messagingSenderId: process.env.MESSAGINGSENDERID,
|
|
18
26
|
appId: process.env.APPID,
|
|
19
27
|
measurementId: process.env.MEASUREMENTID,
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
28
|
+
});
|
|
29
|
+
clientStorage = getStorage(clientApp);
|
|
30
|
+
}
|
|
31
|
+
return { app: clientApp, storage: clientStorage };
|
|
32
|
+
}
|
|
33
|
+
function getFirebaseClientApp() {
|
|
34
|
+
return initClient().app;
|
|
35
|
+
}
|
|
36
|
+
function getFirebaseStorage() {
|
|
37
|
+
return initClient().storage;
|
|
38
|
+
}
|
|
39
|
+
function createFirebaseClientPlugin(options = {}) {
|
|
40
|
+
return async function pluginFirebase(fastify) {
|
|
41
|
+
const { app, storage } = initClient(options);
|
|
42
|
+
fastify.decorate("firebase", app);
|
|
43
|
+
fastify.decorate("storage", storage);
|
|
44
|
+
if (options.decorate)
|
|
45
|
+
await options.decorate(fastify, { app, storage });
|
|
25
46
|
};
|
|
26
47
|
}
|
|
27
48
|
//# sourceMappingURL=client-plugin.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client-plugin.js","sourceRoot":"","sources":["../../src/firebase/client-plugin.ts"],"names":[],"mappings":";AAAA,6EAA6E;AAC7E,6EAA6E;AAC7E,kEAAkE;;
|
|
1
|
+
{"version":3,"file":"client-plugin.js","sourceRoot":"","sources":["../../src/firebase/client-plugin.ts"],"names":[],"mappings":";AAAA,6EAA6E;AAC7E,6EAA6E;AAC7E,kEAAkE;AAClE,EAAE;AACF,uEAAuE;AACvE,4EAA4E;AAC5E,0CAA0C;;AAgC1C,oDAEC;AAED,gDAEC;AAED,gEAOC;AArCD,IAAI,SAAS,GAAQ,IAAI,CAAC;AAC1B,IAAI,aAAa,GAAQ,IAAI,CAAC;AAE9B,uDAAuD;AACvD,SAAS,UAAU,CAAC,SAAsC,EAAE;IACxD,IAAI,CAAC,SAAS,EAAE,CAAC;QACb,MAAM,aAAa,GAAG,MAAM,CAAC,aAAa,IAAI,OAAO,CAAC,cAAc,CAAC,CAAC,aAAa,CAAC;QACpF,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAAC,UAAU,CAAC;QAC/E,SAAS,GAAG,aAAa,CAAC;YACtB,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,MAAM;YAC1B,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU;YAClC,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS;YAChC,aAAa,EAAE,OAAO,CAAC,GAAG,CAAC,aAAa;YACxC,iBAAiB,EAAE,OAAO,CAAC,GAAG,CAAC,iBAAiB;YAChD,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,KAAK;YACxB,aAAa,EAAE,OAAO,CAAC,GAAG,CAAC,aAAa;SAC3C,CAAC,CAAC;QACH,aAAa,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC;IAC1C,CAAC;IACD,OAAO,EAAC,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE,aAAa,EAAC,CAAC;AACpD,CAAC;AAED,SAAgB,oBAAoB;IAChC,OAAO,UAAU,EAAE,CAAC,GAAG,CAAC;AAC5B,CAAC;AAED,SAAgB,kBAAkB;IAC9B,OAAO,UAAU,EAAE,CAAC,OAAO,CAAC;AAChC,CAAC;AAED,SAAgB,0BAA0B,CAAC,UAAuC,EAAE;IAChF,OAAO,KAAK,UAAU,cAAc,CAAC,OAAY;QAC7C,MAAM,EAAC,GAAG,EAAE,OAAO,EAAC,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;QAC3C,OAAO,CAAC,QAAQ,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;QAClC,OAAO,CAAC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QACrC,IAAI,OAAO,CAAC,QAAQ;YAAE,MAAM,OAAO,CAAC,QAAQ,CAAC,OAAO,EAAE,EAAC,GAAG,EAAE,OAAO,EAAC,CAAC,CAAC;IAC1E,CAAC,CAAC;AACN,CAAC"}
|
|
@@ -3,6 +3,8 @@ export interface ErrorHandlerOptions {
|
|
|
3
3
|
genericMessage?: string;
|
|
4
4
|
/** Força expor mensagens cruas (ex: dev local). Default: false */
|
|
5
5
|
exposeInternal?: boolean;
|
|
6
|
+
/** Mensagens por código Prisma (P2002 etc). String fixa ou função(error). */
|
|
7
|
+
prismaMessages?: Record<string, string | ((error: any) => string)>;
|
|
6
8
|
}
|
|
7
9
|
export declare function isInternalError(error: any): boolean;
|
|
8
10
|
export declare function createErrorHandler(options?: ErrorHandlerOptions): (fastify: any) => Promise<void>;
|
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
// infra (Prisma/banco/rede) NUNCA chegam ao cliente com a mensagem crua —
|
|
4
4
|
// "Can't reach database server at /cloudsql/..." no browser do aluno é
|
|
5
5
|
// exposição de infraestrutura. Log completo sempre; resposta sanitizada.
|
|
6
|
+
//
|
|
7
|
+
// - Preserva error.statusCode quando presente (ex: GuardError 403 do post);
|
|
8
|
+
// sem statusCode, mantém o 400 histórico das APIs.
|
|
9
|
+
// - prismaMessages: mapa código Prisma -> mensagem amigável (ex: P2002 ->
|
|
10
|
+
// "Registro duplicado") — a mensagem mapeada é exposta mesmo sendo Prisma.
|
|
6
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
12
|
exports.isInternalError = isInternalError;
|
|
8
13
|
exports.createErrorHandler = createErrorHandler;
|
|
@@ -22,11 +27,20 @@ function createErrorHandler(options = {}) {
|
|
|
22
27
|
return async function onErrorMiddleware(fastify) {
|
|
23
28
|
fastify.setErrorHandler(async (error, request, reply) => {
|
|
24
29
|
console.error(error);
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
+
const status = Number(error?.statusCode) || 400;
|
|
31
|
+
const code = typeof error?.code === "string" ? error.code : "";
|
|
32
|
+
const mapped = code ? options.prismaMessages?.[code] : undefined;
|
|
33
|
+
let message;
|
|
34
|
+
if (mapped !== undefined) {
|
|
35
|
+
message = typeof mapped === "function" ? mapped(error) : mapped;
|
|
36
|
+
}
|
|
37
|
+
else if (options.exposeInternal || !isInternalError(error)) {
|
|
38
|
+
message = error.message;
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
message = generic;
|
|
42
|
+
}
|
|
43
|
+
reply.code(status).send({ statusCode: status, message });
|
|
30
44
|
});
|
|
31
45
|
};
|
|
32
46
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error-handler.js","sourceRoot":"","sources":["../../src/http/error-handler.ts"],"names":[],"mappings":";AAAA,4EAA4E;AAC5E,0EAA0E;AAC1E,uEAAuE;AACvE,yEAAyE;;
|
|
1
|
+
{"version":3,"file":"error-handler.js","sourceRoot":"","sources":["../../src/http/error-handler.ts"],"names":[],"mappings":";AAAA,4EAA4E;AAC5E,0EAA0E;AAC1E,uEAAuE;AACvE,yEAAyE;AACzE,EAAE;AACF,4EAA4E;AAC5E,qDAAqD;AACrD,0EAA0E;AAC1E,6EAA6E;;AAc7E,0CAKC;AAED,gDAsBC;AAhCD,yEAAyE;AACzE,MAAM,iBAAiB,GAAG,mHAAmH,CAAC;AAE9I,SAAgB,eAAe,CAAC,KAAU;IACtC,IAAI,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,cAAc,CAAC;QAAE,OAAO,IAAI,CAAC;IAC3D,IAAI,OAAO,KAAK,EAAE,IAAI,KAAK,QAAQ,IAAI,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAChF,IAAI,CAAC,KAAK,EAAE,UAAU,IAAI,CAAC,CAAC,IAAI,GAAG;QAAE,OAAO,IAAI,CAAC;IACjD,OAAO,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,IAAI,EAAE,CAAC,CAAC,CAAC;AAChE,CAAC;AAED,SAAgB,kBAAkB,CAAC,UAA+B,EAAE;IAChE,MAAM,OAAO,GAAG,OAAO,CAAC,cAAc,IAAI,+BAA+B,CAAC;IAC1E,OAAO,KAAK,UAAU,iBAAiB,CAAC,OAAY;QAChD,OAAO,CAAC,eAAe,CAAC,KAAK,EAAE,KAAU,EAAE,OAAY,EAAE,KAAU,EAAE,EAAE;YACnE,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAErB,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,EAAE,UAAU,CAAC,IAAI,GAAG,CAAC;YAEhD,MAAM,IAAI,GAAG,OAAO,KAAK,EAAE,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YAC/D,MAAM,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,cAAc,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YACjE,IAAI,OAAe,CAAC;YACpB,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;gBACvB,OAAO,GAAG,OAAO,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;YACpE,CAAC;iBAAM,IAAI,OAAO,CAAC,cAAc,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC3D,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;YAC5B,CAAC;iBAAM,CAAC;gBACJ,OAAO,GAAG,OAAO,CAAC;YACtB,CAAC;YAED,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,EAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAC,CAAC,CAAC;QAC3D,CAAC,CAAC,CAAC;IACP,CAAC,CAAC;AACN,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -7,14 +7,16 @@ export type { AuthMiddlewareOptions, TenantAuthLike } from "./tenant/auth";
|
|
|
7
7
|
export { createMultitenantPrisma } from "./prisma/multitenant";
|
|
8
8
|
export { createMultitenantPrismaMariaDb } from "./prisma/mariadb-factory";
|
|
9
9
|
export type { MariaDbMultitenantOptions } from "./prisma/mariadb-factory";
|
|
10
|
+
export { withTenantSchema } from "./prisma/raw-tenant";
|
|
11
|
+
export type { TenantSchemaClientLike } from "./prisma/raw-tenant";
|
|
10
12
|
export type { TenantClient, Multitenant, MultitenantOptions, PoolConfigLike, PoolLike, } from "./prisma/multitenant";
|
|
11
13
|
export { createCorsPlugin, DEFAULT_CORS } from "./cors/plugin";
|
|
12
14
|
export type { CorsOptions } from "./cors/plugin";
|
|
13
15
|
export { createSwaggerPlugin } from "./swagger/plugin";
|
|
14
16
|
export type { SwaggerPluginOptions } from "./swagger/plugin";
|
|
15
|
-
export { createFirebaseClientPlugin } from "./firebase/client-plugin";
|
|
17
|
+
export { createFirebaseClientPlugin, getFirebaseClientApp, getFirebaseStorage } from "./firebase/client-plugin";
|
|
16
18
|
export type { FirebaseClientPluginOptions } from "./firebase/client-plugin";
|
|
17
|
-
export { initFirebaseAdmin, getTenantAuth } from "./firebase/admin";
|
|
19
|
+
export { initFirebaseAdmin, getTenantAuth, getDefaultAuth, getBucket } from "./firebase/admin";
|
|
18
20
|
export type { FirebaseAdminOptions } from "./firebase/admin";
|
|
19
21
|
export { createErrorHandler, isInternalError } from "./http/error-handler";
|
|
20
22
|
export type { ErrorHandlerOptions } from "./http/error-handler";
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.EMBED_MODEL = exports.GEMINI_MODEL = exports.EMBED_DIM = exports.GeminiHelp = exports.isInternalError = exports.createErrorHandler = exports.getTenantAuth = exports.initFirebaseAdmin = exports.createFirebaseClientPlugin = exports.createSwaggerPlugin = exports.DEFAULT_CORS = exports.createCorsPlugin = exports.createMultitenantPrismaMariaDb = exports.createMultitenantPrisma = exports.createAuthMiddleware = exports.createTenantMiddleware = exports.resolveTenantId = void 0;
|
|
3
|
+
exports.EMBED_MODEL = exports.GEMINI_MODEL = exports.EMBED_DIM = exports.GeminiHelp = exports.isInternalError = exports.createErrorHandler = exports.getBucket = exports.getDefaultAuth = exports.getTenantAuth = exports.initFirebaseAdmin = exports.getFirebaseStorage = exports.getFirebaseClientApp = exports.createFirebaseClientPlugin = exports.createSwaggerPlugin = exports.DEFAULT_CORS = exports.createCorsPlugin = exports.withTenantSchema = exports.createMultitenantPrismaMariaDb = exports.createMultitenantPrisma = exports.createAuthMiddleware = exports.createTenantMiddleware = exports.resolveTenantId = void 0;
|
|
4
4
|
var resolve_1 = require("./tenant/resolve");
|
|
5
5
|
Object.defineProperty(exports, "resolveTenantId", { enumerable: true, get: function () { return resolve_1.resolveTenantId; } });
|
|
6
6
|
var middleware_1 = require("./tenant/middleware");
|
|
@@ -11,6 +11,8 @@ var multitenant_1 = require("./prisma/multitenant");
|
|
|
11
11
|
Object.defineProperty(exports, "createMultitenantPrisma", { enumerable: true, get: function () { return multitenant_1.createMultitenantPrisma; } });
|
|
12
12
|
var mariadb_factory_1 = require("./prisma/mariadb-factory");
|
|
13
13
|
Object.defineProperty(exports, "createMultitenantPrismaMariaDb", { enumerable: true, get: function () { return mariadb_factory_1.createMultitenantPrismaMariaDb; } });
|
|
14
|
+
var raw_tenant_1 = require("./prisma/raw-tenant");
|
|
15
|
+
Object.defineProperty(exports, "withTenantSchema", { enumerable: true, get: function () { return raw_tenant_1.withTenantSchema; } });
|
|
14
16
|
var plugin_1 = require("./cors/plugin");
|
|
15
17
|
Object.defineProperty(exports, "createCorsPlugin", { enumerable: true, get: function () { return plugin_1.createCorsPlugin; } });
|
|
16
18
|
Object.defineProperty(exports, "DEFAULT_CORS", { enumerable: true, get: function () { return plugin_1.DEFAULT_CORS; } });
|
|
@@ -18,9 +20,13 @@ var plugin_2 = require("./swagger/plugin");
|
|
|
18
20
|
Object.defineProperty(exports, "createSwaggerPlugin", { enumerable: true, get: function () { return plugin_2.createSwaggerPlugin; } });
|
|
19
21
|
var client_plugin_1 = require("./firebase/client-plugin");
|
|
20
22
|
Object.defineProperty(exports, "createFirebaseClientPlugin", { enumerable: true, get: function () { return client_plugin_1.createFirebaseClientPlugin; } });
|
|
23
|
+
Object.defineProperty(exports, "getFirebaseClientApp", { enumerable: true, get: function () { return client_plugin_1.getFirebaseClientApp; } });
|
|
24
|
+
Object.defineProperty(exports, "getFirebaseStorage", { enumerable: true, get: function () { return client_plugin_1.getFirebaseStorage; } });
|
|
21
25
|
var admin_1 = require("./firebase/admin");
|
|
22
26
|
Object.defineProperty(exports, "initFirebaseAdmin", { enumerable: true, get: function () { return admin_1.initFirebaseAdmin; } });
|
|
23
27
|
Object.defineProperty(exports, "getTenantAuth", { enumerable: true, get: function () { return admin_1.getTenantAuth; } });
|
|
28
|
+
Object.defineProperty(exports, "getDefaultAuth", { enumerable: true, get: function () { return admin_1.getDefaultAuth; } });
|
|
29
|
+
Object.defineProperty(exports, "getBucket", { enumerable: true, get: function () { return admin_1.getBucket; } });
|
|
24
30
|
var error_handler_1 = require("./http/error-handler");
|
|
25
31
|
Object.defineProperty(exports, "createErrorHandler", { enumerable: true, get: function () { return error_handler_1.createErrorHandler; } });
|
|
26
32
|
Object.defineProperty(exports, "isInternalError", { enumerable: true, get: function () { return error_handler_1.isInternalError; } });
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,4CAAiD;AAAzC,0GAAA,eAAe,OAAA;AAGvB,kDAA2D;AAAnD,oHAAA,sBAAsB,OAAA;AAG9B,sCAAmD;AAA3C,4GAAA,oBAAoB,OAAA;AAG5B,oDAA6D;AAArD,sHAAA,uBAAuB,OAAA;AAC/B,4DAAwE;AAAhE,iIAAA,8BAA8B,OAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,4CAAiD;AAAzC,0GAAA,eAAe,OAAA;AAGvB,kDAA2D;AAAnD,oHAAA,sBAAsB,OAAA;AAG9B,sCAAmD;AAA3C,4GAAA,oBAAoB,OAAA;AAG5B,oDAA6D;AAArD,sHAAA,uBAAuB,OAAA;AAC/B,4DAAwE;AAAhE,iIAAA,8BAA8B,OAAA;AAGtC,kDAAqD;AAA7C,8GAAA,gBAAgB,OAAA;AAUxB,wCAA6D;AAArD,0GAAA,gBAAgB,OAAA;AAAE,sGAAA,YAAY,OAAA;AAGtC,2CAAqD;AAA7C,6GAAA,mBAAmB,OAAA;AAG3B,0DAA8G;AAAtG,2HAAA,0BAA0B,OAAA;AAAE,qHAAA,oBAAoB,OAAA;AAAE,mHAAA,kBAAkB,OAAA;AAG5E,0CAA6F;AAArF,0GAAA,iBAAiB,OAAA;AAAE,sGAAA,aAAa,OAAA;AAAE,uGAAA,cAAc,OAAA;AAAE,kGAAA,SAAS,OAAA;AAGnE,sDAAyE;AAAjE,mHAAA,kBAAkB,OAAA;AAAE,gHAAA,eAAe,OAAA;AAG3C,sCAA6E;AAArE,oGAAA,UAAU,OAAA;AAAE,mGAAA,SAAS,OAAA;AAAE,sGAAA,YAAY,OAAA;AAAE,qGAAA,WAAW,OAAA"}
|
|
@@ -32,6 +32,12 @@ function createMultitenantPrisma(options) {
|
|
|
32
32
|
// workaround do leak de prepared statements com pool externo
|
|
33
33
|
// (https://github.com/prisma/prisma/issues/29364)
|
|
34
34
|
prepareCacheLength: 0,
|
|
35
|
+
// defesa em profundidade contra conexão suja (ex.: `USE` cru em relatório
|
|
36
|
+
// que escape do withTenantSchema): reseta a sessão ao devolver a conexão.
|
|
37
|
+
// ATENÇÃO: em MySQL o driver mariadb NÃO emite COM_RESET_CONNECTION (só em
|
|
38
|
+
// MariaDB) — cai num ROLLBACK que preserva o database selecionado. Por isso
|
|
39
|
+
// a proteção real do vazamento cross-tenant é o withTenantSchema, não isto.
|
|
40
|
+
resetAfterUse: true,
|
|
35
41
|
});
|
|
36
42
|
const tenantDbName = options.tenantDbName ?? defaultTenantDbName;
|
|
37
43
|
const clients = new Map();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"multitenant.js","sourceRoot":"","sources":["../../src/prisma/multitenant.ts"],"names":[],"mappings":";AAAA,6EAA6E;AAC7E,2EAA2E;AAC3E,2EAA2E;AAC3E,2EAA2E;AAC3E,2EAA2E;AAC3E,EAAE;AACF,4EAA4E;AAC5E,2EAA2E;;
|
|
1
|
+
{"version":3,"file":"multitenant.js","sourceRoot":"","sources":["../../src/prisma/multitenant.ts"],"names":[],"mappings":";AAAA,6EAA6E;AAC7E,2EAA2E;AAC3E,2EAA2E;AAC3E,2EAA2E;AAC3E,2EAA2E;AAC3E,EAAE;AACF,4EAA4E;AAC5E,2EAA2E;;AA8D3E,0DAkFC;AAtFD,SAAS,mBAAmB,CAAC,UAAkB,EAAE,QAAgB;IAC7D,OAAO,UAAU,CAAC,OAAO,CAAC,UAAU,EAAE,UAAU,QAAQ,EAAE,CAAC,CAAC;AAChE,CAAC;AAED,SAAgB,uBAAuB,CACnC,OAA2C;IAE3C,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;IAC5C,MAAM,UAAU,GAAG,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,SAAS,CAAC;IAClE,MAAM,UAAU,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACpE,MAAM,SAAS,GACX,OAAO,CAAC,SAAS;QACjB,MAAM,CACF,OAAO,CAAC,GAAG,CAAC,iBAAiB;YAC7B,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,kBAAkB,CAAC;YAC3C,CAAC,CACJ,CAAC;IAEN,MAAM,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC;QAC5B,GAAG,CAAC,UAAU;YACV,CAAC,CAAC,EAAC,UAAU,EAAC;YACd,CAAC,CAAC,EAAC,IAAI,EAAE,MAAM,CAAC,QAAQ,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,IAAI,IAAI,CAAC,EAAC,CAAC;QACjE,IAAI,EAAE,kBAAkB,CAAC,MAAM,CAAC,QAAQ,CAAC;QACzC,QAAQ,EAAE,kBAAkB,CAAC,MAAM,CAAC,QAAQ,CAAC;QAC7C,eAAe,EAAE,SAAS;QAC1B,cAAc,EAAE,OAAO,CAAC,cAAc,IAAI,KAAK;QAC/C,cAAc,EAAE,OAAO,CAAC,cAAc,IAAI,KAAK;QAC/C,6DAA6D;QAC7D,kDAAkD;QAClD,kBAAkB,EAAE,CAAC;QACrB,0EAA0E;QAC1E,0EAA0E;QAC1E,2EAA2E;QAC3E,4EAA4E;QAC5E,4EAA4E;QAC5E,aAAa,EAAE,IAAI;KACtB,CAAC,CAAC;IAEH,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,mBAAmB,CAAC;IACjE,MAAM,OAAO,GAAG,IAAI,GAAG,EAAiC,CAAC;IAEzD,SAAS,SAAS,CAAC,MAAc;QAC7B,IAAI,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACjC,IAAI,CAAC,MAAM,EAAE,CAAC;YACV,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,aAAa,CAAC,IAAI,EAAE,EAAC,QAAQ,EAAE,MAAM,EAAC,CAAC,CAAC;YACpE,MAAM,GAAG,IAAI,OAAO,CAAC,YAAY,CAAC;gBAC9B,OAAO;gBACP,GAAG,EAAE,OAAO,CAAC,GAAG;gBAChB,WAAW,EAAE,OAAO,CAAC,WAAW;aACnC,CAA0B,CAAC;YAC3B,MAAc,CAAC,OAAO,GAAG,MAAM,CAAC;YACjC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAChC,CAAC;QACD,OAAO,MAAM,CAAC;IAClB,CAAC;IAED,MAAM,aAAa,GAAG,SAAS,CAAC,UAAU,CAAC,CAAC;IAE5C,SAAS,SAAS,CAAC,QAAgB;QAC/B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,OAAO,UAAU,CAAC;QACxE,OAAO,YAAY,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IAC9C,CAAC;IAED,SAAS,eAAe,CAAC,QAAgB;QACrC,mEAAmE;QACnE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,OAAO,aAAa,CAAC;QAC3E,OAAO,SAAS,CAAC,YAAY,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC;IACzD,CAAC;IAED,KAAK,UAAU,aAAa;QACxB,MAAM,OAAO,CAAC,GAAG,CACb,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAChC,CAAS,CAAC,WAAW,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE;QACtC,CAAC,CAAC,CACL,CACJ,CAAC;QACF,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE;QAC5B,CAAC,CAAC,CAAC;IACP,CAAC;IAED,MAAM,MAAM,GAAG,KAAK,EAAE,OAAY,EAAE,EAAE;QAClC,OAAO,CAAC,QAAQ,CAAC,eAAe,EAAE,aAAa,CAAC,CAAC;QACjD,OAAO,CAAC,OAAO,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;IAC9C,CAAC,CAAC;IAEF,OAAO,EAAC,IAAI,EAAE,aAAa,EAAE,eAAe,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,EAAC,CAAC;AACpF,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export interface TenantSchemaClientLike {
|
|
2
|
+
/** Nome do database do tenant (setado pelo multitenant como client.$schema) */
|
|
3
|
+
$schema: string;
|
|
4
|
+
/** $transaction interativa do PrismaClient */
|
|
5
|
+
$transaction: <R>(fn: (tx: any) => Promise<R>) => Promise<R>;
|
|
6
|
+
}
|
|
7
|
+
export declare function withTenantSchema<R>(prisma: TenantSchemaClientLike, fn: (tx: any) => Promise<R>): Promise<R>;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Executa SQL cru (relatórios/views com tabelas SEM qualificar) contra o
|
|
3
|
+
// database do tenant, sem deixar a conexão suja no pool.
|
|
4
|
+
//
|
|
5
|
+
// O pool é único e SEM database default (ver multitenant.ts): um raw não
|
|
6
|
+
// qualificado precisa de `USE` para selecionar o schema. Mas o `USE` gruda o
|
|
7
|
+
// database selecionado na conexão; quando ela volta ao pool, o próximo
|
|
8
|
+
// request de OUTRA escola herda esse database e um raw não qualificado rodaria
|
|
9
|
+
// contra o tenant errado — vazamento cross-tenant.
|
|
10
|
+
//
|
|
11
|
+
// `resetAfterUse` do pool NÃO resolve em MySQL: o driver mariadb só emite
|
|
12
|
+
// COM_RESET_CONNECTION (que limparia o database) quando o servidor é MariaDB;
|
|
13
|
+
// em MySQL cai num ROLLBACK que preserva o database selecionado. Por isso a
|
|
14
|
+
// limpeza é explícita aqui: a transação prende UMA conexão, o `USE` seleciona
|
|
15
|
+
// o schema do tenant e o `finally` devolve a conexão neutralizada
|
|
16
|
+
// (`USE information_schema`) ANTES de o commit/rollback liberá-la — em qualquer
|
|
17
|
+
// caminho, sucesso ou erro.
|
|
18
|
+
//
|
|
19
|
+
// information_schema existe em todo MySQL/MariaDB e não tem tabelas de app;
|
|
20
|
+
// assim, um raw não qualificado que porventura vaze cai em "tabela não existe"
|
|
21
|
+
// (falha alto) em vez de atingir silenciosamente outra escola.
|
|
22
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
+
exports.withTenantSchema = withTenantSchema;
|
|
24
|
+
async function withTenantSchema(prisma, fn) {
|
|
25
|
+
const schema = prisma.$schema;
|
|
26
|
+
// o nome entra cru num `USE` — só aceita identificador de database seguro
|
|
27
|
+
if (!schema || !/^[A-Za-z0-9_$]+$/.test(schema)) {
|
|
28
|
+
throw new Error(`withTenantSchema: nome de schema inválido: ${schema}`);
|
|
29
|
+
}
|
|
30
|
+
return prisma.$transaction(async (tx) => {
|
|
31
|
+
await tx.$executeRawUnsafe(`USE \`${schema}\``);
|
|
32
|
+
try {
|
|
33
|
+
return await fn(tx);
|
|
34
|
+
}
|
|
35
|
+
finally {
|
|
36
|
+
// neutraliza a conexão antes de voltar ao pool — não pode derrubar a task
|
|
37
|
+
await tx.$executeRawUnsafe("USE `information_schema`").catch(() => { });
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=raw-tenant.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"raw-tenant.js","sourceRoot":"","sources":["../../src/prisma/raw-tenant.ts"],"names":[],"mappings":";AAAA,yEAAyE;AACzE,yDAAyD;AACzD,EAAE;AACF,yEAAyE;AACzE,6EAA6E;AAC7E,uEAAuE;AACvE,+EAA+E;AAC/E,mDAAmD;AACnD,EAAE;AACF,0EAA0E;AAC1E,8EAA8E;AAC9E,4EAA4E;AAC5E,8EAA8E;AAC9E,kEAAkE;AAClE,gFAAgF;AAChF,4BAA4B;AAC5B,EAAE;AACF,4EAA4E;AAC5E,+EAA+E;AAC/E,+DAA+D;;AAS/D,4CAkBC;AAlBM,KAAK,UAAU,gBAAgB,CAClC,MAA8B,EAC9B,EAA2B;IAE3B,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC;IAC9B,0EAA0E;IAC1E,IAAI,CAAC,MAAM,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QAC9C,MAAM,IAAI,KAAK,CAAC,8CAA8C,MAAM,EAAE,CAAC,CAAC;IAC5E,CAAC;IACD,OAAO,MAAM,CAAC,YAAY,CAAC,KAAK,EAAE,EAAO,EAAE,EAAE;QACzC,MAAM,EAAE,CAAC,iBAAiB,CAAC,SAAS,MAAM,IAAI,CAAC,CAAC;QAChD,IAAI,CAAC;YACD,OAAO,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;QACxB,CAAC;gBAAS,CAAC;YACP,0EAA0E;YAC1E,MAAM,EAAE,CAAC,iBAAiB,CAAC,0BAA0B,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAC3E,CAAC;IACL,CAAC,CAAC,CAAC;AACP,CAAC"}
|
package/dist/swagger/plugin.d.ts
CHANGED
|
@@ -3,5 +3,7 @@ export interface SwaggerPluginOptions {
|
|
|
3
3
|
description: string;
|
|
4
4
|
version?: string;
|
|
5
5
|
routePrefix?: string;
|
|
6
|
+
/** CSP estático do Swagger UI (magazines usa false). Default: true */
|
|
7
|
+
staticCSP?: boolean;
|
|
6
8
|
}
|
|
7
9
|
export declare function createSwaggerPlugin(options: SwaggerPluginOptions): (fastify: any) => Promise<void>;
|
package/dist/swagger/plugin.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/swagger/plugin.ts"],"names":[],"mappings":";AAAA,wEAAwE;AACxE,0EAA0E;AAC1E,yEAAyE;;
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/swagger/plugin.ts"],"names":[],"mappings":";AAAA,wEAAwE;AACxE,0EAA0E;AAC1E,yEAAyE;;AAsBzE,kDAwCC;AAnDD,yEAAyE;AACzE,qEAAqE;AACrE,aAAa;AACb,IAAI,CAAE,MAAM,CAAC,SAAiB,CAAC,MAAM,EAAE,CAAC;IACpC,aAAa;IACb,MAAM,CAAC,SAAS,CAAC,MAAM,GAAG;QACtB,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC7C,OAAO,GAAG,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;IAClC,CAAC,CAAC;AACN,CAAC;AAED,SAAgB,mBAAmB,CAAC,OAA6B;IAC7D,OAAO,KAAK,UAAU,aAAa,CAAC,OAAY;QAC5C,uDAAuD;QACvD,MAAM,OAAO,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;QAC5C,MAAM,SAAS,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAC;QAEjD,MAAM,OAAO,CAAC,QAAQ,CAAC,OAAO,EAAE;YAC5B,SAAS,EAAE,IAAI;YACf,WAAW,EAAE,IAAI;YACjB,OAAO,EAAE;gBACL,IAAI,EAAE;oBACF,KAAK,EAAE,OAAO,CAAC,KAAK;oBACpB,WAAW,EAAE,OAAO,CAAC,WAAW;oBAChC,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,OAAO;iBACtC;gBACD,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,MAAM;gBACxB,OAAO,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;gBAC3B,mBAAmB,EAAE;oBACjB,UAAU,EAAE;wBACR,IAAI,EAAE,QAAQ;wBACd,IAAI,EAAE,eAAe;wBACrB,EAAE,EAAE,QAAQ;wBACZ,WAAW,EAAE,wDAAwD;qBACxE;iBACJ;gBACD,QAAQ,EAAE,CAAC,EAAC,UAAU,EAAE,EAAE,EAAC,CAAC;aAC/B;SACJ,CAAC,CAAC;QAEH,MAAM,OAAO,CAAC,QAAQ,CAAC,SAAS,EAAE;YAC9B,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,gBAAgB;YACpD,QAAQ,EAAE;gBACN,YAAY,EAAE,MAAM;gBACpB,WAAW,EAAE,KAAK;aACrB;YACD,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,IAAI;YACpC,WAAW,EAAE,IAAI;YACjB,kBAAkB,EAAE,CAAC,MAAW,EAAE,EAAE,CAAC,MAAM;SAC9C,CAAC,CAAC;IACP,CAAC,CAAC;AACN,CAAC"}
|
package/dist/tenant/auth.d.ts
CHANGED
|
@@ -2,10 +2,13 @@ export interface TenantAuthLike {
|
|
|
2
2
|
verifyIdToken(token: string, checkRevoked?: boolean): Promise<any>;
|
|
3
3
|
}
|
|
4
4
|
export interface AuthMiddlewareOptions {
|
|
5
|
-
/** Rotas públicas (regex sobre
|
|
5
|
+
/** Rotas públicas (regex sobre o PATHNAME, sem query string) — dispensam token */
|
|
6
6
|
publicRoutes: RegExp[];
|
|
7
7
|
/** Resolve o Auth do tenant Firebase (ex: getTenantAuth do pkg) */
|
|
8
8
|
getTenantAuth: (tenantId: string | undefined) => TenantAuthLike;
|
|
9
|
+
/** false = API single-tenant (ex: core): não lê o claim firebase.tenant e
|
|
10
|
+
* chama getTenantAuth(undefined) — passe () => getDefaultAuth(). Default: true */
|
|
11
|
+
tenantAware?: boolean;
|
|
9
12
|
/** Aceitar token via ?token= (default: false — vaza em logs) */
|
|
10
13
|
allowQueryToken?: boolean;
|
|
11
14
|
/** Bypass SEM token (dev local). NUNCA ligar em produção. Default: false */
|
package/dist/tenant/auth.js
CHANGED
|
@@ -46,8 +46,13 @@ function createAuthMiddleware(options) {
|
|
|
46
46
|
return async function authMiddleware(fastify) {
|
|
47
47
|
fastify.decorate("authenticate", async (request, reply) => {
|
|
48
48
|
try {
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
// Testa só o PATHNAME. request.raw.url inclui a query string;
|
|
50
|
+
// testar a URL crua deixava um `?x=/public/a` casar rotas
|
|
51
|
+
// públicas não ancoradas e burlar a autenticação por completo
|
|
52
|
+
// (GET protegido → 401, mas `...?cb=/webhook` → 200 sem token).
|
|
53
|
+
const rawUrl = request.raw?.url || "";
|
|
54
|
+
const pathname = rawUrl.split("?")[0];
|
|
55
|
+
if (options.publicRoutes.some(regex => regex.test(pathname)) || pathname === "" || pathname === "/") {
|
|
51
56
|
return;
|
|
52
57
|
}
|
|
53
58
|
let authHeader = request.headers?.authorization;
|
|
@@ -76,12 +81,16 @@ function createAuthMiddleware(options) {
|
|
|
76
81
|
}
|
|
77
82
|
}
|
|
78
83
|
request.user = cached;
|
|
84
|
+
request.user_id = cached.uid;
|
|
79
85
|
request.authToken = authHeader;
|
|
80
86
|
return;
|
|
81
87
|
}
|
|
82
88
|
// o claim de tenant orienta QUAL Auth valida; a prova é criptográfica
|
|
83
|
-
|
|
84
|
-
|
|
89
|
+
let tenantId;
|
|
90
|
+
if (options.tenantAware !== false) {
|
|
91
|
+
const payload = JSON.parse(Buffer.from(parts[1], "base64").toString("utf-8"));
|
|
92
|
+
tenantId = payload?.firebase?.tenant;
|
|
93
|
+
}
|
|
85
94
|
const auth = options.getTenantAuth(tenantId);
|
|
86
95
|
const decodedToken = await auth.verifyIdToken(authHeader, true);
|
|
87
96
|
setCached(authHeader, decodedToken, decodedToken.exp);
|
|
@@ -94,6 +103,7 @@ function createAuthMiddleware(options) {
|
|
|
94
103
|
}
|
|
95
104
|
}
|
|
96
105
|
request.user = decodedToken;
|
|
106
|
+
request.user_id = decodedToken.uid;
|
|
97
107
|
request.authToken = authHeader;
|
|
98
108
|
}
|
|
99
109
|
catch (err) {
|
package/dist/tenant/auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/tenant/auth.ts"],"names":[],"mappings":";AAAA,8DAA8D;AAC9D,EAAE;AACF,qEAAqE;AACrE,4EAA4E;AAC5E,uEAAuE;AACvE,wEAAwE;AACxE,0EAA0E;AAC1E,6EAA6E;AAC7E,0EAA0E;AAC1E,oEAAoE;AACpE,EAAE;AACF,uEAAuE;AACvE,wEAAwE;AACxE,mDAAmD;;
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/tenant/auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
export interface TenantMiddlewareOptions<TClient> {
|
|
2
2
|
getTenantClient: (idSchool: string) => TClient;
|
|
3
3
|
defaultClient: TClient;
|
|
4
|
+
/** Chamado após resolver o tenant e setar req.prisma; idSchool = null
|
|
5
|
+
* quando o request caiu no default. Erros aqui NÃO derrubam o request
|
|
6
|
+
* (log + segue) — enriquecimento é best-effort. */
|
|
7
|
+
onTenantResolved?: (req: any, idSchool: string | null) => void | Promise<void>;
|
|
4
8
|
}
|
|
5
9
|
export declare function createTenantMiddleware<TClient>(options: TenantMiddlewareOptions<TClient>): (fastify: any) => Promise<void>;
|
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
// para o client default quando não há id válido. É a ÚNICA porta de entrada
|
|
5
5
|
// oficial do Prisma nos handlers — garante que o :id_school da URL decide o
|
|
6
6
|
// schema, nunca o handler.
|
|
7
|
+
//
|
|
8
|
+
// Também seta req.id_school (número; undefined quando sem id válido) e
|
|
9
|
+
// oferece onTenantResolved para enriquecer o request (ex: carregar
|
|
10
|
+
// permissões do usuário) sem cada API duplicar lógica no middleware.
|
|
7
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
12
|
exports.createTenantMiddleware = createTenantMiddleware;
|
|
9
13
|
const resolve_1 = require("./resolve");
|
|
@@ -11,9 +15,21 @@ function createTenantMiddleware(options) {
|
|
|
11
15
|
return async function tenantMiddleware(fastify) {
|
|
12
16
|
fastify.addHook("preValidation", async (req) => {
|
|
13
17
|
const idSchool = (0, resolve_1.resolveTenantId)(req);
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
18
|
+
if (idSchool) {
|
|
19
|
+
req.prisma = options.getTenantClient(idSchool);
|
|
20
|
+
req.id_school = Number(idSchool);
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
req.prisma = options.defaultClient;
|
|
24
|
+
}
|
|
25
|
+
if (options.onTenantResolved) {
|
|
26
|
+
try {
|
|
27
|
+
await options.onTenantResolved(req, idSchool);
|
|
28
|
+
}
|
|
29
|
+
catch (e) {
|
|
30
|
+
console.error("onTenantResolved error:", e);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
17
33
|
});
|
|
18
34
|
};
|
|
19
35
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/tenant/middleware.ts"],"names":[],"mappings":";AAAA,4EAA4E;AAC5E,4EAA4E;AAC5E,4EAA4E;AAC5E,4EAA4E;AAC5E,2BAA2B;;
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/tenant/middleware.ts"],"names":[],"mappings":";AAAA,4EAA4E;AAC5E,4EAA4E;AAC5E,4EAA4E;AAC5E,4EAA4E;AAC5E,2BAA2B;AAC3B,EAAE;AACF,uEAAuE;AACvE,mEAAmE;AACnE,qEAAqE;;AAarE,wDAsBC;AAjCD,uCAA6D;AAW7D,SAAgB,sBAAsB,CAAU,OAAyC;IACrF,OAAO,KAAK,UAAU,gBAAgB,CAAC,OAAY;QAC/C,OAAO,CAAC,OAAO,CAAC,eAAe,EAAE,KAAK,EAAE,GAGvC,EAAE,EAAE;YACD,MAAM,QAAQ,GAAG,IAAA,yBAAe,EAAC,GAAG,CAAC,CAAC;YACtC,IAAI,QAAQ,EAAE,CAAC;gBACX,GAAG,CAAC,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;gBAC/C,GAAG,CAAC,SAAS,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;YACrC,CAAC;iBAAM,CAAC;gBACJ,GAAG,CAAC,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;YACvC,CAAC;YACD,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC;gBAC3B,IAAI,CAAC;oBACD,MAAM,OAAO,CAAC,gBAAgB,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;gBAClD,CAAC;gBAAC,OAAO,CAAC,EAAE,CAAC;oBACT,OAAO,CAAC,KAAK,CAAC,yBAAyB,EAAE,CAAC,CAAC,CAAC;gBAChD,CAAC;YACL,CAAC;QACL,CAAC,CAAC,CAAC;IACP,CAAC,CAAC;AACN,CAAC"}
|