@soulbatical/tetra-core 0.10.5 → 0.11.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/README.md +34 -6
- package/dist/core/createApp.d.ts +1 -1
- package/dist/core/createApp.d.ts.map +1 -1
- package/dist/frontend/sentry.d.ts +1 -1
- package/dist/frontend/sentry.js +1 -1
- package/dist/frontend/storage/StorageDropzone.d.ts +1 -1
- package/dist/frontend/storage/StorageDropzone.js +1 -1
- package/dist/frontend/storage/storageUrl.d.ts +1 -1
- package/dist/frontend/storage/storageUrl.js +1 -1
- package/dist/generators/rls-checker.d.ts +1 -1
- package/dist/generators/rls-checker.js +1 -1
- package/dist/generators/rls-exec-sql.d.ts +1 -1
- package/dist/generators/rls-exec-sql.js +1 -1
- package/dist/generators/rpc/index.d.ts +1 -1
- package/dist/generators/rpc/index.js +1 -1
- package/dist/index.d.ts +0 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -9
- package/dist/index.js.map +1 -1
- package/dist/middleware/securityMiddleware.d.ts +1 -1
- package/dist/middleware/securityMiddleware.d.ts.map +1 -1
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +12 -1
- package/dist/utils/logger.js.map +1 -1
- package/package.json +32 -43
- package/dist/affiliate.d.ts +0 -11
- package/dist/affiliate.d.ts.map +0 -1
- package/dist/affiliate.js +0 -10
- package/dist/affiliate.js.map +0 -1
- package/dist/billing.d.ts +0 -8
- package/dist/billing.d.ts.map +0 -1
- package/dist/billing.js +0 -7
- package/dist/billing.js.map +0 -1
- package/dist/email.d.ts +0 -9
- package/dist/email.d.ts.map +0 -1
- package/dist/email.js +0 -8
- package/dist/email.js.map +0 -1
- package/dist/generators/rls-exec-sql.sql +0 -57
- package/dist/generators.d.ts +0 -9
- package/dist/generators.d.ts.map +0 -1
- package/dist/generators.js +0 -8
- package/dist/generators.js.map +0 -1
- package/dist/mcp.d.ts +0 -8
- package/dist/mcp.d.ts.map +0 -1
- package/dist/mcp.js +0 -7
- package/dist/mcp.js.map +0 -1
- package/dist/planner.d.ts +0 -8
- package/dist/planner.d.ts.map +0 -1
- package/dist/planner.js +0 -7
- package/dist/planner.js.map +0 -1
- package/dist/shared/affiliate/AffiliateAttributionService.d.ts +0 -47
- package/dist/shared/affiliate/AffiliateAttributionService.d.ts.map +0 -1
- package/dist/shared/affiliate/AffiliateAttributionService.js +0 -308
- package/dist/shared/affiliate/AffiliateAttributionService.js.map +0 -1
- package/dist/shared/affiliate/AffiliateClickService.d.ts +0 -35
- package/dist/shared/affiliate/AffiliateClickService.d.ts.map +0 -1
- package/dist/shared/affiliate/AffiliateClickService.js +0 -87
- package/dist/shared/affiliate/AffiliateClickService.js.map +0 -1
- package/dist/shared/affiliate/affiliateFeatureConfig.d.ts +0 -11
- package/dist/shared/affiliate/affiliateFeatureConfig.d.ts.map +0 -1
- package/dist/shared/affiliate/affiliateFeatureConfig.js +0 -242
- package/dist/shared/affiliate/affiliateFeatureConfig.js.map +0 -1
- package/dist/shared/affiliate/index.d.ts +0 -11
- package/dist/shared/affiliate/index.d.ts.map +0 -1
- package/dist/shared/affiliate/index.js +0 -13
- package/dist/shared/affiliate/index.js.map +0 -1
- package/dist/shared/affiliate/routes.d.ts +0 -87
- package/dist/shared/affiliate/routes.d.ts.map +0 -1
- package/dist/shared/affiliate/routes.js +0 -404
- package/dist/shared/affiliate/routes.js.map +0 -1
- package/dist/shared/affiliate/types.d.ts +0 -170
- package/dist/shared/affiliate/types.d.ts.map +0 -1
- package/dist/shared/affiliate/types.js +0 -11
- package/dist/shared/affiliate/types.js.map +0 -1
- package/dist/shared/billing/BillingService.d.ts +0 -56
- package/dist/shared/billing/BillingService.d.ts.map +0 -1
- package/dist/shared/billing/BillingService.js +0 -588
- package/dist/shared/billing/BillingService.js.map +0 -1
- package/dist/shared/billing/SeatBillingService.d.ts +0 -106
- package/dist/shared/billing/SeatBillingService.d.ts.map +0 -1
- package/dist/shared/billing/SeatBillingService.js +0 -292
- package/dist/shared/billing/SeatBillingService.js.map +0 -1
- package/dist/shared/billing/index.d.ts +0 -30
- package/dist/shared/billing/index.d.ts.map +0 -1
- package/dist/shared/billing/index.js +0 -27
- package/dist/shared/billing/index.js.map +0 -1
- package/dist/shared/billing/routes.d.ts +0 -45
- package/dist/shared/billing/routes.d.ts.map +0 -1
- package/dist/shared/billing/routes.js +0 -184
- package/dist/shared/billing/routes.js.map +0 -1
- package/dist/shared/billing/seat-pricing.d.ts +0 -53
- package/dist/shared/billing/seat-pricing.d.ts.map +0 -1
- package/dist/shared/billing/seat-pricing.js +0 -81
- package/dist/shared/billing/seat-pricing.js.map +0 -1
- package/dist/shared/billing/types.d.ts +0 -109
- package/dist/shared/billing/types.d.ts.map +0 -1
- package/dist/shared/billing/types.js +0 -8
- package/dist/shared/billing/types.js.map +0 -1
- package/dist/shared/email/EmailService.d.ts +0 -64
- package/dist/shared/email/EmailService.d.ts.map +0 -1
- package/dist/shared/email/EmailService.js +0 -300
- package/dist/shared/email/EmailService.js.map +0 -1
- package/dist/shared/email/adminRoutes.d.ts +0 -30
- package/dist/shared/email/adminRoutes.d.ts.map +0 -1
- package/dist/shared/email/adminRoutes.js +0 -227
- package/dist/shared/email/adminRoutes.js.map +0 -1
- package/dist/shared/email/gmail.d.ts +0 -208
- package/dist/shared/email/gmail.d.ts.map +0 -1
- package/dist/shared/email/gmail.js +0 -626
- package/dist/shared/email/gmail.js.map +0 -1
- package/dist/shared/email/index.d.ts +0 -15
- package/dist/shared/email/index.d.ts.map +0 -1
- package/dist/shared/email/index.js +0 -18
- package/dist/shared/email/index.js.map +0 -1
- package/dist/shared/email/mailgun.d.ts +0 -18
- package/dist/shared/email/mailgun.d.ts.map +0 -1
- package/dist/shared/email/mailgun.js +0 -76
- package/dist/shared/email/mailgun.js.map +0 -1
- package/dist/shared/email/sanitize.d.ts +0 -25
- package/dist/shared/email/sanitize.d.ts.map +0 -1
- package/dist/shared/email/sanitize.js +0 -39
- package/dist/shared/email/sanitize.js.map +0 -1
- package/dist/shared/email/smtp.d.ts +0 -20
- package/dist/shared/email/smtp.d.ts.map +0 -1
- package/dist/shared/email/smtp.js +0 -53
- package/dist/shared/email/smtp.js.map +0 -1
- package/dist/shared/email/types.d.ts +0 -113
- package/dist/shared/email/types.d.ts.map +0 -1
- package/dist/shared/email/types.js +0 -7
- package/dist/shared/email/types.js.map +0 -1
- package/dist/shared/email/webhookRoutes.d.ts +0 -29
- package/dist/shared/email/webhookRoutes.d.ts.map +0 -1
- package/dist/shared/email/webhookRoutes.js +0 -125
- package/dist/shared/email/webhookRoutes.js.map +0 -1
- package/dist/shared/mcp/index.d.ts +0 -51
- package/dist/shared/mcp/index.d.ts.map +0 -1
- package/dist/shared/mcp/index.js +0 -51
- package/dist/shared/mcp/index.js.map +0 -1
- package/dist/shared/mcp/mcp-auth-routes.d.ts +0 -26
- package/dist/shared/mcp/mcp-auth-routes.d.ts.map +0 -1
- package/dist/shared/mcp/mcp-auth-routes.js +0 -141
- package/dist/shared/mcp/mcp-auth-routes.js.map +0 -1
- package/dist/shared/mcp/mcp-db.d.ts +0 -99
- package/dist/shared/mcp/mcp-db.d.ts.map +0 -1
- package/dist/shared/mcp/mcp-db.js +0 -106
- package/dist/shared/mcp/mcp-db.js.map +0 -1
- package/dist/shared/mcp/mcp-routes.d.ts +0 -29
- package/dist/shared/mcp/mcp-routes.d.ts.map +0 -1
- package/dist/shared/mcp/mcp-routes.js +0 -171
- package/dist/shared/mcp/mcp-routes.js.map +0 -1
- package/dist/shared/mcp/mcp-tokens-routes.d.ts +0 -35
- package/dist/shared/mcp/mcp-tokens-routes.d.ts.map +0 -1
- package/dist/shared/mcp/mcp-tokens-routes.js +0 -94
- package/dist/shared/mcp/mcp-tokens-routes.js.map +0 -1
- package/dist/shared/mcp/mcp-usage-routes.d.ts +0 -17
- package/dist/shared/mcp/mcp-usage-routes.d.ts.map +0 -1
- package/dist/shared/mcp/mcp-usage-routes.js +0 -81
- package/dist/shared/mcp/mcp-usage-routes.js.map +0 -1
- package/dist/shared/mcp/tenant-context.d.ts +0 -59
- package/dist/shared/mcp/tenant-context.d.ts.map +0 -1
- package/dist/shared/mcp/tenant-context.js +0 -136
- package/dist/shared/mcp/tenant-context.js.map +0 -1
- package/dist/shared/mcp/types.d.ts +0 -74
- package/dist/shared/mcp/types.d.ts.map +0 -1
- package/dist/shared/mcp/types.js +0 -7
- package/dist/shared/mcp/types.js.map +0 -1
- package/dist/shared/planner/GoogleCalendarService.d.ts +0 -137
- package/dist/shared/planner/GoogleCalendarService.d.ts.map +0 -1
- package/dist/shared/planner/GoogleCalendarService.js +0 -525
- package/dist/shared/planner/GoogleCalendarService.js.map +0 -1
- package/dist/shared/planner/PlannerService.d.ts +0 -264
- package/dist/shared/planner/PlannerService.d.ts.map +0 -1
- package/dist/shared/planner/PlannerService.js +0 -1393
- package/dist/shared/planner/PlannerService.js.map +0 -1
- package/dist/shared/planner/index.d.ts +0 -37
- package/dist/shared/planner/index.d.ts.map +0 -1
- package/dist/shared/planner/index.js +0 -35
- package/dist/shared/planner/index.js.map +0 -1
- package/dist/shared/planner/intervals.d.ts +0 -60
- package/dist/shared/planner/intervals.d.ts.map +0 -1
- package/dist/shared/planner/intervals.js +0 -141
- package/dist/shared/planner/intervals.js.map +0 -1
- package/dist/shared/planner/routes.d.ts +0 -69
- package/dist/shared/planner/routes.d.ts.map +0 -1
- package/dist/shared/planner/routes.js +0 -770
- package/dist/shared/planner/routes.js.map +0 -1
- package/dist/shared/planner/types.d.ts +0 -328
- package/dist/shared/planner/types.d.ts.map +0 -1
- package/dist/shared/planner/types.js +0 -9
- package/dist/shared/planner/types.js.map +0 -1
- package/dist/shared/storage/ImageProcessingService.d.ts +0 -32
- package/dist/shared/storage/ImageProcessingService.d.ts.map +0 -1
- package/dist/shared/storage/ImageProcessingService.js +0 -127
- package/dist/shared/storage/ImageProcessingService.js.map +0 -1
- package/dist/shared/storage/StorageProxyService.d.ts +0 -47
- package/dist/shared/storage/StorageProxyService.d.ts.map +0 -1
- package/dist/shared/storage/StorageProxyService.js +0 -196
- package/dist/shared/storage/StorageProxyService.js.map +0 -1
- package/dist/shared/storage/StorageUploadService.d.ts +0 -126
- package/dist/shared/storage/StorageUploadService.d.ts.map +0 -1
- package/dist/shared/storage/StorageUploadService.js +0 -206
- package/dist/shared/storage/StorageUploadService.js.map +0 -1
- package/dist/shared/storage/creative-urls.d.ts +0 -14
- package/dist/shared/storage/creative-urls.d.ts.map +0 -1
- package/dist/shared/storage/creative-urls.js +0 -30
- package/dist/shared/storage/creative-urls.js.map +0 -1
- package/dist/shared/storage/index.d.ts +0 -28
- package/dist/shared/storage/index.d.ts.map +0 -1
- package/dist/shared/storage/index.js +0 -27
- package/dist/shared/storage/index.js.map +0 -1
- package/dist/shared/storage/routes.d.ts +0 -42
- package/dist/shared/storage/routes.d.ts.map +0 -1
- package/dist/shared/storage/routes.js +0 -160
- package/dist/shared/storage/routes.js.map +0 -1
- package/dist/shared/storage/types.d.ts +0 -53
- package/dist/shared/storage/types.d.ts.map +0 -1
- package/dist/shared/storage/types.js +0 -2
- package/dist/shared/storage/types.js.map +0 -1
- package/dist/shared/telegram/index.d.ts +0 -4
- package/dist/shared/telegram/index.d.ts.map +0 -1
- package/dist/shared/telegram/index.js +0 -3
- package/dist/shared/telegram/index.js.map +0 -1
- package/dist/shared/telegram/routes.d.ts +0 -48
- package/dist/shared/telegram/routes.d.ts.map +0 -1
- package/dist/shared/telegram/routes.js +0 -918
- package/dist/shared/telegram/routes.js.map +0 -1
- package/dist/shared/telegram/types.d.ts +0 -186
- package/dist/shared/telegram/types.d.ts.map +0 -1
- package/dist/shared/telegram/types.js +0 -7
- package/dist/shared/telegram/types.js.map +0 -1
- package/dist/shared/telegram/utils.d.ts +0 -44
- package/dist/shared/telegram/utils.d.ts.map +0 -1
- package/dist/shared/telegram/utils.js +0 -121
- package/dist/shared/telegram/utils.js.map +0 -1
- package/dist/storage.d.ts +0 -9
- package/dist/storage.d.ts.map +0 -1
- package/dist/storage.js +0 -8
- package/dist/storage.js.map +0 -1
- package/dist/telemetry.d.ts +0 -9
- package/dist/telemetry.d.ts.map +0 -1
- package/dist/telemetry.js +0 -8
- package/dist/telemetry.js.map +0 -1
- package/scripts/postinstall.js +0 -79
- package/src/shared/affiliate/migrations/001_create_affiliates.sql +0 -49
- package/src/shared/affiliate/migrations/002_create_affiliate_commissions.sql +0 -31
- package/src/shared/affiliate/migrations/003_create_affiliate_clicks.sql +0 -26
- package/src/shared/affiliate/migrations/004_create_affiliate_payments.sql +0 -34
- package/src/shared/affiliate/migrations/005_create_affiliate_tier_history.sql +0 -19
- package/src/shared/affiliate/migrations/006_create_affiliate_rpc_functions.sql +0 -209
- package/src/shared/affiliate/migrations/007_create_affiliate_rls_policies.sql +0 -123
- package/src/shared/billing/migrations/00000000000001_billing.sql +0 -114
package/dist/email.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"email.js","sourceRoot":"","sources":["../src/email.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,aAAa,EAAE,UAAU,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAClJ,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,eAAe,EAAE,eAAe,EAAE,eAAe,EAAE,wBAAwB,EAAE,yBAAyB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC"}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
-- ============================================================================
|
|
2
|
-
-- Tetra Core: exec_sql — Secure SQL Execution for RLS Checker
|
|
3
|
-
-- ============================================================================
|
|
4
|
-
-- This function allows the RLS checker to run audit queries against the DB.
|
|
5
|
-
--
|
|
6
|
-
-- SECURITY MODEL:
|
|
7
|
-
-- - SECURITY INVOKER (NOT definer) — runs as the calling role
|
|
8
|
-
-- - Only service_role should call this (via backend service client)
|
|
9
|
-
-- - anon and authenticated are explicitly REVOKED from EXECUTE
|
|
10
|
-
-- - search_path set to '' to prevent search_path injection
|
|
11
|
-
-- - Returns JSONB array so it works with any query shape
|
|
12
|
-
--
|
|
13
|
-
-- WHY THIS IS SAFE:
|
|
14
|
-
-- 1. service_role already bypasses RLS — this adds no new capability
|
|
15
|
-
-- 2. anon/authenticated CANNOT call it (REVOKE + no GRANT)
|
|
16
|
-
-- 3. Even if somehow called by authenticated, it runs as THAT role
|
|
17
|
-
-- (SECURITY INVOKER), so RLS still applies to their queries
|
|
18
|
-
-- 4. The function is just a thin wrapper around EXECUTE — it doesn't
|
|
19
|
-
-- escalate privileges
|
|
20
|
-
--
|
|
21
|
-
-- PREREQUISITE: Must be deployed BEFORE runRLSCheck() can work.
|
|
22
|
-
-- ============================================================================
|
|
23
|
-
|
|
24
|
-
-- Drop old versions if they exist
|
|
25
|
-
DROP FUNCTION IF EXISTS public.exec_sql(text);
|
|
26
|
-
DROP FUNCTION IF EXISTS public.execute_sql(text);
|
|
27
|
-
|
|
28
|
-
-- Create the function
|
|
29
|
-
CREATE OR REPLACE FUNCTION public.exec_sql(query text)
|
|
30
|
-
RETURNS jsonb
|
|
31
|
-
LANGUAGE plpgsql
|
|
32
|
-
VOLATILE
|
|
33
|
-
SECURITY INVOKER
|
|
34
|
-
SET search_path = ''
|
|
35
|
-
AS $$
|
|
36
|
-
DECLARE
|
|
37
|
-
result jsonb;
|
|
38
|
-
BEGIN
|
|
39
|
-
EXECUTE 'SELECT COALESCE(jsonb_agg(row_to_json(t)), ''[]''::jsonb) FROM (' || query || ') t'
|
|
40
|
-
INTO result;
|
|
41
|
-
RETURN result;
|
|
42
|
-
END;
|
|
43
|
-
$$;
|
|
44
|
-
|
|
45
|
-
-- CRITICAL: Revoke from all public-facing roles
|
|
46
|
-
REVOKE ALL ON FUNCTION public.exec_sql(text) FROM PUBLIC;
|
|
47
|
-
REVOKE ALL ON FUNCTION public.exec_sql(text) FROM anon;
|
|
48
|
-
REVOKE ALL ON FUNCTION public.exec_sql(text) FROM authenticated;
|
|
49
|
-
|
|
50
|
-
-- Only service_role (backend) and postgres (migrations) can use it
|
|
51
|
-
GRANT EXECUTE ON FUNCTION public.exec_sql(text) TO service_role;
|
|
52
|
-
GRANT EXECUTE ON FUNCTION public.exec_sql(text) TO postgres;
|
|
53
|
-
|
|
54
|
-
-- Add a comment explaining the security model
|
|
55
|
-
COMMENT ON FUNCTION public.exec_sql(text) IS
|
|
56
|
-
'Tetra RLS Checker: executes audit SQL. SECURITY INVOKER + only service_role can call. '
|
|
57
|
-
'anon/authenticated are explicitly revoked. Does NOT escalate privileges.';
|
package/dist/generators.d.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @soulbatical/tetra-core/generators
|
|
3
|
-
*
|
|
4
|
-
* Compatibility entrypoint.
|
|
5
|
-
* The canonical generator surface now lives in `@soulbatical/tetra-supabase`.
|
|
6
|
-
*/
|
|
7
|
-
export { generateRLS, generateRLSBatch, getAuditChecks, generateAuditSQL, runRLSCheck, runRLSCheckDebug, generateExecSQL, checkExecSQLExists, RPCGenerator, DetailRPCGenerator, validateRPCConfig, generateAuthCheck, generateAuthWhereClause, generateAuthDeclarations, generateTimestamp, getTableAlias, escapeIdentifier, } from '@soulbatical/tetra-supabase';
|
|
8
|
-
export type { RLSConfig, RLSTableType, RLSGeneratorResult, AuditCheck, RLSReport, RLSCheckResult, RLSViolation, RLSCategorySummary, GeneratedSQL, FilterDefinition, RPCGeneratorOptions, DetailRPCGeneratorOptions, RPCValidationResult, RPCValidationError, AccessLevel, CreatorVisibilityConfig as RPCCreatorVisibilityConfig, } from '@soulbatical/tetra-supabase';
|
|
9
|
-
//# sourceMappingURL=generators.d.ts.map
|
package/dist/generators.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"generators.d.ts","sourceRoot":"","sources":["../src/generators.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,WAAW,EACX,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EAChB,WAAW,EACX,gBAAgB,EAChB,eAAe,EACf,kBAAkB,EAClB,YAAY,EACZ,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,uBAAuB,EACvB,wBAAwB,EACxB,iBAAiB,EACjB,aAAa,EACb,gBAAgB,GACjB,MAAM,6BAA6B,CAAC;AAErC,YAAY,EACV,SAAS,EACT,YAAY,EACZ,kBAAkB,EAClB,UAAU,EACV,SAAS,EACT,cAAc,EACd,YAAY,EACZ,kBAAkB,EAClB,YAAY,EACZ,gBAAgB,EAChB,mBAAmB,EACnB,yBAAyB,EACzB,mBAAmB,EACnB,kBAAkB,EAClB,WAAW,EACX,uBAAuB,IAAI,0BAA0B,GACtD,MAAM,6BAA6B,CAAC"}
|
package/dist/generators.js
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @soulbatical/tetra-core/generators
|
|
3
|
-
*
|
|
4
|
-
* Compatibility entrypoint.
|
|
5
|
-
* The canonical generator surface now lives in `@soulbatical/tetra-supabase`.
|
|
6
|
-
*/
|
|
7
|
-
export { generateRLS, generateRLSBatch, getAuditChecks, generateAuditSQL, runRLSCheck, runRLSCheckDebug, generateExecSQL, checkExecSQLExists, RPCGenerator, DetailRPCGenerator, validateRPCConfig, generateAuthCheck, generateAuthWhereClause, generateAuthDeclarations, generateTimestamp, getTableAlias, escapeIdentifier, } from '@soulbatical/tetra-supabase';
|
|
8
|
-
//# sourceMappingURL=generators.js.map
|
package/dist/generators.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"generators.js","sourceRoot":"","sources":["../src/generators.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,WAAW,EACX,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EAChB,WAAW,EACX,gBAAgB,EAChB,eAAe,EACf,kBAAkB,EAClB,YAAY,EACZ,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,uBAAuB,EACvB,wBAAwB,EACxB,iBAAiB,EACjB,aAAa,EACb,gBAAgB,GACjB,MAAM,6BAA6B,CAAC"}
|
package/dist/mcp.d.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @soulbatical/tetra-core/mcp
|
|
3
|
-
*
|
|
4
|
-
* MCP Online module. Requires @modelcontextprotocol/sdk.
|
|
5
|
-
*/
|
|
6
|
-
export { addMcpRoutes, addMcpAuthRoutes, addMcpTokenRoutes, addMcpUsageRoutes, getMcpOrganizationId, getMcpTenantContext, runWithMcpTenant, validateMcpApiToken, generateMcpApiToken, mcpDB, mcpScopedDB } from './shared/mcp/index.js';
|
|
7
|
-
export type { McpRoutesConfig, McpAuthRoutesConfig, McpToolDefinition, McpToolResult, McpToolHandler, TenantContext, TenantContext as McpTenantContext, McpDBResult, McpDBLocalResult, McpDBOptions, GenerateMcpApiTokenOptions } from './shared/mcp/index.js';
|
|
8
|
-
//# sourceMappingURL=mcp.d.ts.map
|
package/dist/mcp.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACxO,YAAY,EAAE,eAAe,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,aAAa,EAAE,cAAc,EAAE,aAAa,EAAE,aAAa,IAAI,gBAAgB,EAAE,WAAW,EAAE,gBAAgB,EAAE,YAAY,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAC"}
|
package/dist/mcp.js
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @soulbatical/tetra-core/mcp
|
|
3
|
-
*
|
|
4
|
-
* MCP Online module. Requires @modelcontextprotocol/sdk.
|
|
5
|
-
*/
|
|
6
|
-
export { addMcpRoutes, addMcpAuthRoutes, addMcpTokenRoutes, addMcpUsageRoutes, getMcpOrganizationId, getMcpTenantContext, runWithMcpTenant, validateMcpApiToken, generateMcpApiToken, mcpDB, mcpScopedDB } from './shared/mcp/index.js';
|
|
7
|
-
//# sourceMappingURL=mcp.js.map
|
package/dist/mcp.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mcp.js","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC"}
|
package/dist/planner.d.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @soulbatical/tetra-core/planner
|
|
3
|
-
*
|
|
4
|
-
* Planner module (scheduling, availability, Google Calendar).
|
|
5
|
-
*/
|
|
6
|
-
export { PlannerService, GoogleCalendarService, addPlannerRoutes, addPlannerPublicRoutes, addPlannerCalendarRoutes, computeAvailableSlots, timeToMinutes, minutesToTime } from './shared/planner/index.js';
|
|
7
|
-
export type { PlannerConfig, PlannerCalendarConfig, PlannerRouteOptions, PlannerCallbackContext, AvailabilitySlotRecord, AvailabilitySlotInput, AppointmentRecord, AppointmentInput, SchedulerSettingsRecord, VacationRecord, CalendarTokenRecord, PublicBookingInput, PublicBookingVisitor, TimeSlot, DayAvailability, TeamAvailability, PublicSchedulerData } from './shared/planner/index.js';
|
|
8
|
-
//# sourceMappingURL=planner.d.ts.map
|
package/dist/planner.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"planner.d.ts","sourceRoot":"","sources":["../src/planner.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC3M,YAAY,EAAE,aAAa,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,cAAc,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,QAAQ,EAAE,eAAe,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC"}
|
package/dist/planner.js
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @soulbatical/tetra-core/planner
|
|
3
|
-
*
|
|
4
|
-
* Planner module (scheduling, availability, Google Calendar).
|
|
5
|
-
*/
|
|
6
|
-
export { PlannerService, GoogleCalendarService, addPlannerRoutes, addPlannerPublicRoutes, addPlannerCalendarRoutes, computeAvailableSlots, timeToMinutes, minutesToTime } from './shared/planner/index.js';
|
|
7
|
-
//# sourceMappingURL=planner.js.map
|
package/dist/planner.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"planner.js","sourceRoot":"","sources":["../src/planner.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC"}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AffiliateAttributionService
|
|
3
|
-
*
|
|
4
|
-
* Generic affiliate attribution and commission engine.
|
|
5
|
-
* Internal referral only (voucher > URL > cookie).
|
|
6
|
-
* Config-driven tier upgrades.
|
|
7
|
-
*
|
|
8
|
-
* @module @soulbatical/tetra-core/affiliate
|
|
9
|
-
*/
|
|
10
|
-
import type { SupabaseClient } from '@supabase/supabase-js';
|
|
11
|
-
import type { Request } from 'express';
|
|
12
|
-
import type { AffiliateAttribution, AffiliateConfig, AffiliateOrder } from './types.js';
|
|
13
|
-
export declare class AffiliateAttributionService {
|
|
14
|
-
private supabase;
|
|
15
|
-
private organizationId?;
|
|
16
|
-
private config;
|
|
17
|
-
constructor(supabase: SupabaseClient, config?: Partial<AffiliateConfig>, organizationId?: string);
|
|
18
|
-
/**
|
|
19
|
-
* Determine affiliate attribution for an order.
|
|
20
|
-
* Priority: Voucher > URL (?ref=) > Cookie
|
|
21
|
-
*/
|
|
22
|
-
determineAttribution(req: Request, voucherCode?: string): Promise<AffiliateAttribution | null>;
|
|
23
|
-
/**
|
|
24
|
-
* Create a commission record for an affiliate sale.
|
|
25
|
-
* Updates affiliate stats and checks for tier upgrade.
|
|
26
|
-
*/
|
|
27
|
-
createCommission(order: AffiliateOrder, attribution: AffiliateAttribution): Promise<void>;
|
|
28
|
-
/**
|
|
29
|
-
* Generate a referral code for a new affiliate.
|
|
30
|
-
* Uses custom generator from config or default format.
|
|
31
|
-
*/
|
|
32
|
-
generateReferralCode(contactName: string): string;
|
|
33
|
-
private checkVoucherAttribution;
|
|
34
|
-
private checkUrlParameter;
|
|
35
|
-
private checkCookie;
|
|
36
|
-
/**
|
|
37
|
-
* Resolve an affiliate from referral code or ID.
|
|
38
|
-
*/
|
|
39
|
-
resolveAffiliate(reference: string, affiliateId: string | null): Promise<any>;
|
|
40
|
-
private updateAffiliateStats;
|
|
41
|
-
/**
|
|
42
|
-
* Check if affiliate qualifies for tier upgrade (config-driven).
|
|
43
|
-
*/
|
|
44
|
-
checkTierUpgrade(affiliateId: string, totalSales: number): Promise<void>;
|
|
45
|
-
private upgradeAffiliateTier;
|
|
46
|
-
}
|
|
47
|
-
//# sourceMappingURL=AffiliateAttributionService.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AffiliateAttributionService.d.ts","sourceRoot":"","sources":["../../../src/shared/affiliate/AffiliateAttributionService.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAGvC,OAAO,KAAK,EAAE,oBAAoB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AA0BxF,qBAAa,2BAA2B;IACtC,OAAO,CAAC,QAAQ,CAAiB;IACjC,OAAO,CAAC,cAAc,CAAC,CAAS;IAChC,OAAO,CAAC,MAAM,CAAkB;gBAG9B,QAAQ,EAAE,cAAc,EACxB,MAAM,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,EACjC,cAAc,CAAC,EAAE,MAAM;IASzB;;;OAGG;IACG,oBAAoB,CACxB,GAAG,EAAE,OAAO,EACZ,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC;IAgCvC;;;OAGG;IACG,gBAAgB,CACpB,KAAK,EAAE,cAAc,EACrB,WAAW,EAAE,oBAAoB,GAChC,OAAO,CAAC,IAAI,CAAC;IA6DhB;;;OAGG;IACH,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM;YAOnC,uBAAuB;IAwBrC,OAAO,CAAC,iBAAiB;IAYzB,OAAO,CAAC,WAAW;IAiCnB;;OAEG;IACG,gBAAgB,CACpB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,GAAG,IAAI,GACzB,OAAO,CAAC,GAAG,CAAC;YAkCD,oBAAoB;IAgBlC;;OAEG;IACG,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YA8ChE,oBAAoB;CAkDnC"}
|
|
@@ -1,308 +0,0 @@
|
|
|
1
|
-
import crypto from 'crypto';
|
|
2
|
-
import { createLogger } from '../../utils/logger.js';
|
|
3
|
-
const logger = createLogger('service:affiliate:attribution');
|
|
4
|
-
const DEFAULT_CONFIG = {
|
|
5
|
-
defaultCommissionPercentage: 30,
|
|
6
|
-
cookieDurationDays: 90,
|
|
7
|
-
tiers: [
|
|
8
|
-
{ name: 'starter', minSales: 0, commissionPercentage: 30 },
|
|
9
|
-
{ name: 'active', minSales: 10, commissionPercentage: 35 },
|
|
10
|
-
],
|
|
11
|
-
};
|
|
12
|
-
/**
|
|
13
|
-
* Generate a referral code from a contact name.
|
|
14
|
-
* Format: firstname-XXXX (e.g., 'monique-1234')
|
|
15
|
-
*/
|
|
16
|
-
function defaultReferralCodeGenerator(contactName) {
|
|
17
|
-
const normalized = contactName
|
|
18
|
-
.toLowerCase()
|
|
19
|
-
.replace(/[^a-z0-9]/g, '')
|
|
20
|
-
.substring(0, 15);
|
|
21
|
-
const randomDigits = crypto.randomInt(1000, 10000);
|
|
22
|
-
return `${normalized}-${randomDigits}`;
|
|
23
|
-
}
|
|
24
|
-
export class AffiliateAttributionService {
|
|
25
|
-
supabase;
|
|
26
|
-
organizationId;
|
|
27
|
-
config;
|
|
28
|
-
constructor(supabase, config, organizationId) {
|
|
29
|
-
this.supabase = supabase;
|
|
30
|
-
this.organizationId = organizationId;
|
|
31
|
-
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
32
|
-
}
|
|
33
|
-
// ─── Attribution ────────────────────────────────────────────
|
|
34
|
-
/**
|
|
35
|
-
* Determine affiliate attribution for an order.
|
|
36
|
-
* Priority: Voucher > URL (?ref=) > Cookie
|
|
37
|
-
*/
|
|
38
|
-
async determineAttribution(req, voucherCode) {
|
|
39
|
-
logger.info('Determining affiliate attribution', { voucherCode });
|
|
40
|
-
// 1. Voucher with affiliate
|
|
41
|
-
if (voucherCode) {
|
|
42
|
-
const attribution = await this.checkVoucherAttribution(voucherCode);
|
|
43
|
-
if (attribution) {
|
|
44
|
-
logger.info('Attribution via voucher', attribution);
|
|
45
|
-
return attribution;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
// 2. URL parameter (?ref=)
|
|
49
|
-
const urlAttribution = this.checkUrlParameter(req);
|
|
50
|
-
if (urlAttribution) {
|
|
51
|
-
logger.info('Attribution via URL', urlAttribution);
|
|
52
|
-
return urlAttribution;
|
|
53
|
-
}
|
|
54
|
-
// 3. Cookie fallback
|
|
55
|
-
const cookieAttribution = this.checkCookie(req);
|
|
56
|
-
if (cookieAttribution) {
|
|
57
|
-
logger.info('Attribution via cookie', cookieAttribution);
|
|
58
|
-
return cookieAttribution;
|
|
59
|
-
}
|
|
60
|
-
logger.info('No affiliate attribution found');
|
|
61
|
-
return null;
|
|
62
|
-
}
|
|
63
|
-
// ─── Commission ─────────────────────────────────────────────
|
|
64
|
-
/**
|
|
65
|
-
* Create a commission record for an affiliate sale.
|
|
66
|
-
* Updates affiliate stats and checks for tier upgrade.
|
|
67
|
-
*/
|
|
68
|
-
async createCommission(order, attribution) {
|
|
69
|
-
logger.info('Creating commission', { orderId: order.id, attribution });
|
|
70
|
-
const affiliate = await this.resolveAffiliate(attribution.reference, attribution.affiliate_id);
|
|
71
|
-
if (!affiliate) {
|
|
72
|
-
logger.warn('Affiliate not found', { attribution });
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
const orderAmount = order.total_excl_vat;
|
|
76
|
-
const commissionAmount = orderAmount * (affiliate.commission_percentage / 100);
|
|
77
|
-
logger.info('Commission calculated', {
|
|
78
|
-
affiliateId: affiliate.id,
|
|
79
|
-
orderAmount,
|
|
80
|
-
commissionPercentage: affiliate.commission_percentage,
|
|
81
|
-
commissionAmount,
|
|
82
|
-
});
|
|
83
|
-
// Create commission record
|
|
84
|
-
const { data: commission, error } = await this.supabase
|
|
85
|
-
.from('affiliate_commissions')
|
|
86
|
-
.insert({
|
|
87
|
-
organization_id: order.organization_id,
|
|
88
|
-
affiliate_id: affiliate.id,
|
|
89
|
-
order_id: order.id,
|
|
90
|
-
affiliate_source: 'internal',
|
|
91
|
-
product_name: 'Order',
|
|
92
|
-
order_amount_excl_vat: orderAmount,
|
|
93
|
-
commission_percentage: affiliate.commission_percentage,
|
|
94
|
-
commission_amount: commissionAmount,
|
|
95
|
-
status: 'pending',
|
|
96
|
-
})
|
|
97
|
-
.select()
|
|
98
|
-
.single();
|
|
99
|
-
if (error) {
|
|
100
|
-
logger.error('Failed to create commission', { error, orderId: order.id });
|
|
101
|
-
throw error;
|
|
102
|
-
}
|
|
103
|
-
logger.info('Commission created', { commissionId: commission.id });
|
|
104
|
-
// Update affiliate stats
|
|
105
|
-
await this.updateAffiliateStats(affiliate.id, {
|
|
106
|
-
total_sales: affiliate.total_sales + 1,
|
|
107
|
-
total_revenue: affiliate.total_revenue + orderAmount,
|
|
108
|
-
total_commission_earned: affiliate.total_commission_earned + commissionAmount,
|
|
109
|
-
last_sale_at: new Date().toISOString(),
|
|
110
|
-
});
|
|
111
|
-
// Check for tier upgrade
|
|
112
|
-
await this.checkTierUpgrade(affiliate.id, affiliate.total_sales + 1);
|
|
113
|
-
}
|
|
114
|
-
// ─── Referral Code ──────────────────────────────────────────
|
|
115
|
-
/**
|
|
116
|
-
* Generate a referral code for a new affiliate.
|
|
117
|
-
* Uses custom generator from config or default format.
|
|
118
|
-
*/
|
|
119
|
-
generateReferralCode(contactName) {
|
|
120
|
-
const generator = this.config.referralCodeGenerator || defaultReferralCodeGenerator;
|
|
121
|
-
return generator(contactName);
|
|
122
|
-
}
|
|
123
|
-
// ─── Private: Attribution Checks ────────────────────────────
|
|
124
|
-
async checkVoucherAttribution(voucherCode) {
|
|
125
|
-
try {
|
|
126
|
-
const { data: voucher, error } = await this.supabase
|
|
127
|
-
.from('vouchers')
|
|
128
|
-
.select('id, affiliate_id')
|
|
129
|
-
.eq('code', voucherCode)
|
|
130
|
-
.single();
|
|
131
|
-
if (error || !voucher?.affiliate_id) {
|
|
132
|
-
return null;
|
|
133
|
-
}
|
|
134
|
-
return {
|
|
135
|
-
affiliate_id: voucher.affiliate_id,
|
|
136
|
-
source: 'internal',
|
|
137
|
-
attribution_type: 'voucher',
|
|
138
|
-
reference: voucherCode,
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
catch (error) {
|
|
142
|
-
logger.error('Error checking voucher attribution', error);
|
|
143
|
-
return null;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
checkUrlParameter(req) {
|
|
147
|
-
const ref = req.query.ref;
|
|
148
|
-
if (!ref)
|
|
149
|
-
return null;
|
|
150
|
-
return {
|
|
151
|
-
affiliate_id: null,
|
|
152
|
-
source: 'internal',
|
|
153
|
-
attribution_type: 'url',
|
|
154
|
-
reference: ref,
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
checkCookie(req) {
|
|
158
|
-
const cookie = req.cookies?.affiliate_ref;
|
|
159
|
-
if (!cookie)
|
|
160
|
-
return null;
|
|
161
|
-
try {
|
|
162
|
-
const data = JSON.parse(cookie);
|
|
163
|
-
const age = Date.now() - data.timestamp;
|
|
164
|
-
const maxAge = this.config.cookieDurationDays * 24 * 60 * 60 * 1000;
|
|
165
|
-
if (age > maxAge) {
|
|
166
|
-
logger.info('Affiliate cookie expired', { ageDays: Math.floor(age / (24 * 60 * 60 * 1000)) });
|
|
167
|
-
return null;
|
|
168
|
-
}
|
|
169
|
-
// Only accept internal source in tetra-core
|
|
170
|
-
if (data.source && data.source !== 'internal') {
|
|
171
|
-
return null;
|
|
172
|
-
}
|
|
173
|
-
return {
|
|
174
|
-
affiliate_id: null,
|
|
175
|
-
source: 'internal',
|
|
176
|
-
attribution_type: 'cookie',
|
|
177
|
-
reference: data.ref,
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
|
-
catch (error) {
|
|
181
|
-
logger.error('Error parsing affiliate cookie', error);
|
|
182
|
-
return null;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
// ─── Private: Affiliate Resolution ──────────────────────────
|
|
186
|
-
/**
|
|
187
|
-
* Resolve an affiliate from referral code or ID.
|
|
188
|
-
*/
|
|
189
|
-
async resolveAffiliate(reference, affiliateId) {
|
|
190
|
-
// If affiliate ID already known (voucher attribution)
|
|
191
|
-
if (affiliateId) {
|
|
192
|
-
const { data, error } = await this.supabase
|
|
193
|
-
.from('affiliates')
|
|
194
|
-
.select('*')
|
|
195
|
-
.eq('id', affiliateId)
|
|
196
|
-
.eq('status', 'active')
|
|
197
|
-
.single();
|
|
198
|
-
if (error) {
|
|
199
|
-
logger.error('Failed to fetch affiliate by ID', { error, affiliateId });
|
|
200
|
-
return null;
|
|
201
|
-
}
|
|
202
|
-
return data;
|
|
203
|
-
}
|
|
204
|
-
// Look up by referral code
|
|
205
|
-
const { data, error } = await this.supabase
|
|
206
|
-
.from('affiliates')
|
|
207
|
-
.select('*')
|
|
208
|
-
.eq('referral_code', reference)
|
|
209
|
-
.eq('status', 'active')
|
|
210
|
-
.single();
|
|
211
|
-
if (error) {
|
|
212
|
-
logger.error('Failed to fetch affiliate by referral code', { error, reference });
|
|
213
|
-
return null;
|
|
214
|
-
}
|
|
215
|
-
return data;
|
|
216
|
-
}
|
|
217
|
-
// ─── Private: Stats & Tiers ─────────────────────────────────
|
|
218
|
-
async updateAffiliateStats(affiliateId, updates) {
|
|
219
|
-
const { error } = await this.supabase
|
|
220
|
-
.from('affiliates')
|
|
221
|
-
.update(updates)
|
|
222
|
-
.eq('id', affiliateId);
|
|
223
|
-
if (error) {
|
|
224
|
-
logger.error('Failed to update affiliate stats', { error, affiliateId });
|
|
225
|
-
throw error;
|
|
226
|
-
}
|
|
227
|
-
logger.info('Affiliate stats updated', { affiliateId, updates });
|
|
228
|
-
}
|
|
229
|
-
/**
|
|
230
|
-
* Check if affiliate qualifies for tier upgrade (config-driven).
|
|
231
|
-
*/
|
|
232
|
-
async checkTierUpgrade(affiliateId, totalSales) {
|
|
233
|
-
const { data: affiliate, error } = await this.supabase
|
|
234
|
-
.from('affiliates')
|
|
235
|
-
.select('tier, commission_percentage')
|
|
236
|
-
.eq('id', affiliateId)
|
|
237
|
-
.single();
|
|
238
|
-
if (error || !affiliate) {
|
|
239
|
-
logger.error('Failed to fetch affiliate for tier check', { error, affiliateId });
|
|
240
|
-
return;
|
|
241
|
-
}
|
|
242
|
-
// Find the highest qualifying tier
|
|
243
|
-
const sortedTiers = [...this.config.tiers].sort((a, b) => b.minSales - a.minSales);
|
|
244
|
-
const qualifyingTier = sortedTiers.find(t => totalSales >= t.minSales);
|
|
245
|
-
if (!qualifyingTier || qualifyingTier.name === affiliate.tier) {
|
|
246
|
-
return; // No upgrade needed
|
|
247
|
-
}
|
|
248
|
-
// Check if this is actually an upgrade (not a downgrade)
|
|
249
|
-
const currentTierIndex = this.config.tiers.findIndex(t => t.name === affiliate.tier);
|
|
250
|
-
const newTierIndex = this.config.tiers.findIndex(t => t.name === qualifyingTier.name);
|
|
251
|
-
if (newTierIndex <= currentTierIndex) {
|
|
252
|
-
return;
|
|
253
|
-
}
|
|
254
|
-
logger.info('Tier upgrade triggered', {
|
|
255
|
-
affiliateId,
|
|
256
|
-
oldTier: affiliate.tier,
|
|
257
|
-
newTier: qualifyingTier.name,
|
|
258
|
-
oldCommission: affiliate.commission_percentage,
|
|
259
|
-
newCommission: qualifyingTier.commissionPercentage,
|
|
260
|
-
totalSales,
|
|
261
|
-
});
|
|
262
|
-
await this.upgradeAffiliateTier(affiliateId, {
|
|
263
|
-
old_tier: affiliate.tier,
|
|
264
|
-
new_tier: qualifyingTier.name,
|
|
265
|
-
old_commission: affiliate.commission_percentage,
|
|
266
|
-
new_commission: qualifyingTier.commissionPercentage,
|
|
267
|
-
reason: 'milestone_reached',
|
|
268
|
-
triggered_by_sale_count: totalSales,
|
|
269
|
-
});
|
|
270
|
-
}
|
|
271
|
-
async upgradeAffiliateTier(affiliateId, upgrade) {
|
|
272
|
-
// Update affiliate tier and commission
|
|
273
|
-
const { error: updateError } = await this.supabase
|
|
274
|
-
.from('affiliates')
|
|
275
|
-
.update({
|
|
276
|
-
tier: upgrade.new_tier,
|
|
277
|
-
commission_percentage: upgrade.new_commission,
|
|
278
|
-
updated_at: new Date().toISOString(),
|
|
279
|
-
})
|
|
280
|
-
.eq('id', affiliateId);
|
|
281
|
-
if (updateError) {
|
|
282
|
-
logger.error('Failed to update affiliate tier', { error: updateError, affiliateId });
|
|
283
|
-
throw updateError;
|
|
284
|
-
}
|
|
285
|
-
// Record in tier history
|
|
286
|
-
const { error: historyError } = await this.supabase
|
|
287
|
-
.from('affiliate_tier_history')
|
|
288
|
-
.insert({
|
|
289
|
-
affiliate_id: affiliateId,
|
|
290
|
-
old_tier: upgrade.old_tier,
|
|
291
|
-
new_tier: upgrade.new_tier,
|
|
292
|
-
old_commission_percentage: upgrade.old_commission,
|
|
293
|
-
new_commission_percentage: upgrade.new_commission,
|
|
294
|
-
reason: upgrade.reason,
|
|
295
|
-
triggered_by_sale_count: upgrade.triggered_by_sale_count,
|
|
296
|
-
});
|
|
297
|
-
if (historyError) {
|
|
298
|
-
logger.error('Failed to record tier history', { error: historyError, affiliateId });
|
|
299
|
-
// Don't throw — tier upgrade succeeded even if history failed
|
|
300
|
-
}
|
|
301
|
-
logger.info('Tier upgraded successfully', {
|
|
302
|
-
affiliateId,
|
|
303
|
-
oldTier: upgrade.old_tier,
|
|
304
|
-
newTier: upgrade.new_tier,
|
|
305
|
-
});
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
//# sourceMappingURL=AffiliateAttributionService.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AffiliateAttributionService.js","sourceRoot":"","sources":["../../../src/shared/affiliate/AffiliateAttributionService.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AffiliateClickService
|
|
3
|
-
*
|
|
4
|
-
* Handles click tracking and conversion marking for the affiliate module.
|
|
5
|
-
*
|
|
6
|
-
* @module @soulbatical/tetra-core/affiliate
|
|
7
|
-
*/
|
|
8
|
-
import type { SupabaseClient } from '@supabase/supabase-js';
|
|
9
|
-
export declare class AffiliateClickService {
|
|
10
|
-
private supabase;
|
|
11
|
-
constructor(supabase: SupabaseClient);
|
|
12
|
-
/**
|
|
13
|
-
* Register an affiliate click event.
|
|
14
|
-
* Returns the click record ID.
|
|
15
|
-
*/
|
|
16
|
-
registerClick(data: {
|
|
17
|
-
affiliate_id?: string | null;
|
|
18
|
-
organization_id?: string | null;
|
|
19
|
-
affiliate_source: string;
|
|
20
|
-
visitor_ip?: string | null;
|
|
21
|
-
user_agent?: string | null;
|
|
22
|
-
referrer_url?: string | null;
|
|
23
|
-
landing_page?: string | null;
|
|
24
|
-
external_click_ref?: string | null;
|
|
25
|
-
}): Promise<string | null>;
|
|
26
|
-
/**
|
|
27
|
-
* Increment affiliate total_clicks counter.
|
|
28
|
-
*/
|
|
29
|
-
incrementClickCount(affiliateId: string): Promise<void>;
|
|
30
|
-
/**
|
|
31
|
-
* Mark a click as converted after a successful order.
|
|
32
|
-
*/
|
|
33
|
-
markConverted(clickId: string, orderId: string): Promise<void>;
|
|
34
|
-
}
|
|
35
|
-
//# sourceMappingURL=AffiliateClickService.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AffiliateClickService.d.ts","sourceRoot":"","sources":["../../../src/shared/affiliate/AffiliateClickService.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAK5D,qBAAa,qBAAqB;IAChC,OAAO,CAAC,QAAQ,CAAiB;gBAErB,QAAQ,EAAE,cAAc;IAIpC;;;OAGG;IACG,aAAa,CAAC,IAAI,EAAE;QACxB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC7B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAChC,gBAAgB,EAAE,MAAM,CAAC;QACzB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC3B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC3B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC7B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC7B,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KACpC,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IA0B1B;;OAEG;IACG,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAuB7D;;OAEG;IACG,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CA4BrE"}
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import { createLogger } from '../../utils/logger.js';
|
|
2
|
-
const logger = createLogger('service:affiliate:clicks');
|
|
3
|
-
export class AffiliateClickService {
|
|
4
|
-
supabase;
|
|
5
|
-
constructor(supabase) {
|
|
6
|
-
this.supabase = supabase;
|
|
7
|
-
}
|
|
8
|
-
/**
|
|
9
|
-
* Register an affiliate click event.
|
|
10
|
-
* Returns the click record ID.
|
|
11
|
-
*/
|
|
12
|
-
async registerClick(data) {
|
|
13
|
-
const { data: click, error } = await this.supabase
|
|
14
|
-
.from('affiliate_clicks')
|
|
15
|
-
.insert({
|
|
16
|
-
organization_id: data.organization_id || null,
|
|
17
|
-
affiliate_id: data.affiliate_id || null,
|
|
18
|
-
visitor_ip: data.visitor_ip || null,
|
|
19
|
-
user_agent: data.user_agent || null,
|
|
20
|
-
referrer_url: data.referrer_url || null,
|
|
21
|
-
landing_page: data.landing_page || null,
|
|
22
|
-
affiliate_source: data.affiliate_source,
|
|
23
|
-
external_click_ref: data.external_click_ref || null,
|
|
24
|
-
converted: false,
|
|
25
|
-
})
|
|
26
|
-
.select('id')
|
|
27
|
-
.single();
|
|
28
|
-
if (error) {
|
|
29
|
-
logger.error('Failed to record click', { error });
|
|
30
|
-
return null;
|
|
31
|
-
}
|
|
32
|
-
logger.info('Click registered', { clickId: click.id, affiliateId: data.affiliate_id });
|
|
33
|
-
return click.id;
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Increment affiliate total_clicks counter.
|
|
37
|
-
*/
|
|
38
|
-
async incrementClickCount(affiliateId) {
|
|
39
|
-
// Fetch current count, then increment (safe for low-concurrency affiliate use case)
|
|
40
|
-
const { data: affiliate, error: fetchError } = await this.supabase
|
|
41
|
-
.from('affiliates')
|
|
42
|
-
.select('total_clicks')
|
|
43
|
-
.eq('id', affiliateId)
|
|
44
|
-
.single();
|
|
45
|
-
if (fetchError || !affiliate) {
|
|
46
|
-
logger.error('Failed to fetch affiliate for click count update', { error: fetchError, affiliateId });
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
const { error: updateError } = await this.supabase
|
|
50
|
-
.from('affiliates')
|
|
51
|
-
.update({ total_clicks: (affiliate.total_clicks || 0) + 1 })
|
|
52
|
-
.eq('id', affiliateId);
|
|
53
|
-
if (updateError) {
|
|
54
|
-
logger.error('Failed to update click count', { error: updateError, affiliateId });
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Mark a click as converted after a successful order.
|
|
59
|
-
*/
|
|
60
|
-
async markConverted(clickId, orderId) {
|
|
61
|
-
const { data: click, error: fetchError } = await this.supabase
|
|
62
|
-
.from('affiliate_clicks')
|
|
63
|
-
.select('clicked_at')
|
|
64
|
-
.eq('id', clickId)
|
|
65
|
-
.single();
|
|
66
|
-
let conversionTimeHours = null;
|
|
67
|
-
if (!fetchError && click?.clicked_at) {
|
|
68
|
-
const clickedAt = new Date(click.clicked_at).getTime();
|
|
69
|
-
conversionTimeHours = Math.round((Date.now() - clickedAt) / (1000 * 60 * 60));
|
|
70
|
-
}
|
|
71
|
-
const { error } = await this.supabase
|
|
72
|
-
.from('affiliate_clicks')
|
|
73
|
-
.update({
|
|
74
|
-
converted: true,
|
|
75
|
-
order_id: orderId,
|
|
76
|
-
conversion_time_hours: conversionTimeHours,
|
|
77
|
-
})
|
|
78
|
-
.eq('id', clickId);
|
|
79
|
-
if (error) {
|
|
80
|
-
logger.error('Failed to mark click as converted', { error, clickId, orderId });
|
|
81
|
-
}
|
|
82
|
-
else {
|
|
83
|
-
logger.info('Click marked as converted', { clickId, orderId, conversionTimeHours });
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
//# sourceMappingURL=AffiliateClickService.js.map
|