@soulbatical/tetra-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/dist/core/SupabaseUserClient.d.ts +14 -0
- package/dist/core/SupabaseUserClient.d.ts.map +1 -0
- package/dist/core/SupabaseUserClient.js +49 -0
- package/dist/core/SupabaseUserClient.js.map +1 -0
- package/dist/core/adminDb.d.ts +21 -0
- package/dist/core/adminDb.d.ts.map +1 -0
- package/dist/core/adminDb.js +43 -0
- package/dist/core/adminDb.js.map +1 -0
- package/dist/core/publicDb.d.ts +19 -0
- package/dist/core/publicDb.d.ts.map +1 -0
- package/dist/core/publicDb.js +29 -0
- package/dist/core/publicDb.js.map +1 -0
- package/dist/core/superadminDb.d.ts +29 -0
- package/dist/core/superadminDb.d.ts.map +1 -0
- package/dist/core/superadminDb.js +53 -0
- package/dist/core/superadminDb.js.map +1 -0
- package/dist/core/systemDb.d.ts +34 -0
- package/dist/core/systemDb.d.ts.map +1 -0
- package/dist/core/systemDb.js +64 -0
- package/dist/core/systemDb.js.map +1 -0
- package/dist/core/userDb.d.ts +22 -0
- package/dist/core/userDb.d.ts.map +1 -0
- package/dist/core/userDb.js +36 -0
- package/dist/core/userDb.js.map +1 -0
- package/dist/core/webhookDb.d.ts +16 -0
- package/dist/core/webhookDb.d.ts.map +1 -0
- package/dist/core/webhookDb.js +26 -0
- package/dist/core/webhookDb.js.map +1 -0
- package/dist/frontend/components/FeatureFilters.d.ts +81 -0
- package/dist/frontend/components/FeatureFilters.d.ts.map +1 -0
- package/dist/frontend/components/FeatureFilters.js +257 -0
- package/dist/frontend/components/FeatureFilters.js.map +1 -0
- package/dist/frontend/components/FeatureTable.d.ts +96 -0
- package/dist/frontend/components/FeatureTable.d.ts.map +1 -0
- package/dist/frontend/components/FeatureTable.js +279 -0
- package/dist/frontend/components/FeatureTable.js.map +1 -0
- package/dist/frontend/hooks/useFeature.d.ts +108 -0
- package/dist/frontend/hooks/useFeature.d.ts.map +1 -0
- package/dist/frontend/hooks/useFeature.js +354 -0
- package/dist/frontend/hooks/useFeature.js.map +1 -0
- package/dist/frontend/index.d.ts +17 -0
- package/dist/frontend/index.d.ts.map +1 -0
- package/dist/frontend/index.js +14 -0
- package/dist/frontend/index.js.map +1 -0
- package/dist/index.d.ts +78 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +71 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/authMiddleware.d.ts +137 -0
- package/dist/middleware/authMiddleware.d.ts.map +1 -0
- package/dist/middleware/authMiddleware.js +292 -0
- package/dist/middleware/authMiddleware.js.map +1 -0
- package/dist/middleware/autoRegisterValidators.d.ts +31 -0
- package/dist/middleware/autoRegisterValidators.d.ts.map +1 -0
- package/dist/middleware/autoRegisterValidators.js +125 -0
- package/dist/middleware/autoRegisterValidators.js.map +1 -0
- package/dist/middleware/validateQueryParams.d.ts +62 -0
- package/dist/middleware/validateQueryParams.d.ts.map +1 -0
- package/dist/middleware/validateQueryParams.js +321 -0
- package/dist/middleware/validateQueryParams.js.map +1 -0
- package/dist/shared/controllers/BaseMutationController.d.ts +79 -0
- package/dist/shared/controllers/BaseMutationController.d.ts.map +1 -0
- package/dist/shared/controllers/BaseMutationController.js +241 -0
- package/dist/shared/controllers/BaseMutationController.js.map +1 -0
- package/dist/shared/controllers/BaseQueryController.d.ts +72 -0
- package/dist/shared/controllers/BaseQueryController.d.ts.map +1 -0
- package/dist/shared/controllers/BaseQueryController.js +375 -0
- package/dist/shared/controllers/BaseQueryController.js.map +1 -0
- package/dist/shared/controllers/FilterConfigController.d.ts +37 -0
- package/dist/shared/controllers/FilterConfigController.d.ts.map +1 -0
- package/dist/shared/controllers/FilterConfigController.js +94 -0
- package/dist/shared/controllers/FilterConfigController.js.map +1 -0
- package/dist/shared/controllers/index.d.ts +5 -0
- package/dist/shared/controllers/index.d.ts.map +1 -0
- package/dist/shared/controllers/index.js +4 -0
- package/dist/shared/controllers/index.js.map +1 -0
- package/dist/shared/controllers/types.d.ts +57 -0
- package/dist/shared/controllers/types.d.ts.map +1 -0
- package/dist/shared/controllers/types.js +2 -0
- package/dist/shared/controllers/types.js.map +1 -0
- package/dist/shared/factories/BatchRouteFactory.d.ts +33 -0
- package/dist/shared/factories/BatchRouteFactory.d.ts.map +1 -0
- package/dist/shared/factories/BatchRouteFactory.js +54 -0
- package/dist/shared/factories/BatchRouteFactory.js.map +1 -0
- package/dist/shared/factories/MutationRouteFactory.d.ts +27 -0
- package/dist/shared/factories/MutationRouteFactory.d.ts.map +1 -0
- package/dist/shared/factories/MutationRouteFactory.js +39 -0
- package/dist/shared/factories/MutationRouteFactory.js.map +1 -0
- package/dist/shared/factories/PhaseRouteFactory.d.ts +33 -0
- package/dist/shared/factories/PhaseRouteFactory.d.ts.map +1 -0
- package/dist/shared/factories/PhaseRouteFactory.js +67 -0
- package/dist/shared/factories/PhaseRouteFactory.js.map +1 -0
- package/dist/shared/factories/QueryRouteFactory.d.ts +37 -0
- package/dist/shared/factories/QueryRouteFactory.d.ts.map +1 -0
- package/dist/shared/factories/QueryRouteFactory.js +244 -0
- package/dist/shared/factories/QueryRouteFactory.js.map +1 -0
- package/dist/shared/factories/QueryServiceFactory.d.ts +282 -0
- package/dist/shared/factories/QueryServiceFactory.d.ts.map +1 -0
- package/dist/shared/factories/QueryServiceFactory.js +277 -0
- package/dist/shared/factories/QueryServiceFactory.js.map +1 -0
- package/dist/shared/factories/index.d.ts +8 -0
- package/dist/shared/factories/index.d.ts.map +1 -0
- package/dist/shared/factories/index.js +6 -0
- package/dist/shared/factories/index.js.map +1 -0
- package/dist/shared/factories/types.d.ts +98 -0
- package/dist/shared/factories/types.d.ts.map +1 -0
- package/dist/shared/factories/types.js +8 -0
- package/dist/shared/factories/types.js.map +1 -0
- package/dist/shared/ownership/types.d.ts +84 -0
- package/dist/shared/ownership/types.d.ts.map +1 -0
- package/dist/shared/ownership/types.js +8 -0
- package/dist/shared/ownership/types.js.map +1 -0
- package/dist/shared/rfc7807ErrorResponse.d.ts +54 -0
- package/dist/shared/rfc7807ErrorResponse.d.ts.map +1 -0
- package/dist/shared/rfc7807ErrorResponse.js +98 -0
- package/dist/shared/rfc7807ErrorResponse.js.map +1 -0
- package/dist/shared/services/BaseCronService.d.ts +171 -0
- package/dist/shared/services/BaseCronService.d.ts.map +1 -0
- package/dist/shared/services/BaseCronService.js +444 -0
- package/dist/shared/services/BaseCronService.js.map +1 -0
- package/dist/shared/services/BasePhaseService.d.ts +170 -0
- package/dist/shared/services/BasePhaseService.d.ts.map +1 -0
- package/dist/shared/services/BasePhaseService.js +409 -0
- package/dist/shared/services/BasePhaseService.js.map +1 -0
- package/dist/shared/services/CronRegistry.d.ts +67 -0
- package/dist/shared/services/CronRegistry.d.ts.map +1 -0
- package/dist/shared/services/CronRegistry.js +68 -0
- package/dist/shared/services/CronRegistry.js.map +1 -0
- package/dist/shared/services/SimpleRPCQueryService.d.ts +111 -0
- package/dist/shared/services/SimpleRPCQueryService.d.ts.map +1 -0
- package/dist/shared/services/SimpleRPCQueryService.js +265 -0
- package/dist/shared/services/SimpleRPCQueryService.js.map +1 -0
- package/dist/shared/types/feature-config.d.ts +611 -0
- package/dist/shared/types/feature-config.d.ts.map +1 -0
- package/dist/shared/types/feature-config.js +85 -0
- package/dist/shared/types/feature-config.js.map +1 -0
- package/dist/shared/types/query-config.d.ts +41 -0
- package/dist/shared/types/query-config.d.ts.map +1 -0
- package/dist/shared/types/query-config.js +6 -0
- package/dist/shared/types/query-config.js.map +1 -0
- package/dist/shared/utils/config-driven-filters.d.ts +215 -0
- package/dist/shared/utils/config-driven-filters.d.ts.map +1 -0
- package/dist/shared/utils/config-driven-filters.js +451 -0
- package/dist/shared/utils/config-driven-filters.js.map +1 -0
- package/dist/shared/utils/controllerErrorHandler.d.ts +44 -0
- package/dist/shared/utils/controllerErrorHandler.d.ts.map +1 -0
- package/dist/shared/utils/controllerErrorHandler.js +126 -0
- package/dist/shared/utils/controllerErrorHandler.js.map +1 -0
- package/dist/shared/utils/parseInclude.d.ts +14 -0
- package/dist/shared/utils/parseInclude.d.ts.map +1 -0
- package/dist/shared/utils/parseInclude.js +28 -0
- package/dist/shared/utils/parseInclude.js.map +1 -0
- package/dist/shared/utils/queryHelpers.d.ts +62 -0
- package/dist/shared/utils/queryHelpers.d.ts.map +1 -0
- package/dist/shared/utils/queryHelpers.js +61 -0
- package/dist/shared/utils/queryHelpers.js.map +1 -0
- package/dist/shared/utils/response-mapper.d.ts +42 -0
- package/dist/shared/utils/response-mapper.d.ts.map +1 -0
- package/dist/shared/utils/response-mapper.js +176 -0
- package/dist/shared/utils/response-mapper.js.map +1 -0
- package/dist/shared/utils/responseBuilder.d.ts +103 -0
- package/dist/shared/utils/responseBuilder.d.ts.map +1 -0
- package/dist/shared/utils/responseBuilder.js +131 -0
- package/dist/shared/utils/responseBuilder.js.map +1 -0
- package/dist/shared/validators/featureConfigValidator.d.ts +23 -0
- package/dist/shared/validators/featureConfigValidator.d.ts.map +1 -0
- package/dist/shared/validators/featureConfigValidator.js +143 -0
- package/dist/shared/validators/featureConfigValidator.js.map +1 -0
- package/dist/shared/validators/organizationValidator.d.ts +53 -0
- package/dist/shared/validators/organizationValidator.d.ts.map +1 -0
- package/dist/shared/validators/organizationValidator.js +69 -0
- package/dist/shared/validators/organizationValidator.js.map +1 -0
- package/dist/shared/validators/uuidValidator.d.ts +57 -0
- package/dist/shared/validators/uuidValidator.d.ts.map +1 -0
- package/dist/shared/validators/uuidValidator.js +77 -0
- package/dist/shared/validators/uuidValidator.js.map +1 -0
- package/dist/utils/logger.d.ts +40 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +47 -0
- package/dist/utils/logger.js.map +1 -0
- package/package.json +80 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { SupabaseClient } from '@supabase/supabase-js';
|
|
2
|
+
export declare class SupabaseUserClient {
|
|
3
|
+
/**
|
|
4
|
+
* Create a public Supabase client (no authentication)
|
|
5
|
+
* Use this for public endpoints that don't require a user token
|
|
6
|
+
*/
|
|
7
|
+
static getPublicClient(): SupabaseClient;
|
|
8
|
+
/**
|
|
9
|
+
* Create a Supabase client with user authentication context
|
|
10
|
+
* This ensures RLS policies use the authenticated user's permissions
|
|
11
|
+
*/
|
|
12
|
+
static createForUser(userToken: string): Promise<SupabaseClient>;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=SupabaseUserClient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SupabaseUserClient.d.ts","sourceRoot":"","sources":["../../src/core/SupabaseUserClient.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAKrE,qBAAa,kBAAkB;IAC7B;;;OAGG;IACH,MAAM,CAAC,eAAe,IAAI,cAAc;IAgBxC;;;OAGG;WACU,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;CAyBvE"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { createClient } from '@supabase/supabase-js';
|
|
2
|
+
import { createLogger } from '../utils/logger.js';
|
|
3
|
+
const logger = createLogger('database:client:user');
|
|
4
|
+
export class SupabaseUserClient {
|
|
5
|
+
/**
|
|
6
|
+
* Create a public Supabase client (no authentication)
|
|
7
|
+
* Use this for public endpoints that don't require a user token
|
|
8
|
+
*/
|
|
9
|
+
static getPublicClient() {
|
|
10
|
+
const supabaseUrl = process.env.SUPABASE_URL;
|
|
11
|
+
const supabaseAnonKey = process.env.SUPABASE_ANON_KEY;
|
|
12
|
+
if (!supabaseAnonKey) {
|
|
13
|
+
throw new Error('SUPABASE_ANON_KEY not found in environment');
|
|
14
|
+
}
|
|
15
|
+
return createClient(supabaseUrl, supabaseAnonKey, {
|
|
16
|
+
auth: {
|
|
17
|
+
persistSession: false,
|
|
18
|
+
autoRefreshToken: false
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Create a Supabase client with user authentication context
|
|
24
|
+
* This ensures RLS policies use the authenticated user's permissions
|
|
25
|
+
*/
|
|
26
|
+
static async createForUser(userToken) {
|
|
27
|
+
const supabaseUrl = process.env.SUPABASE_URL;
|
|
28
|
+
const supabaseAnonKey = process.env.SUPABASE_ANON_KEY;
|
|
29
|
+
if (!supabaseAnonKey) {
|
|
30
|
+
throw new Error('SUPABASE_ANON_KEY not found in environment');
|
|
31
|
+
}
|
|
32
|
+
if (!userToken) {
|
|
33
|
+
logger.error('No user token provided for authenticated client');
|
|
34
|
+
throw new Error('User token required for authenticated database access');
|
|
35
|
+
}
|
|
36
|
+
return createClient(supabaseUrl, supabaseAnonKey, {
|
|
37
|
+
global: {
|
|
38
|
+
headers: {
|
|
39
|
+
Authorization: `Bearer ${userToken}`
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
auth: {
|
|
43
|
+
persistSession: false,
|
|
44
|
+
autoRefreshToken: false
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=SupabaseUserClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SupabaseUserClient.js","sourceRoot":"","sources":["../../src/core/SupabaseUserClient.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAkB,MAAM,uBAAuB,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,MAAM,MAAM,GAAG,YAAY,CAAC,sBAAsB,CAAC,CAAC;AAEpD,MAAM,OAAO,kBAAkB;IAC7B;;;OAGG;IACH,MAAM,CAAC,eAAe;QACpB,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,YAAa,CAAC;QAC9C,MAAM,eAAe,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAkB,CAAC;QAEvD,IAAI,CAAC,eAAe,EAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;QAChE,CAAC;QAED,OAAO,YAAY,CAAC,WAAW,EAAE,eAAe,EAAE;YAChD,IAAI,EAAE;gBACJ,cAAc,EAAE,KAAK;gBACrB,gBAAgB,EAAE,KAAK;aACxB;SACF,CAAC,CAAC;IACL,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,SAAiB;QAC1C,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,YAAa,CAAC;QAC9C,MAAM,eAAe,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAkB,CAAC;QAEvD,IAAI,CAAC,eAAe,EAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;QAChE,CAAC;QAED,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,CAAC,KAAK,CAAC,iDAAiD,CAAC,CAAC;YAChE,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;QAC3E,CAAC;QAED,OAAO,YAAY,CAAC,WAAW,EAAE,eAAe,EAAE;YAChD,MAAM,EAAE;gBACN,OAAO,EAAE;oBACP,aAAa,EAAE,UAAU,SAAS,EAAE;iBACrC;aACF;YACD,IAAI,EAAE;gBACJ,cAAc,EAAE,KAAK;gBACrB,gBAAgB,EAAE,KAAK;aACxB;SACF,CAAC,CAAC;IACL,CAAC;CACF"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { AuthenticatedRequest } from '../middleware/authMiddleware.js';
|
|
2
|
+
/**
|
|
3
|
+
* Admin database helper - for organization admin operations
|
|
4
|
+
* Enforces admin role and logs access for audit
|
|
5
|
+
* RLS policies will allow access to all organization data
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { adminDB } from '@tetra/core';
|
|
10
|
+
*
|
|
11
|
+
* async getAllOrgOrders(req: AuthenticatedRequest, res: Response) {
|
|
12
|
+
* const supabase = await adminDB(req);
|
|
13
|
+
* const { data, error } = await supabase
|
|
14
|
+
* .from('orders')
|
|
15
|
+
* .select('*');
|
|
16
|
+
* // RLS will filter to organization data only
|
|
17
|
+
* }
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export declare function adminDB(req: AuthenticatedRequest): Promise<import("@supabase/supabase-js").SupabaseClient<any, "public", "public", any, any>>;
|
|
21
|
+
//# sourceMappingURL=adminDb.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adminDb.d.ts","sourceRoot":"","sources":["../../src/core/adminDb.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAMvE;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,OAAO,CAAC,GAAG,EAAE,oBAAoB,8FAyBtD"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { SupabaseUserClient } from './SupabaseUserClient.js';
|
|
2
|
+
import { createLogger } from '../utils/logger.js';
|
|
3
|
+
const logger = createLogger('security:db:admin');
|
|
4
|
+
/**
|
|
5
|
+
* Admin database helper - for organization admin operations
|
|
6
|
+
* Enforces admin role and logs access for audit
|
|
7
|
+
* RLS policies will allow access to all organization data
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { adminDB } from '@tetra/core';
|
|
12
|
+
*
|
|
13
|
+
* async getAllOrgOrders(req: AuthenticatedRequest, res: Response) {
|
|
14
|
+
* const supabase = await adminDB(req);
|
|
15
|
+
* const { data, error } = await supabase
|
|
16
|
+
* .from('orders')
|
|
17
|
+
* .select('*');
|
|
18
|
+
* // RLS will filter to organization data only
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export async function adminDB(req) {
|
|
23
|
+
if (!req.userToken) {
|
|
24
|
+
throw new Error('Unauthorized: No user token provided');
|
|
25
|
+
}
|
|
26
|
+
// Check if user has admin privileges (either org admin or superadmin)
|
|
27
|
+
if (!req.user?.organizationId && !req.user?.is_superadmin) {
|
|
28
|
+
logger.error(`Admin access denied - no organization for user ${req.user?.id}`);
|
|
29
|
+
throw new Error('Forbidden: No active organization');
|
|
30
|
+
}
|
|
31
|
+
// For superadmins, they have admin access to any org
|
|
32
|
+
// For regular admins, RLS will enforce org boundaries
|
|
33
|
+
const isAdmin = req.user?.is_superadmin || req.user?.organizationId;
|
|
34
|
+
if (!isAdmin) {
|
|
35
|
+
logger.error(`Admin access denied for user ${req.user?.id}`);
|
|
36
|
+
throw new Error('Forbidden: Admin access required');
|
|
37
|
+
}
|
|
38
|
+
// Admin calls are expected and common - only log at debug level
|
|
39
|
+
logger.debug(`Admin database access: user=${req.user.id} org=${req.user.organizationId || 'superadmin'}`);
|
|
40
|
+
// Use regular user client - RLS will handle admin permissions
|
|
41
|
+
return SupabaseUserClient.createForUser(req.userToken);
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=adminDb.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adminDb.js","sourceRoot":"","sources":["../../src/core/adminDb.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,MAAM,MAAM,GAAG,YAAY,CAAC,mBAAmB,CAAC,CAAC;AAEjD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,GAAyB;IACrD,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;IAC1D,CAAC;IAED,sEAAsE;IACtE,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,cAAc,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,aAAa,EAAE,CAAC;QAC1D,MAAM,CAAC,KAAK,CAAC,kDAAkD,GAAG,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;QAC/E,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;IACvD,CAAC;IAED,qDAAqD;IACrD,sDAAsD;IACtD,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,aAAa,IAAI,GAAG,CAAC,IAAI,EAAE,cAAc,CAAC;IAEpE,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,CAAC,KAAK,CAAC,gCAAgC,GAAG,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;QAC7D,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;IACtD,CAAC;IAED,gEAAgE;IAChE,MAAM,CAAC,KAAK,CAAC,+BAA+B,GAAG,CAAC,IAAI,CAAC,EAAE,QAAQ,GAAG,CAAC,IAAI,CAAC,cAAc,IAAI,YAAY,EAAE,CAAC,CAAC;IAE1G,8DAA8D;IAC9D,OAAO,kBAAkB,CAAC,aAAa,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;AACzD,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public database helper - uses anonymous key for public access
|
|
3
|
+
* Respects RLS policies for public data access
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```typescript
|
|
7
|
+
* import { publicDB } from '../../core/publicDb.js';
|
|
8
|
+
*
|
|
9
|
+
* async getPublicProducts(req: Request, res: Response) {
|
|
10
|
+
* const supabase = publicDB();
|
|
11
|
+
* const { data, error } = await supabase
|
|
12
|
+
* .from('products')
|
|
13
|
+
* .select('*')
|
|
14
|
+
* .eq('active', true);
|
|
15
|
+
* }
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export declare function publicDB(): import("@supabase/supabase-js").SupabaseClient<any, "public", "public", any, any>;
|
|
19
|
+
//# sourceMappingURL=publicDb.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publicDb.d.ts","sourceRoot":"","sources":["../../src/core/publicDb.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,QAAQ,sFAUvB"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { createClient } from '@supabase/supabase-js';
|
|
2
|
+
/**
|
|
3
|
+
* Public database helper - uses anonymous key for public access
|
|
4
|
+
* Respects RLS policies for public data access
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```typescript
|
|
8
|
+
* import { publicDB } from '../../core/publicDb.js';
|
|
9
|
+
*
|
|
10
|
+
* async getPublicProducts(req: Request, res: Response) {
|
|
11
|
+
* const supabase = publicDB();
|
|
12
|
+
* const { data, error } = await supabase
|
|
13
|
+
* .from('products')
|
|
14
|
+
* .select('*')
|
|
15
|
+
* .eq('active', true);
|
|
16
|
+
* }
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export function publicDB() {
|
|
20
|
+
const supabaseUrl = process.env.SUPABASE_URL;
|
|
21
|
+
const supabaseAnonKey = process.env.SUPABASE_ANON_KEY;
|
|
22
|
+
if (!supabaseUrl || !supabaseAnonKey) {
|
|
23
|
+
throw new Error('Missing Supabase configuration for public access');
|
|
24
|
+
}
|
|
25
|
+
// Create client with anon key - respects RLS policies
|
|
26
|
+
return createClient(supabaseUrl, supabaseAnonKey);
|
|
27
|
+
}
|
|
28
|
+
// NO ALIASES - BE EXPLICIT!
|
|
29
|
+
//# sourceMappingURL=publicDb.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publicDb.js","sourceRoot":"","sources":["../../src/core/publicDb.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAErD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,QAAQ;IACtB,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;IAC7C,MAAM,eAAe,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;IAEtD,IAAI,CAAC,WAAW,IAAI,CAAC,eAAe,EAAE,CAAC;QACrC,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;IACtE,CAAC;IAED,sDAAsD;IACtD,OAAO,YAAY,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC;AACpD,CAAC;AAED,4BAA4B"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { AuthenticatedRequest } from '../middleware/authMiddleware.js';
|
|
2
|
+
/**
|
|
3
|
+
* Superadmin database helper - for cross-organization operations
|
|
4
|
+
* Only for users with is_superadmin = true
|
|
5
|
+
* Uses system database with full audit logging
|
|
6
|
+
*
|
|
7
|
+
* ⚠️ SECURITY WARNING:
|
|
8
|
+
* This provides FULL database access across ALL organizations!
|
|
9
|
+
* Only use for legitimate superadmin operations like:
|
|
10
|
+
* - Cross-organization reporting
|
|
11
|
+
* - System-wide maintenance
|
|
12
|
+
* - Emergency fixes
|
|
13
|
+
* - Data migrations
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* import { superadminDB } from '../../core/superadminDb.js';
|
|
18
|
+
*
|
|
19
|
+
* async getAllSystemOrders(req: AuthenticatedRequest, res: Response) {
|
|
20
|
+
* const supabase = await superadminDB(req);
|
|
21
|
+
* const { data, error } = await supabase
|
|
22
|
+
* .from('orders')
|
|
23
|
+
* .select('*');
|
|
24
|
+
* // No RLS filtering - returns ALL orders from ALL organizations
|
|
25
|
+
* }
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export declare function superadminDB(req: AuthenticatedRequest): Promise<import("@supabase/supabase-js").SupabaseClient<any, "public", "public", any, any>>;
|
|
29
|
+
//# sourceMappingURL=superadminDb.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"superadminDb.d.ts","sourceRoot":"","sources":["../../src/core/superadminDb.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAMvE;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,YAAY,CAAC,GAAG,EAAE,oBAAoB,8FAyB3D"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { systemDB } from './systemDb.js';
|
|
2
|
+
import { createLogger } from '../utils/logger.js';
|
|
3
|
+
const logger = createLogger('security:db:superadmin');
|
|
4
|
+
/**
|
|
5
|
+
* Superadmin database helper - for cross-organization operations
|
|
6
|
+
* Only for users with is_superadmin = true
|
|
7
|
+
* Uses system database with full audit logging
|
|
8
|
+
*
|
|
9
|
+
* ⚠️ SECURITY WARNING:
|
|
10
|
+
* This provides FULL database access across ALL organizations!
|
|
11
|
+
* Only use for legitimate superadmin operations like:
|
|
12
|
+
* - Cross-organization reporting
|
|
13
|
+
* - System-wide maintenance
|
|
14
|
+
* - Emergency fixes
|
|
15
|
+
* - Data migrations
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* import { superadminDB } from '../../core/superadminDb.js';
|
|
20
|
+
*
|
|
21
|
+
* async getAllSystemOrders(req: AuthenticatedRequest, res: Response) {
|
|
22
|
+
* const supabase = await superadminDB(req);
|
|
23
|
+
* const { data, error } = await supabase
|
|
24
|
+
* .from('orders')
|
|
25
|
+
* .select('*');
|
|
26
|
+
* // No RLS filtering - returns ALL orders from ALL organizations
|
|
27
|
+
* }
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export async function superadminDB(req) {
|
|
31
|
+
if (!req.user) {
|
|
32
|
+
throw new Error('Unauthorized: No authenticated user');
|
|
33
|
+
}
|
|
34
|
+
if (!req.user?.isSuperAdmin) {
|
|
35
|
+
logger.error(`🚫 Superadmin access denied for user ${req.user?.id}`);
|
|
36
|
+
throw new Error('Forbidden: Superadmin access required');
|
|
37
|
+
}
|
|
38
|
+
const context = `superadmin:${req.user.id}:${req.method}:${req.path}`;
|
|
39
|
+
logger.warn(`👑 Superadmin database access: ${context}`);
|
|
40
|
+
// Log additional details for audit trail
|
|
41
|
+
logger.info({
|
|
42
|
+
userId: req.user.id,
|
|
43
|
+
email: req.user.email,
|
|
44
|
+
method: req.method,
|
|
45
|
+
path: req.path,
|
|
46
|
+
timestamp: new Date().toISOString()
|
|
47
|
+
}, `Superadmin operation details:`);
|
|
48
|
+
// Superadmins get system access with full audit trail
|
|
49
|
+
// This bypasses RLS for cross-organizational operations
|
|
50
|
+
return systemDB(context);
|
|
51
|
+
}
|
|
52
|
+
// NO ALIASES - BE EXPLICIT!
|
|
53
|
+
//# sourceMappingURL=superadminDb.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"superadminDb.js","sourceRoot":"","sources":["../../src/core/superadminDb.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,MAAM,MAAM,GAAG,YAAY,CAAC,wBAAwB,CAAC,CAAC;AAEtD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,GAAyB;IAC1D,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;IACzD,CAAC;IAED,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,YAAY,EAAE,CAAC;QAC5B,MAAM,CAAC,KAAK,CAAC,wCAAwC,GAAG,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;QACrE,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;IAC3D,CAAC;IAED,MAAM,OAAO,GAAG,cAAc,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;IACtE,MAAM,CAAC,IAAI,CAAC,kCAAkC,OAAO,EAAE,CAAC,CAAC;IAEzD,yCAAyC;IACzC,MAAM,CAAC,IAAI,CAAC;QACV,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,EAAE;QACnB,KAAK,EAAE,GAAG,CAAC,IAAI,CAAC,KAAK;QACrB,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACpC,EAAE,+BAA+B,CAAC,CAAC;IAEpC,sDAAsD;IACtD,wDAAwD;IACxD,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC;AAC3B,CAAC;AAED,4BAA4B"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* System Database Client — Service Role Key (bypasses RLS)
|
|
3
|
+
*
|
|
4
|
+
* For background tasks, cron jobs, and system operations that need
|
|
5
|
+
* full database access without user context.
|
|
6
|
+
*
|
|
7
|
+
* Projects should register their known contexts via addWhitelistedContexts()
|
|
8
|
+
* to avoid warning logs for legitimate system operations.
|
|
9
|
+
*/
|
|
10
|
+
import { SupabaseClient } from '@supabase/supabase-js';
|
|
11
|
+
/**
|
|
12
|
+
* Register additional whitelisted contexts for your project.
|
|
13
|
+
* Call at app startup.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* addWhitelistedContexts([
|
|
17
|
+
* 'order-sync-service',
|
|
18
|
+
* 'email-queue-processor',
|
|
19
|
+
* 'phase-cron-service',
|
|
20
|
+
* ]);
|
|
21
|
+
*/
|
|
22
|
+
export declare function addWhitelistedContexts(contexts: string[]): void;
|
|
23
|
+
/**
|
|
24
|
+
* Create a system-level Supabase client (service role key, bypasses RLS).
|
|
25
|
+
*
|
|
26
|
+
* @param context - Descriptive string for audit logging (e.g., 'order-sync-service')
|
|
27
|
+
*/
|
|
28
|
+
export declare function systemDB(context: string): SupabaseClient;
|
|
29
|
+
/**
|
|
30
|
+
* Secure system DB — same as systemDB but with stricter logging.
|
|
31
|
+
* Use for operations that modify critical data.
|
|
32
|
+
*/
|
|
33
|
+
export declare function secureSystemDB(context: string): SupabaseClient;
|
|
34
|
+
//# sourceMappingURL=systemDb.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"systemDb.d.ts","sourceRoot":"","sources":["../../src/core/systemDb.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAgB,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAerE;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,CAI/D;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,CAexD;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,CAG9D"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* System Database Client — Service Role Key (bypasses RLS)
|
|
3
|
+
*
|
|
4
|
+
* For background tasks, cron jobs, and system operations that need
|
|
5
|
+
* full database access without user context.
|
|
6
|
+
*
|
|
7
|
+
* Projects should register their known contexts via addWhitelistedContexts()
|
|
8
|
+
* to avoid warning logs for legitimate system operations.
|
|
9
|
+
*/
|
|
10
|
+
import { createClient } from '@supabase/supabase-js';
|
|
11
|
+
import { createLogger } from '../utils/logger.js';
|
|
12
|
+
const logger = createLogger('security:db:system');
|
|
13
|
+
/**
|
|
14
|
+
* Whitelist of known/approved system database contexts.
|
|
15
|
+
* Projects add their own via addWhitelistedContexts().
|
|
16
|
+
*/
|
|
17
|
+
const WHITELISTED_CONTEXTS = new Set([
|
|
18
|
+
// Tetra core
|
|
19
|
+
'system-migration',
|
|
20
|
+
'system-health-check',
|
|
21
|
+
]);
|
|
22
|
+
/**
|
|
23
|
+
* Register additional whitelisted contexts for your project.
|
|
24
|
+
* Call at app startup.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* addWhitelistedContexts([
|
|
28
|
+
* 'order-sync-service',
|
|
29
|
+
* 'email-queue-processor',
|
|
30
|
+
* 'phase-cron-service',
|
|
31
|
+
* ]);
|
|
32
|
+
*/
|
|
33
|
+
export function addWhitelistedContexts(contexts) {
|
|
34
|
+
for (const ctx of contexts) {
|
|
35
|
+
WHITELISTED_CONTEXTS.add(ctx);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Create a system-level Supabase client (service role key, bypasses RLS).
|
|
40
|
+
*
|
|
41
|
+
* @param context - Descriptive string for audit logging (e.g., 'order-sync-service')
|
|
42
|
+
*/
|
|
43
|
+
export function systemDB(context) {
|
|
44
|
+
const url = process.env.SUPABASE_URL;
|
|
45
|
+
const serviceKey = process.env.SUPABASE_SERVICE_ROLE_KEY;
|
|
46
|
+
if (!url || !serviceKey) {
|
|
47
|
+
throw new Error('Missing SUPABASE_URL or SUPABASE_SERVICE_ROLE_KEY environment variables');
|
|
48
|
+
}
|
|
49
|
+
if (!WHITELISTED_CONTEXTS.has(context)) {
|
|
50
|
+
logger.warn({ context }, 'systemDB called with unknown context — consider whitelisting');
|
|
51
|
+
}
|
|
52
|
+
return createClient(url, serviceKey, {
|
|
53
|
+
auth: { persistSession: false }
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Secure system DB — same as systemDB but with stricter logging.
|
|
58
|
+
* Use for operations that modify critical data.
|
|
59
|
+
*/
|
|
60
|
+
export function secureSystemDB(context) {
|
|
61
|
+
logger.info({ context }, 'secureSystemDB access');
|
|
62
|
+
return systemDB(context);
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=systemDb.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"systemDb.js","sourceRoot":"","sources":["../../src/core/systemDb.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,YAAY,EAAkB,MAAM,uBAAuB,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,MAAM,MAAM,GAAG,YAAY,CAAC,oBAAoB,CAAC,CAAC;AAElD;;;GAGG;AACH,MAAM,oBAAoB,GAAG,IAAI,GAAG,CAAS;IAC3C,aAAa;IACb,kBAAkB;IAClB,qBAAqB;CACtB,CAAC,CAAC;AAEH;;;;;;;;;;GAUG;AACH,MAAM,UAAU,sBAAsB,CAAC,QAAkB;IACvD,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,oBAAoB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAChC,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,QAAQ,CAAC,OAAe;IACtC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;IACrC,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC;IAEzD,IAAI,CAAC,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,yEAAyE,CAAC,CAAC;IAC7F,CAAC;IAED,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;QACvC,MAAM,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,EAAE,8DAA8D,CAAC,CAAC;IAC3F,CAAC;IAED,OAAO,YAAY,CAAC,GAAG,EAAE,UAAU,EAAE;QACnC,IAAI,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE;KAChC,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,OAAe;IAC5C,MAAM,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,EAAE,uBAAuB,CAAC,CAAC;IAClD,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC;AAC3B,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { AuthenticatedRequest } from '../middleware/authMiddleware.js';
|
|
2
|
+
/**
|
|
3
|
+
* User database helper - for accessing user's own data
|
|
4
|
+
* Enforces JWT authentication and user context
|
|
5
|
+
* RLS policies will filter to user's own records
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { userDB } from '@tetra/core';
|
|
10
|
+
*
|
|
11
|
+
* async getUserProfile(req: AuthenticatedRequest, res: Response) {
|
|
12
|
+
* const supabase = await userDB(req);
|
|
13
|
+
* const { data, error } = await supabase
|
|
14
|
+
* .from('users_public')
|
|
15
|
+
* .select('*')
|
|
16
|
+
* .eq('id', req.user.id)
|
|
17
|
+
* .single();
|
|
18
|
+
* }
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export declare function userDB(req: AuthenticatedRequest): Promise<import("@supabase/supabase-js").SupabaseClient<any, "public", "public", any, any>>;
|
|
22
|
+
//# sourceMappingURL=userDb.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"userDb.d.ts","sourceRoot":"","sources":["../../src/core/userDb.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAMvE;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,MAAM,CAAC,GAAG,EAAE,oBAAoB,8FAerD"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { SupabaseUserClient } from './SupabaseUserClient.js';
|
|
2
|
+
import { createLogger } from '../utils/logger.js';
|
|
3
|
+
const logger = createLogger('security:db:user');
|
|
4
|
+
/**
|
|
5
|
+
* User database helper - for accessing user's own data
|
|
6
|
+
* Enforces JWT authentication and user context
|
|
7
|
+
* RLS policies will filter to user's own records
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { userDB } from '@tetra/core';
|
|
12
|
+
*
|
|
13
|
+
* async getUserProfile(req: AuthenticatedRequest, res: Response) {
|
|
14
|
+
* const supabase = await userDB(req);
|
|
15
|
+
* const { data, error } = await supabase
|
|
16
|
+
* .from('users_public')
|
|
17
|
+
* .select('*')
|
|
18
|
+
* .eq('id', req.user.id)
|
|
19
|
+
* .single();
|
|
20
|
+
* }
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export async function userDB(req) {
|
|
24
|
+
if (!req.userToken) {
|
|
25
|
+
logger.error('No user token in request!');
|
|
26
|
+
throw new Error('Unauthorized: No user token provided');
|
|
27
|
+
}
|
|
28
|
+
if (!req.user?.id) {
|
|
29
|
+
logger.error('No user context in request!');
|
|
30
|
+
throw new Error('Unauthorized: No user context');
|
|
31
|
+
}
|
|
32
|
+
logger.debug(`User database access: user=${req.user.id} org=${req.user.organizationId || 'none'}`);
|
|
33
|
+
// Use user client - RLS will filter to user's own data
|
|
34
|
+
return SupabaseUserClient.createForUser(req.userToken);
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=userDb.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"userDb.js","sourceRoot":"","sources":["../../src/core/userDb.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,MAAM,MAAM,GAAG,YAAY,CAAC,kBAAkB,CAAC,CAAC;AAEhD;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,GAAyB;IACpD,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC;QACnB,MAAM,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAC;QAC1C,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;IAC1D,CAAC;IAED,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC;QAClB,MAAM,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAC;QAC5C,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;IACnD,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,8BAA8B,GAAG,CAAC,IAAI,CAAC,EAAE,QAAQ,GAAG,CAAC,IAAI,CAAC,cAAc,IAAI,MAAM,EAAE,CAAC,CAAC;IAEnG,uDAAuD;IACvD,OAAO,kBAAkB,CAAC,aAAa,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;AACzD,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Webhook database helper - for external webhook endpoints
|
|
3
|
+
* Uses Service Role Key to bypass RLS for webhook operations
|
|
4
|
+
* Automatically prefixes context with "webhook:" for audit trail
|
|
5
|
+
*
|
|
6
|
+
* @param webhookSource - Name of the webhook source (e.g., 'buildship', 'stripe', 'mollie')
|
|
7
|
+
* @returns Supabase client with full system access
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* // In a webhook controller
|
|
12
|
+
* const supabase = webhookDB('stripe');
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export declare function webhookDB(webhookSource: string): import("@supabase/supabase-js").SupabaseClient<any, "public", "public", any, any>;
|
|
16
|
+
//# sourceMappingURL=webhookDb.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhookDb.d.ts","sourceRoot":"","sources":["../../src/core/webhookDb.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;GAaG;AACH,wBAAgB,SAAS,CAAC,aAAa,EAAE,MAAM,qFAQ9C"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { systemDB } from './systemDb.js';
|
|
2
|
+
import { createLogger } from '../utils/logger.js';
|
|
3
|
+
const logger = createLogger('security:db:webhook');
|
|
4
|
+
/**
|
|
5
|
+
* Webhook database helper - for external webhook endpoints
|
|
6
|
+
* Uses Service Role Key to bypass RLS for webhook operations
|
|
7
|
+
* Automatically prefixes context with "webhook:" for audit trail
|
|
8
|
+
*
|
|
9
|
+
* @param webhookSource - Name of the webhook source (e.g., 'buildship', 'stripe', 'mollie')
|
|
10
|
+
* @returns Supabase client with full system access
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* // In a webhook controller
|
|
15
|
+
* const supabase = webhookDB('stripe');
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export function webhookDB(webhookSource) {
|
|
19
|
+
const context = `webhook:${webhookSource}`;
|
|
20
|
+
// Log webhook access for security auditing
|
|
21
|
+
logger.info(`🔗 Webhook database access: ${context}`);
|
|
22
|
+
// Use systemDB under the hood with webhook-specific context
|
|
23
|
+
return systemDB(context);
|
|
24
|
+
}
|
|
25
|
+
// NO ALIASES - BE EXPLICIT!
|
|
26
|
+
//# sourceMappingURL=webhookDb.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhookDb.js","sourceRoot":"","sources":["../../src/core/webhookDb.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,MAAM,MAAM,GAAG,YAAY,CAAC,qBAAqB,CAAC,CAAC;AAEnD;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,SAAS,CAAC,aAAqB;IAC7C,MAAM,OAAO,GAAG,WAAW,aAAa,EAAE,CAAC;IAE3C,2CAA2C;IAC3C,MAAM,CAAC,IAAI,CAAC,+BAA+B,OAAO,EAAE,CAAC,CAAC;IAEtD,4DAA4D;IAC5D,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC;AAC3B,CAAC;AAED,4BAA4B"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FeatureFilters — Config-driven filter bar component
|
|
3
|
+
*
|
|
4
|
+
* Replaces ALL per-feature filter implementations with a single generic component
|
|
5
|
+
* driven by FeatureConfig.filters[].
|
|
6
|
+
*
|
|
7
|
+
* Before (50+ lines boilerplate PER feature):
|
|
8
|
+
* <Select value={status} onChange={setStatus} options={statusOptions} />
|
|
9
|
+
* <Input value={search} onChange={setSearch} placeholder="Search..." />
|
|
10
|
+
* <DatePicker value={dateRange} onChange={setDateRange} />
|
|
11
|
+
*
|
|
12
|
+
* After (ZERO boilerplate):
|
|
13
|
+
* <FeatureFilters config={ordersConfig} feature={feature} />
|
|
14
|
+
*
|
|
15
|
+
* Filter type → UI mapping:
|
|
16
|
+
* - search → SearchInput (debounced text input)
|
|
17
|
+
* - enum → Select dropdown
|
|
18
|
+
* - column → Select dropdown (same as enum, column-based)
|
|
19
|
+
* - boolean → Toggle / Switch
|
|
20
|
+
* - multiselect → Multi-select with checkboxes
|
|
21
|
+
* - daterange → Date range picker (from/to)
|
|
22
|
+
* - time → Select dropdown (preset time periods)
|
|
23
|
+
* - nullable → Select dropdown (all/with/without)
|
|
24
|
+
* - related → Select dropdown (all/with/without)
|
|
25
|
+
* - numeric → Number range inputs (min/max)
|
|
26
|
+
* - array → Multi-select (same UI as multiselect)
|
|
27
|
+
*
|
|
28
|
+
* @module @tetra/core/frontend
|
|
29
|
+
* Created: February 20, 2026
|
|
30
|
+
*/
|
|
31
|
+
import React from 'react';
|
|
32
|
+
import type { FeatureConfig, FilterConfig } from '../../shared/types/feature-config.js';
|
|
33
|
+
import type { UseFeatureResult } from '../hooks/useFeature.js';
|
|
34
|
+
export interface FeatureFiltersProps<TItem = Record<string, unknown>> {
|
|
35
|
+
/** Feature configuration with filters[] */
|
|
36
|
+
config: FeatureConfig<TItem>;
|
|
37
|
+
/** Result from useFeature() hook */
|
|
38
|
+
feature: UseFeatureResult<TItem>;
|
|
39
|
+
/** CSS class for the filter bar wrapper */
|
|
40
|
+
className?: string;
|
|
41
|
+
/** Layout: horizontal bar or vertical stack (default: 'horizontal') */
|
|
42
|
+
layout?: 'horizontal' | 'vertical';
|
|
43
|
+
/** Show reset button (default: true) */
|
|
44
|
+
showReset?: boolean;
|
|
45
|
+
/** Reset button label (default: 'Reset') */
|
|
46
|
+
resetLabel?: string;
|
|
47
|
+
/** Show active filter count badge (default: true) */
|
|
48
|
+
showActiveCount?: boolean;
|
|
49
|
+
/** Collapse filters behind a "More filters" button (default: auto based on filter count) */
|
|
50
|
+
collapsible?: boolean;
|
|
51
|
+
/** Max visible filters before collapsing (default: 4) */
|
|
52
|
+
maxVisible?: number;
|
|
53
|
+
/** "More filters" button label (default: 'More filters') */
|
|
54
|
+
moreLabel?: string;
|
|
55
|
+
/** Show filter count badges from feature.counts (default: true) */
|
|
56
|
+
showCounts?: boolean;
|
|
57
|
+
/** Custom filter renderers (keyed by filter name) */
|
|
58
|
+
filterRenderers?: Record<string, (filter: FilterConfig, value: string | string[] | undefined, onChange: (value: string | string[] | undefined) => void, counts?: Record<string, number>) => React.ReactNode>;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* FeatureFilters — Config-driven filter bar
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```tsx
|
|
65
|
+
* import { ordersConfig } from '@/config/features/orders.config';
|
|
66
|
+
* import { useFeature, FeatureFilters, FeatureTable } from '@tetra/core/frontend';
|
|
67
|
+
*
|
|
68
|
+
* function OrdersPage() {
|
|
69
|
+
* const feature = useFeature(ordersConfig, supabase, { organizationId });
|
|
70
|
+
*
|
|
71
|
+
* return (
|
|
72
|
+
* <div>
|
|
73
|
+
* <FeatureFilters config={ordersConfig} feature={feature} />
|
|
74
|
+
* <FeatureTable config={ordersConfig} feature={feature} />
|
|
75
|
+
* </div>
|
|
76
|
+
* );
|
|
77
|
+
* }
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
export declare function FeatureFilters<TItem = Record<string, unknown>>({ config, feature, className, layout, showReset, resetLabel, showActiveCount, collapsible, maxVisible, moreLabel, showCounts, filterRenderers, }: FeatureFiltersProps<TItem>): import("react/jsx-runtime").JSX.Element;
|
|
81
|
+
//# sourceMappingURL=FeatureFilters.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FeatureFilters.d.ts","sourceRoot":"","sources":["../../../src/frontend/components/FeatureFilters.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,KAAyC,MAAM,OAAO,CAAC;AAC9D,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAkB,MAAM,sCAAsC,CAAC;AACxG,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAW/D,MAAM,WAAW,mBAAmB,CAAC,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAClE,2CAA2C;IAC3C,MAAM,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC;IAE7B,oCAAoC;IACpC,OAAO,EAAE,gBAAgB,CAAC,KAAK,CAAC,CAAC;IAEjC,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,uEAAuE;IACvE,MAAM,CAAC,EAAE,YAAY,GAAG,UAAU,CAAC;IAEnC,wCAAwC;IACxC,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB,4CAA4C;IAC5C,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,qDAAqD;IACrD,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B,4FAA4F;IAC5F,WAAW,CAAC,EAAE,OAAO,CAAC;IAEtB,yDAAyD;IACzD,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,mEAAmE;IACnE,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB,qDAAqD;IACrD,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,CAC/B,MAAM,EAAE,YAAY,EACpB,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,EACpC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,KAAK,IAAI,EACxD,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAC5B,KAAK,CAAC,SAAS,CAAC,CAAC;CACvB;AAoaD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,cAAc,CAAC,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAC9D,MAAM,EACN,OAAO,EACP,SAAS,EACT,MAAqB,EACrB,SAAgB,EAChB,UAAoB,EACpB,eAAsB,EACtB,WAAW,EACX,UAAc,EACd,SAA0B,EAC1B,UAAiB,EACjB,eAAe,GAChB,EAAE,mBAAmB,CAAC,KAAK,CAAC,2CAuI5B"}
|