@soulbatical/tetra-core 0.10.4 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +78 -38
- package/dist/core/createApp.d.ts +1 -1
- package/dist/core/createApp.d.ts.map +1 -1
- package/dist/core/createApp.js +77 -2
- package/dist/core/createApp.js.map +1 -1
- package/dist/core/dualWriteProxy.d.ts +7 -2
- package/dist/core/dualWriteProxy.d.ts.map +1 -1
- package/dist/core/dualWriteProxy.js +16 -5
- package/dist/core/dualWriteProxy.js.map +1 -1
- package/dist/core/routeContext.d.ts +24 -0
- package/dist/core/routeContext.d.ts.map +1 -1
- package/dist/core/routeContext.js +31 -4
- package/dist/core/routeContext.js.map +1 -1
- package/dist/core/systemDb.d.ts +2 -2
- package/dist/core/systemDb.js +2 -2
- 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 +3 -31
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -32
- 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/middleware/validateBody.d.ts.map +1 -1
- package/dist/middleware/validateBody.js +51 -8
- package/dist/middleware/validateBody.js.map +1 -1
- package/dist/shared/rfc7807ErrorResponse.d.ts +7 -0
- package/dist/shared/rfc7807ErrorResponse.d.ts.map +1 -1
- package/dist/shared/rfc7807ErrorResponse.js +19 -5
- package/dist/shared/rfc7807ErrorResponse.js.map +1 -1
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +16 -1
- package/dist/utils/logger.js.map +1 -1
- package/package.json +33 -77
- 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 -15
- package/dist/generators.d.ts.map +0 -1
- package/dist/generators.js +0 -12
- 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 -43
- package/dist/shared/telegram/routes.d.ts.map +0 -1
- package/dist/shared/telegram/routes.js +0 -868
- package/dist/shared/telegram/routes.js.map +0 -1
- package/dist/shared/telegram/types.d.ts +0 -168
- 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/src/shared/email/migrations/000_create_email_logs.sql +0 -27
- package/src/shared/email/migrations/001_create_email_templates.sql +0 -27
- package/src/shared/email/migrations/002_add_rls_baseline_policies.sql +0 -37
- package/src/shared/email/migrations/003_create_gmail_accounts.sql +0 -82
- package/src/shared/email/migrations/004_add_email_logs_tracking_columns.sql +0 -15
- package/src/shared/mcp/migrations/001_mcp_api_tokens.sql +0 -21
- package/src/shared/mcp/migrations/002_mcp_audit_log.sql +0 -16
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Storage Proxy Service — streams files from Supabase through the API
|
|
3
|
-
*
|
|
4
|
-
* Sets proper CORS/CORP/Cache headers so images work cross-origin.
|
|
5
|
-
* Never exposes Supabase URLs to the client.
|
|
6
|
-
*
|
|
7
|
-
* Supports on-the-fly image transforms via query parameters:
|
|
8
|
-
* ?w=400 — resize to max width 400px (aspect ratio preserved)
|
|
9
|
-
* ?h=300 — resize to max height 300px
|
|
10
|
-
* ?q=80 — quality 1-100 (default: 80)
|
|
11
|
-
* ?f=webp — output format: webp, jpeg, png (default: auto from original)
|
|
12
|
-
*
|
|
13
|
-
* Transforms require sharp as optional peer dependency.
|
|
14
|
-
* Without sharp, images are served at original size.
|
|
15
|
-
*/
|
|
16
|
-
import { createLogger } from '../../utils/logger.js';
|
|
17
|
-
const logger = createLogger('storage:proxy');
|
|
18
|
-
// Simple LRU-ish in-memory cache for transformed images
|
|
19
|
-
const transformCache = new Map();
|
|
20
|
-
const MAX_CACHE_SIZE = 200;
|
|
21
|
-
const CACHE_TTL_MS = 30 * 60 * 1000; // 30 minutes
|
|
22
|
-
function getCached(key) {
|
|
23
|
-
const entry = transformCache.get(key);
|
|
24
|
-
if (!entry)
|
|
25
|
-
return null;
|
|
26
|
-
if (Date.now() - entry.timestamp > CACHE_TTL_MS) {
|
|
27
|
-
transformCache.delete(key);
|
|
28
|
-
return null;
|
|
29
|
-
}
|
|
30
|
-
return entry;
|
|
31
|
-
}
|
|
32
|
-
function setCache(key, buffer, contentType) {
|
|
33
|
-
// Evict oldest if at capacity
|
|
34
|
-
if (transformCache.size >= MAX_CACHE_SIZE) {
|
|
35
|
-
const oldest = transformCache.keys().next().value;
|
|
36
|
-
if (oldest)
|
|
37
|
-
transformCache.delete(oldest);
|
|
38
|
-
}
|
|
39
|
-
transformCache.set(key, { buffer, contentType, timestamp: Date.now() });
|
|
40
|
-
}
|
|
41
|
-
export class StorageProxyService {
|
|
42
|
-
supabaseUrl;
|
|
43
|
-
cacheMaxAge;
|
|
44
|
-
constructor(config) {
|
|
45
|
-
this.supabaseUrl = config.supabaseUrl || process.env.SUPABASE_URL || '';
|
|
46
|
-
this.cacheMaxAge = config.cacheMaxAge ?? 3600;
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* Parse transform options from query parameters.
|
|
50
|
-
*/
|
|
51
|
-
static parseTransformOptions(query) {
|
|
52
|
-
const w = query.w ? parseInt(query.w, 10) : undefined;
|
|
53
|
-
const h = query.h ? parseInt(query.h, 10) : undefined;
|
|
54
|
-
const q = query.q ? parseInt(query.q, 10) : undefined;
|
|
55
|
-
const f = query.f;
|
|
56
|
-
if (!w && !h && !q && !f)
|
|
57
|
-
return null;
|
|
58
|
-
return {
|
|
59
|
-
width: w && w > 0 && w <= 4000 ? w : undefined,
|
|
60
|
-
height: h && h > 0 && h <= 4000 ? h : undefined,
|
|
61
|
-
quality: q && q > 0 && q <= 100 ? q : 80,
|
|
62
|
-
format: f && ['webp', 'jpeg', 'png'].includes(f) ? f : undefined,
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Stream a file from Supabase storage by full path (wildcard).
|
|
67
|
-
* Supports any depth: bucket/subfolder/org/file.png, etc.
|
|
68
|
-
*/
|
|
69
|
-
async streamByPath(bucket, path, res, transform) {
|
|
70
|
-
if (!this.supabaseUrl) {
|
|
71
|
-
res.status(503).json({ success: false, error: 'Storage not configured' });
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
const cleanPath = path.startsWith('/') ? path.substring(1) : path;
|
|
75
|
-
const url = `${this.supabaseUrl}/storage/v1/object/public/${bucket}/${cleanPath}`;
|
|
76
|
-
return this._stream(url, res, transform);
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Stream a file from Supabase storage to the response.
|
|
80
|
-
* Optionally transforms images on-the-fly with sharp.
|
|
81
|
-
*/
|
|
82
|
-
async streamFile(bucket, orgId, fileId, res, transform) {
|
|
83
|
-
if (!this.supabaseUrl) {
|
|
84
|
-
res.status(503).json({ success: false, error: 'Storage not configured' });
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
const url = `${this.supabaseUrl}/storage/v1/object/public/${bucket}/${orgId}/${fileId}`;
|
|
88
|
-
return this._stream(url, res, transform);
|
|
89
|
-
}
|
|
90
|
-
/** Internal: stream from a resolved Supabase URL */
|
|
91
|
-
async _stream(url, res, transform) {
|
|
92
|
-
// Check transform cache
|
|
93
|
-
if (transform) {
|
|
94
|
-
const cacheKey = `${url}:w${transform.width || ''}:h${transform.height || ''}:q${transform.quality || ''}:f${transform.format || ''}`;
|
|
95
|
-
const cached = getCached(cacheKey);
|
|
96
|
-
if (cached) {
|
|
97
|
-
this.setHeaders(res, cached.contentType);
|
|
98
|
-
res.setHeader('Content-Length', cached.buffer.length.toString());
|
|
99
|
-
res.setHeader('X-Transform-Cache', 'hit');
|
|
100
|
-
res.end(cached.buffer);
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
try {
|
|
105
|
-
const upstream = await fetch(url);
|
|
106
|
-
if (!upstream.ok) {
|
|
107
|
-
res.status(upstream.status === 404 ? 404 : 502).json({
|
|
108
|
-
success: false,
|
|
109
|
-
error: upstream.status === 404 ? 'File not found' : 'Storage error',
|
|
110
|
-
});
|
|
111
|
-
return;
|
|
112
|
-
}
|
|
113
|
-
const contentType = upstream.headers.get('content-type') || 'application/octet-stream';
|
|
114
|
-
const isImage = contentType.startsWith('image/');
|
|
115
|
-
// If transform requested and it's an image, try sharp
|
|
116
|
-
if (transform && isImage) {
|
|
117
|
-
try {
|
|
118
|
-
const buffer = Buffer.from(await upstream.arrayBuffer());
|
|
119
|
-
const transformed = await this.transformImage(buffer, transform);
|
|
120
|
-
const outType = transform.format
|
|
121
|
-
? `image/${transform.format}`
|
|
122
|
-
: contentType;
|
|
123
|
-
// Cache the result
|
|
124
|
-
const cacheKey = `${url}:w${transform.width || ''}:h${transform.height || ''}:q${transform.quality || ''}:f${transform.format || ''}`;
|
|
125
|
-
setCache(cacheKey, transformed, outType);
|
|
126
|
-
this.setHeaders(res, outType);
|
|
127
|
-
res.setHeader('Content-Length', transformed.length.toString());
|
|
128
|
-
res.setHeader('X-Transform', 'applied');
|
|
129
|
-
res.end(transformed);
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
catch (transformError) {
|
|
133
|
-
// Sharp not available or transform failed — fall through to stream original
|
|
134
|
-
logger.debug({ error: transformError }, 'Transform failed, streaming original');
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
// Stream original
|
|
138
|
-
this.setHeaders(res, contentType);
|
|
139
|
-
const contentLength = upstream.headers.get('content-length');
|
|
140
|
-
if (contentLength) {
|
|
141
|
-
res.setHeader('Content-Length', contentLength);
|
|
142
|
-
}
|
|
143
|
-
const reader = upstream.body?.getReader();
|
|
144
|
-
if (!reader) {
|
|
145
|
-
res.status(502).json({ success: false, error: 'No response body' });
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
while (true) {
|
|
149
|
-
const { done, value } = await reader.read();
|
|
150
|
-
if (done)
|
|
151
|
-
break;
|
|
152
|
-
res.write(value);
|
|
153
|
-
}
|
|
154
|
-
res.end();
|
|
155
|
-
}
|
|
156
|
-
catch (err) {
|
|
157
|
-
logger.error({ error: err, url }, 'Failed to stream file');
|
|
158
|
-
res.status(502).json({ success: false, error: 'Failed to fetch from storage' });
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
setHeaders(res, contentType) {
|
|
162
|
-
res.setHeader('Content-Type', contentType);
|
|
163
|
-
res.setHeader('Cache-Control', `public, max-age=${this.cacheMaxAge}, immutable`);
|
|
164
|
-
res.setHeader('Cross-Origin-Resource-Policy', 'cross-origin');
|
|
165
|
-
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
166
|
-
}
|
|
167
|
-
async transformImage(buffer, options) {
|
|
168
|
-
// Dynamic import — sharp is optional peer dep
|
|
169
|
-
const sharp = (await import('sharp')).default;
|
|
170
|
-
let pipeline = sharp(buffer);
|
|
171
|
-
if (options.width || options.height) {
|
|
172
|
-
pipeline = pipeline.resize(options.width, options.height, {
|
|
173
|
-
fit: 'inside',
|
|
174
|
-
withoutEnlargement: true,
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
const quality = options.quality || 80;
|
|
178
|
-
switch (options.format) {
|
|
179
|
-
case 'webp':
|
|
180
|
-
pipeline = pipeline.webp({ quality });
|
|
181
|
-
break;
|
|
182
|
-
case 'jpeg':
|
|
183
|
-
pipeline = pipeline.jpeg({ quality, progressive: true });
|
|
184
|
-
break;
|
|
185
|
-
case 'png':
|
|
186
|
-
pipeline = pipeline.png({ quality });
|
|
187
|
-
break;
|
|
188
|
-
default:
|
|
189
|
-
// Auto-detect from original — apply quality
|
|
190
|
-
pipeline = pipeline.jpeg({ quality, progressive: true });
|
|
191
|
-
break;
|
|
192
|
-
}
|
|
193
|
-
return pipeline.toBuffer();
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
//# sourceMappingURL=StorageProxyService.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"StorageProxyService.js","sourceRoot":"","sources":["../../../src/shared/storage/StorageProxyService.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Storage Upload Service — server-side upload to Supabase Storage
|
|
3
|
-
*
|
|
4
|
-
* Centralizes all file uploads (images, PDFs, videos, any file type).
|
|
5
|
-
* Returns proxy URLs, never Supabase URLs.
|
|
6
|
-
*
|
|
7
|
-
* Three upload modes:
|
|
8
|
-
* 1. uploadFile() — single file upload, returns proxy URL
|
|
9
|
-
* 2. uploadMultiSize() — image upload with resize to multiple sizes (sharp)
|
|
10
|
-
* 3. uploadPrivate() — private bucket upload, returns signed URL
|
|
11
|
-
*
|
|
12
|
-
* Usage:
|
|
13
|
-
* ```typescript
|
|
14
|
-
* import { StorageUploadService } from '@soulbatical/tetra-core';
|
|
15
|
-
*
|
|
16
|
-
* const storage = new StorageUploadService(supabase, {
|
|
17
|
-
* allowedBuckets: ['images', 'pdf-books', 'community-media'],
|
|
18
|
-
* });
|
|
19
|
-
*
|
|
20
|
-
* // Public file
|
|
21
|
-
* const result = await storage.uploadFile('images', buffer, {
|
|
22
|
-
* path: 'ai-generated/org123/photo.jpg',
|
|
23
|
-
* contentType: 'image/jpeg',
|
|
24
|
-
* });
|
|
25
|
-
* // result.proxyUrl → "/api/public/storage/images/ai-generated/org123/photo.jpg"
|
|
26
|
-
*
|
|
27
|
-
* // Multi-size image
|
|
28
|
-
* const results = await storage.uploadMultiSize('images', buffer, {
|
|
29
|
-
* prefix: 'ai-generated/org123',
|
|
30
|
-
* baseFilename: 'photo.jpg',
|
|
31
|
-
* });
|
|
32
|
-
* // results[0].proxyUrl → ".../photo-thumb.jpg"
|
|
33
|
-
*
|
|
34
|
-
* // Private file (signed URL)
|
|
35
|
-
* const result = await storage.uploadPrivate('pdf-books', buffer, {
|
|
36
|
-
* path: 'books/user123/book.pdf',
|
|
37
|
-
* contentType: 'application/pdf',
|
|
38
|
-
* expiresIn: 3600 * 24 * 30, // 30 days
|
|
39
|
-
* });
|
|
40
|
-
* // result.signedUrl → "https://...supabase.co/storage/v1/sign/..."
|
|
41
|
-
* ```
|
|
42
|
-
*/
|
|
43
|
-
import type { SupabaseClient } from '@supabase/supabase-js';
|
|
44
|
-
import type { StorageConfig, UploadResult, ImageSize } from './types.js';
|
|
45
|
-
export interface FileUploadOptions {
|
|
46
|
-
/** Storage path within bucket (e.g. "ai-generated/org123/photo.jpg") */
|
|
47
|
-
path: string;
|
|
48
|
-
/** MIME type (e.g. "image/jpeg", "application/pdf") */
|
|
49
|
-
contentType: string;
|
|
50
|
-
/** Overwrite if exists (default: false) */
|
|
51
|
-
upsert?: boolean;
|
|
52
|
-
}
|
|
53
|
-
export interface MultiSizeUploadOptions {
|
|
54
|
-
/** Folder prefix (e.g. "ai-generated/org123") */
|
|
55
|
-
prefix: string;
|
|
56
|
-
/** Base filename (e.g. "photo.jpg") — sizes will be "{base}-{size}.{ext}" */
|
|
57
|
-
baseFilename: string;
|
|
58
|
-
/** Custom sizes (defaults to STANDARD_IMAGE_SIZES) */
|
|
59
|
-
sizes?: ImageSize[];
|
|
60
|
-
/** Image format (defaults to 'jpeg') */
|
|
61
|
-
format?: 'jpeg' | 'png' | 'webp';
|
|
62
|
-
}
|
|
63
|
-
export interface PrivateUploadOptions extends FileUploadOptions {
|
|
64
|
-
/** Signed URL expiry in seconds (default: 30 days) */
|
|
65
|
-
expiresIn?: number;
|
|
66
|
-
}
|
|
67
|
-
export interface PrivateUploadResult {
|
|
68
|
-
bucket: string;
|
|
69
|
-
path: string;
|
|
70
|
-
signedUrl: string;
|
|
71
|
-
contentType: string;
|
|
72
|
-
size: number;
|
|
73
|
-
}
|
|
74
|
-
export interface MultiSizeUploadResult {
|
|
75
|
-
/** All uploaded sizes with their proxy URLs */
|
|
76
|
-
sizes: Array<UploadResult & {
|
|
77
|
-
sizeName: string;
|
|
78
|
-
}>;
|
|
79
|
-
/** Base filename for DB storage */
|
|
80
|
-
baseFilename: string;
|
|
81
|
-
/** Storage prefix for DB storage */
|
|
82
|
-
storagePrefix: string;
|
|
83
|
-
}
|
|
84
|
-
export declare class StorageUploadService {
|
|
85
|
-
private readonly config;
|
|
86
|
-
private readonly supabase;
|
|
87
|
-
constructor(supabase: SupabaseClient, config: Partial<StorageConfig> & {
|
|
88
|
-
allowedBuckets: string[];
|
|
89
|
-
});
|
|
90
|
-
private validateBucket;
|
|
91
|
-
/**
|
|
92
|
-
* Upload a single file to a public bucket.
|
|
93
|
-
* Returns a proxy URL — never a Supabase URL.
|
|
94
|
-
*/
|
|
95
|
-
uploadFile(bucket: string, buffer: Buffer, options: FileUploadOptions): Promise<UploadResult>;
|
|
96
|
-
/**
|
|
97
|
-
* Upload an image in multiple sizes (thumb, medium, large, original).
|
|
98
|
-
* Requires sharp as a peer dependency.
|
|
99
|
-
* Returns proxy URLs for all sizes.
|
|
100
|
-
*/
|
|
101
|
-
uploadMultiSize(bucket: string, buffer: Buffer, options: MultiSizeUploadOptions): Promise<MultiSizeUploadResult>;
|
|
102
|
-
/**
|
|
103
|
-
* Upload a file to a private bucket.
|
|
104
|
-
* Returns a signed URL (for PDFs, protected documents, etc.)
|
|
105
|
-
*/
|
|
106
|
-
uploadPrivate(bucket: string, buffer: Buffer, options: PrivateUploadOptions): Promise<PrivateUploadResult>;
|
|
107
|
-
/**
|
|
108
|
-
* Delete a file from storage.
|
|
109
|
-
*/
|
|
110
|
-
deleteFile(bucket: string, path: string): Promise<void>;
|
|
111
|
-
/**
|
|
112
|
-
* Ensure a bucket exists (create if missing).
|
|
113
|
-
* Call once at startup if needed.
|
|
114
|
-
*/
|
|
115
|
-
ensureBucket(bucket: string, options?: {
|
|
116
|
-
public?: boolean;
|
|
117
|
-
fileSizeLimit?: number;
|
|
118
|
-
allowedMimeTypes?: string[];
|
|
119
|
-
}): Promise<void>;
|
|
120
|
-
/**
|
|
121
|
-
* Build a proxy URL for a given bucket and path.
|
|
122
|
-
* Use this when you already have the path and just need the URL.
|
|
123
|
-
*/
|
|
124
|
-
static buildProxyUrl(bucket: string, path: string): string;
|
|
125
|
-
}
|
|
126
|
-
//# sourceMappingURL=StorageUploadService.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"StorageUploadService.d.ts","sourceRoot":"","sources":["../../../src/shared/storage/StorageUploadService.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,SAAS,EAAwB,MAAM,YAAY,CAAC;AAK/F,MAAM,WAAW,iBAAiB;IAChC,wEAAwE;IACxE,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,WAAW,EAAE,MAAM,CAAC;IACpB,2CAA2C;IAC3C,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,sBAAsB;IACrC,iDAAiD;IACjD,MAAM,EAAE,MAAM,CAAC;IACf,6EAA6E;IAC7E,YAAY,EAAE,MAAM,CAAC;IACrB,sDAAsD;IACtD,KAAK,CAAC,EAAE,SAAS,EAAE,CAAC;IACpB,wCAAwC;IACxC,MAAM,CAAC,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;CAClC;AAED,MAAM,WAAW,oBAAqB,SAAQ,iBAAiB;IAC7D,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,qBAAqB;IACpC,+CAA+C;IAC/C,KAAK,EAAE,KAAK,CAAC,YAAY,GAAG;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAClD,mCAAmC;IACnC,YAAY,EAAE,MAAM,CAAC;IACrB,oCAAoC;IACpC,aAAa,EAAE,MAAM,CAAC;CACvB;AAOD,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;IACvC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiB;gBAE9B,QAAQ,EAAE,cAAc,EAAE,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG;QAAE,cAAc,EAAE,MAAM,EAAE,CAAA;KAAE;IAWnG,OAAO,CAAC,cAAc;IAMtB;;;OAGG;IACG,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,YAAY,CAAC;IAiCnG;;;;OAIG;IACG,eAAe,CACnB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,qBAAqB,CAAC;IAqCjC;;;OAGG;IACG,aAAa,CACjB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,mBAAmB,CAAC;IAyC/B;;OAEG;IACG,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAe7D;;;OAGG;IACG,YAAY,CAChB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,OAAO,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAC;QAAC,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAA;KAAO,GACtF,OAAO,CAAC,IAAI,CAAC;IAmBhB;;;OAGG;IACH,MAAM,CAAC,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM;CAG3D"}
|
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Storage Upload Service — server-side upload to Supabase Storage
|
|
3
|
-
*
|
|
4
|
-
* Centralizes all file uploads (images, PDFs, videos, any file type).
|
|
5
|
-
* Returns proxy URLs, never Supabase URLs.
|
|
6
|
-
*
|
|
7
|
-
* Three upload modes:
|
|
8
|
-
* 1. uploadFile() — single file upload, returns proxy URL
|
|
9
|
-
* 2. uploadMultiSize() — image upload with resize to multiple sizes (sharp)
|
|
10
|
-
* 3. uploadPrivate() — private bucket upload, returns signed URL
|
|
11
|
-
*
|
|
12
|
-
* Usage:
|
|
13
|
-
* ```typescript
|
|
14
|
-
* import { StorageUploadService } from '@soulbatical/tetra-core';
|
|
15
|
-
*
|
|
16
|
-
* const storage = new StorageUploadService(supabase, {
|
|
17
|
-
* allowedBuckets: ['images', 'pdf-books', 'community-media'],
|
|
18
|
-
* });
|
|
19
|
-
*
|
|
20
|
-
* // Public file
|
|
21
|
-
* const result = await storage.uploadFile('images', buffer, {
|
|
22
|
-
* path: 'ai-generated/org123/photo.jpg',
|
|
23
|
-
* contentType: 'image/jpeg',
|
|
24
|
-
* });
|
|
25
|
-
* // result.proxyUrl → "/api/public/storage/images/ai-generated/org123/photo.jpg"
|
|
26
|
-
*
|
|
27
|
-
* // Multi-size image
|
|
28
|
-
* const results = await storage.uploadMultiSize('images', buffer, {
|
|
29
|
-
* prefix: 'ai-generated/org123',
|
|
30
|
-
* baseFilename: 'photo.jpg',
|
|
31
|
-
* });
|
|
32
|
-
* // results[0].proxyUrl → ".../photo-thumb.jpg"
|
|
33
|
-
*
|
|
34
|
-
* // Private file (signed URL)
|
|
35
|
-
* const result = await storage.uploadPrivate('pdf-books', buffer, {
|
|
36
|
-
* path: 'books/user123/book.pdf',
|
|
37
|
-
* contentType: 'application/pdf',
|
|
38
|
-
* expiresIn: 3600 * 24 * 30, // 30 days
|
|
39
|
-
* });
|
|
40
|
-
* // result.signedUrl → "https://...supabase.co/storage/v1/sign/..."
|
|
41
|
-
* ```
|
|
42
|
-
*/
|
|
43
|
-
import { createLogger } from '../../utils/logger.js';
|
|
44
|
-
const logger = createLogger('storage:upload');
|
|
45
|
-
function buildProxyUrl(bucket, path) {
|
|
46
|
-
const clean = path.startsWith('/') ? path.substring(1) : path;
|
|
47
|
-
return `/api/public/storage/${bucket}/${clean}`;
|
|
48
|
-
}
|
|
49
|
-
export class StorageUploadService {
|
|
50
|
-
config;
|
|
51
|
-
supabase;
|
|
52
|
-
constructor(supabase, config) {
|
|
53
|
-
this.supabase = supabase;
|
|
54
|
-
this.config = {
|
|
55
|
-
allowedBuckets: config.allowedBuckets,
|
|
56
|
-
supabaseUrl: config.supabaseUrl || process.env.SUPABASE_URL,
|
|
57
|
-
supabaseServiceKey: config.supabaseServiceKey || process.env.SUPABASE_SERVICE_ROLE_KEY,
|
|
58
|
-
maxUploadBytes: config.maxUploadBytes ?? 50 * 1024 * 1024,
|
|
59
|
-
cacheMaxAge: config.cacheMaxAge ?? 3600,
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
validateBucket(bucket) {
|
|
63
|
-
if (!this.config.allowedBuckets.includes(bucket)) {
|
|
64
|
-
throw new Error(`Bucket "${bucket}" is not in the allowed list: [${this.config.allowedBuckets.join(', ')}]`);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* Upload a single file to a public bucket.
|
|
69
|
-
* Returns a proxy URL — never a Supabase URL.
|
|
70
|
-
*/
|
|
71
|
-
async uploadFile(bucket, buffer, options) {
|
|
72
|
-
this.validateBucket(bucket);
|
|
73
|
-
if (this.config.maxUploadBytes && buffer.length > this.config.maxUploadBytes) {
|
|
74
|
-
throw new Error(`File size ${buffer.length} exceeds limit ${this.config.maxUploadBytes}`);
|
|
75
|
-
}
|
|
76
|
-
logger.debug({ bucket, path: options.path, size: buffer.length }, 'Uploading file');
|
|
77
|
-
const { data, error } = await this.supabase.storage
|
|
78
|
-
.from(bucket)
|
|
79
|
-
.upload(options.path, buffer, {
|
|
80
|
-
contentType: options.contentType,
|
|
81
|
-
upsert: options.upsert ?? false,
|
|
82
|
-
});
|
|
83
|
-
if (error) {
|
|
84
|
-
logger.error({ error, bucket, path: options.path }, 'Upload failed');
|
|
85
|
-
throw new Error(`Upload failed: ${error.message}`);
|
|
86
|
-
}
|
|
87
|
-
const result = {
|
|
88
|
-
bucket,
|
|
89
|
-
path: data.path,
|
|
90
|
-
proxyUrl: buildProxyUrl(bucket, data.path),
|
|
91
|
-
contentType: options.contentType,
|
|
92
|
-
size: buffer.length,
|
|
93
|
-
};
|
|
94
|
-
logger.info({ bucket, path: data.path, size: buffer.length }, 'File uploaded');
|
|
95
|
-
return result;
|
|
96
|
-
}
|
|
97
|
-
/**
|
|
98
|
-
* Upload an image in multiple sizes (thumb, medium, large, original).
|
|
99
|
-
* Requires sharp as a peer dependency.
|
|
100
|
-
* Returns proxy URLs for all sizes.
|
|
101
|
-
*/
|
|
102
|
-
async uploadMultiSize(bucket, buffer, options) {
|
|
103
|
-
this.validateBucket(bucket);
|
|
104
|
-
// Dynamic import — sharp is optional peer dep
|
|
105
|
-
const { ImageProcessingService, STANDARD_IMAGE_SIZES } = await import('./ImageProcessingService.js');
|
|
106
|
-
const sizes = options.sizes || STANDARD_IMAGE_SIZES;
|
|
107
|
-
const processed = await ImageProcessingService.generateMultipleSizes(buffer, {
|
|
108
|
-
sizes,
|
|
109
|
-
format: options.format,
|
|
110
|
-
});
|
|
111
|
-
const ext = options.baseFilename.split('.').pop() || 'jpg';
|
|
112
|
-
const baseWithoutExt = options.baseFilename.replace(/\.[^.]+$/, '');
|
|
113
|
-
const uploadResults = await Promise.all(processed.map(async (item) => {
|
|
114
|
-
const filename = `${baseWithoutExt}-${item.size}.${ext}`;
|
|
115
|
-
const path = `${options.prefix}/${filename}`;
|
|
116
|
-
const result = await this.uploadFile(bucket, item.buffer, {
|
|
117
|
-
path,
|
|
118
|
-
contentType: `image/${options.format || 'jpeg'}`,
|
|
119
|
-
upsert: true,
|
|
120
|
-
});
|
|
121
|
-
return { ...result, sizeName: item.size };
|
|
122
|
-
}));
|
|
123
|
-
return {
|
|
124
|
-
sizes: uploadResults,
|
|
125
|
-
baseFilename: options.baseFilename,
|
|
126
|
-
storagePrefix: options.prefix,
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* Upload a file to a private bucket.
|
|
131
|
-
* Returns a signed URL (for PDFs, protected documents, etc.)
|
|
132
|
-
*/
|
|
133
|
-
async uploadPrivate(bucket, buffer, options) {
|
|
134
|
-
this.validateBucket(bucket);
|
|
135
|
-
if (this.config.maxUploadBytes && buffer.length > this.config.maxUploadBytes) {
|
|
136
|
-
throw new Error(`File size ${buffer.length} exceeds limit ${this.config.maxUploadBytes}`);
|
|
137
|
-
}
|
|
138
|
-
logger.debug({ bucket, path: options.path, size: buffer.length }, 'Uploading private file');
|
|
139
|
-
const { data, error } = await this.supabase.storage
|
|
140
|
-
.from(bucket)
|
|
141
|
-
.upload(options.path, buffer, {
|
|
142
|
-
contentType: options.contentType,
|
|
143
|
-
upsert: options.upsert ?? false,
|
|
144
|
-
});
|
|
145
|
-
if (error) {
|
|
146
|
-
logger.error({ error, bucket, path: options.path }, 'Private upload failed');
|
|
147
|
-
throw new Error(`Upload failed: ${error.message}`);
|
|
148
|
-
}
|
|
149
|
-
const expiresIn = options.expiresIn ?? 3600 * 24 * 30; // 30 days default
|
|
150
|
-
const { data: signedData, error: signError } = await this.supabase.storage
|
|
151
|
-
.from(bucket)
|
|
152
|
-
.createSignedUrl(data.path, expiresIn);
|
|
153
|
-
if (signError || !signedData?.signedUrl) {
|
|
154
|
-
throw new Error(`Failed to create signed URL: ${signError?.message || 'No URL returned'}`);
|
|
155
|
-
}
|
|
156
|
-
logger.info({ bucket, path: data.path, size: buffer.length }, 'Private file uploaded');
|
|
157
|
-
return {
|
|
158
|
-
bucket,
|
|
159
|
-
path: data.path,
|
|
160
|
-
signedUrl: signedData.signedUrl,
|
|
161
|
-
contentType: options.contentType,
|
|
162
|
-
size: buffer.length,
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
|
-
/**
|
|
166
|
-
* Delete a file from storage.
|
|
167
|
-
*/
|
|
168
|
-
async deleteFile(bucket, path) {
|
|
169
|
-
this.validateBucket(bucket);
|
|
170
|
-
const { error } = await this.supabase.storage
|
|
171
|
-
.from(bucket)
|
|
172
|
-
.remove([path]);
|
|
173
|
-
if (error) {
|
|
174
|
-
logger.error({ error, bucket, path }, 'Delete failed');
|
|
175
|
-
throw new Error(`Delete failed: ${error.message}`);
|
|
176
|
-
}
|
|
177
|
-
logger.info({ bucket, path }, 'File deleted');
|
|
178
|
-
}
|
|
179
|
-
/**
|
|
180
|
-
* Ensure a bucket exists (create if missing).
|
|
181
|
-
* Call once at startup if needed.
|
|
182
|
-
*/
|
|
183
|
-
async ensureBucket(bucket, options = {}) {
|
|
184
|
-
const { data: buckets } = await this.supabase.storage.listBuckets();
|
|
185
|
-
const exists = buckets?.some(b => b.name === bucket);
|
|
186
|
-
if (!exists) {
|
|
187
|
-
const { error } = await this.supabase.storage.createBucket(bucket, {
|
|
188
|
-
public: options.public ?? true,
|
|
189
|
-
fileSizeLimit: options.fileSizeLimit,
|
|
190
|
-
allowedMimeTypes: options.allowedMimeTypes,
|
|
191
|
-
});
|
|
192
|
-
if (error) {
|
|
193
|
-
throw new Error(`Failed to create bucket "${bucket}": ${error.message}`);
|
|
194
|
-
}
|
|
195
|
-
logger.info({ bucket }, 'Bucket created');
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
/**
|
|
199
|
-
* Build a proxy URL for a given bucket and path.
|
|
200
|
-
* Use this when you already have the path and just need the URL.
|
|
201
|
-
*/
|
|
202
|
-
static buildProxyUrl(bucket, path) {
|
|
203
|
-
return buildProxyUrl(bucket, path);
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
//# sourceMappingURL=StorageUploadService.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"StorageUploadService.js","sourceRoot":"","sources":["../../../src/shared/storage/StorageUploadService.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAIH,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAErD,MAAM,MAAM,GAAG,YAAY,CAAC,gBAAgB,CAAC,CAAC;AA4C9C,SAAS,aAAa,CAAC,MAAc,EAAE,IAAY;IACjD,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC9D,OAAO,uBAAuB,MAAM,IAAI,KAAK,EAAE,CAAC;AAClD,CAAC;AAED,MAAM,OAAO,oBAAoB;IACd,MAAM,CAAgB;IACtB,QAAQ,CAAiB;IAE1C,YAAY,QAAwB,EAAE,MAA6D;QACjG,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,MAAM,GAAG;YACZ,cAAc,EAAE,MAAM,CAAC,cAAc;YACrC,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY;YAC3D,kBAAkB,EAAE,MAAM,CAAC,kBAAkB,IAAI,OAAO,CAAC,GAAG,CAAC,yBAAyB;YACtF,cAAc,EAAE,MAAM,CAAC,cAAc,IAAI,EAAE,GAAG,IAAI,GAAG,IAAI;YACzD,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,IAAI;SACxC,CAAC;IACJ,CAAC;IAEO,cAAc,CAAC,MAAc;QACnC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YACjD,MAAM,IAAI,KAAK,CAAC,WAAW,MAAM,kCAAkC,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC/G,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,UAAU,CAAC,MAAc,EAAE,MAAc,EAAE,OAA0B;QACzE,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;QAE5B,IAAI,IAAI,CAAC,MAAM,CAAC,cAAc,IAAI,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;YAC7E,MAAM,IAAI,KAAK,CAAC,aAAa,MAAM,CAAC,MAAM,kBAAkB,IAAI,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC,CAAC;QAC5F,CAAC;QAED,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,EAAE,gBAAgB,CAAC,CAAC;QAEpF,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO;aAChD,IAAI,CAAC,MAAM,CAAC;aACZ,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE;YAC5B,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,KAAK;SAChC,CAAC,CAAC;QAEL,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,EAAE,eAAe,CAAC,CAAC;YACrE,MAAM,IAAI,KAAK,CAAC,kBAAkB,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QACrD,CAAC;QAED,MAAM,MAAM,GAAiB;YAC3B,MAAM;YACN,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,QAAQ,EAAE,aAAa,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC;YAC1C,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,IAAI,EAAE,MAAM,CAAC,MAAM;SACpB,CAAC;QAEF,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,EAAE,eAAe,CAAC,CAAC;QAC/E,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,eAAe,CACnB,MAAc,EACd,MAAc,EACd,OAA+B;QAE/B,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;QAE5B,8CAA8C;QAC9C,MAAM,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,GAAG,MAAM,MAAM,CAAC,6BAA6B,CAAC,CAAC;QACrG,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,oBAAoB,CAAC;QAEpD,MAAM,SAAS,GAAG,MAAM,sBAAsB,CAAC,qBAAqB,CAAC,MAAM,EAAE;YAC3E,KAAK;YACL,MAAM,EAAE,OAAO,CAAC,MAAM;SACvB,CAAC,CAAC;QAEH,MAAM,GAAG,GAAG,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,KAAK,CAAC;QAC3D,MAAM,cAAc,GAAG,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;QAEpE,MAAM,aAAa,GAAG,MAAM,OAAO,CAAC,GAAG,CACrC,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,IAA0B,EAAE,EAAE;YACjD,MAAM,QAAQ,GAAG,GAAG,cAAc,IAAI,IAAI,CAAC,IAAI,IAAI,GAAG,EAAE,CAAC;YACzD,MAAM,IAAI,GAAG,GAAG,OAAO,CAAC,MAAM,IAAI,QAAQ,EAAE,CAAC;YAE7C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE;gBACxD,IAAI;gBACJ,WAAW,EAAE,SAAS,OAAO,CAAC,MAAM,IAAI,MAAM,EAAE;gBAChD,MAAM,EAAE,IAAI;aACb,CAAC,CAAC;YAEH,OAAO,EAAE,GAAG,MAAM,EAAE,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5C,CAAC,CAAC,CACH,CAAC;QAEF,OAAO;YACL,KAAK,EAAE,aAAa;YACpB,YAAY,EAAE,OAAO,CAAC,YAAY;YAClC,aAAa,EAAE,OAAO,CAAC,MAAM;SAC9B,CAAC;IACJ,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,aAAa,CACjB,MAAc,EACd,MAAc,EACd,OAA6B;QAE7B,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;QAE5B,IAAI,IAAI,CAAC,MAAM,CAAC,cAAc,IAAI,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;YAC7E,MAAM,IAAI,KAAK,CAAC,aAAa,MAAM,CAAC,MAAM,kBAAkB,IAAI,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC,CAAC;QAC5F,CAAC;QAED,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,EAAE,wBAAwB,CAAC,CAAC;QAE5F,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO;aAChD,IAAI,CAAC,MAAM,CAAC;aACZ,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE;YAC5B,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,KAAK;SAChC,CAAC,CAAC;QAEL,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,EAAE,uBAAuB,CAAC,CAAC;YAC7E,MAAM,IAAI,KAAK,CAAC,kBAAkB,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QACrD,CAAC;QAED,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,IAAI,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,kBAAkB;QACzE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO;aACvE,IAAI,CAAC,MAAM,CAAC;aACZ,eAAe,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QAEzC,IAAI,SAAS,IAAI,CAAC,UAAU,EAAE,SAAS,EAAE,CAAC;YACxC,MAAM,IAAI,KAAK,CAAC,gCAAgC,SAAS,EAAE,OAAO,IAAI,iBAAiB,EAAE,CAAC,CAAC;QAC7F,CAAC;QAED,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,EAAE,uBAAuB,CAAC,CAAC;QAEvF,OAAO;YACL,MAAM;YACN,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,SAAS,EAAE,UAAU,CAAC,SAAS;YAC/B,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,IAAI,EAAE,MAAM,CAAC,MAAM;SACpB,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,UAAU,CAAC,MAAc,EAAE,IAAY;QAC3C,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;QAE5B,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO;aAC1C,IAAI,CAAC,MAAM,CAAC;aACZ,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QAElB,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,eAAe,CAAC,CAAC;YACvD,MAAM,IAAI,KAAK,CAAC,kBAAkB,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QACrD,CAAC;QAED,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,cAAc,CAAC,CAAC;IAChD,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,YAAY,CAChB,MAAc,EACd,UAAqF,EAAE;QAEvF,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;QACpE,MAAM,MAAM,GAAG,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC;QAErD,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,YAAY,CAAC,MAAM,EAAE;gBACjE,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,IAAI;gBAC9B,aAAa,EAAE,OAAO,CAAC,aAAa;gBACpC,gBAAgB,EAAE,OAAO,CAAC,gBAAgB;aAC3C,CAAC,CAAC;YAEH,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,IAAI,KAAK,CAAC,4BAA4B,MAAM,MAAM,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YAC3E,CAAC;YAED,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,gBAAgB,CAAC,CAAC;QAC5C,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,aAAa,CAAC,MAAc,EAAE,IAAY;QAC/C,OAAO,aAAa,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACrC,CAAC;CACF"}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Deterministic Storage URL Helpers
|
|
3
|
-
*
|
|
4
|
-
* All storage paths follow: {bucket}/{org_id}/{record_id}.{ext}
|
|
5
|
-
* URLs are derived from the ID — no need to store them in the database.
|
|
6
|
-
*
|
|
7
|
-
* Returns absolute Supabase Storage URLs.
|
|
8
|
-
* Falls back to relative proxy URLs when SUPABASE_URL is unavailable.
|
|
9
|
-
*/
|
|
10
|
-
export declare function getCreativeUrl(creativeId: string, orgId: string): string;
|
|
11
|
-
export declare function getCreativeStoragePath(creativeId: string, orgId: string): string;
|
|
12
|
-
export declare function getAdLibraryThumbnailUrl(itemId: string, orgId: string): string;
|
|
13
|
-
export declare function getAdLibraryThumbnailPath(itemId: string, orgId: string): string;
|
|
14
|
-
//# sourceMappingURL=creative-urls.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"creative-urls.d.ts","sourceRoot":"","sources":["../../../src/shared/storage/creative-urls.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAYH,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAExE;AAED,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhF;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAE9E;AAED,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAE/E"}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Deterministic Storage URL Helpers
|
|
3
|
-
*
|
|
4
|
-
* All storage paths follow: {bucket}/{org_id}/{record_id}.{ext}
|
|
5
|
-
* URLs are derived from the ID — no need to store them in the database.
|
|
6
|
-
*
|
|
7
|
-
* Returns absolute Supabase Storage URLs.
|
|
8
|
-
* Falls back to relative proxy URLs when SUPABASE_URL is unavailable.
|
|
9
|
-
*/
|
|
10
|
-
const getSupabaseUrl = () => process.env.SUPABASE_URL || process.env.NEXT_PUBLIC_SUPABASE_URL || '';
|
|
11
|
-
function storageUrl(bucket, path) {
|
|
12
|
-
const supabaseUrl = getSupabaseUrl();
|
|
13
|
-
if (supabaseUrl) {
|
|
14
|
-
return `${supabaseUrl}/storage/v1/object/public/${bucket}/${path}`;
|
|
15
|
-
}
|
|
16
|
-
return `/api/public/storage/${bucket}/${path}`;
|
|
17
|
-
}
|
|
18
|
-
export function getCreativeUrl(creativeId, orgId) {
|
|
19
|
-
return storageUrl('ad-creatives', `${orgId}/${creativeId}.png`);
|
|
20
|
-
}
|
|
21
|
-
export function getCreativeStoragePath(creativeId, orgId) {
|
|
22
|
-
return `${orgId}/${creativeId}.png`;
|
|
23
|
-
}
|
|
24
|
-
export function getAdLibraryThumbnailUrl(itemId, orgId) {
|
|
25
|
-
return storageUrl('ad-library', `${orgId}/${itemId}.jpg`);
|
|
26
|
-
}
|
|
27
|
-
export function getAdLibraryThumbnailPath(itemId, orgId) {
|
|
28
|
-
return `${orgId}/${itemId}.jpg`;
|
|
29
|
-
}
|
|
30
|
-
//# sourceMappingURL=creative-urls.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"creative-urls.js","sourceRoot":"","sources":["../../../src/shared/storage/creative-urls.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,cAAc,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,OAAO,CAAC,GAAG,CAAC,wBAAwB,IAAI,EAAE,CAAC;AAEpG,SAAS,UAAU,CAAC,MAAc,EAAE,IAAY;IAC9C,MAAM,WAAW,GAAG,cAAc,EAAE,CAAC;IACrC,IAAI,WAAW,EAAE,CAAC;QAChB,OAAO,GAAG,WAAW,6BAA6B,MAAM,IAAI,IAAI,EAAE,CAAC;IACrE,CAAC;IACD,OAAO,uBAAuB,MAAM,IAAI,IAAI,EAAE,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,UAAkB,EAAE,KAAa;IAC9D,OAAO,UAAU,CAAC,cAAc,EAAE,GAAG,KAAK,IAAI,UAAU,MAAM,CAAC,CAAC;AAClE,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,UAAkB,EAAE,KAAa;IACtE,OAAO,GAAG,KAAK,IAAI,UAAU,MAAM,CAAC;AACtC,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,MAAc,EAAE,KAAa;IACpE,OAAO,UAAU,CAAC,YAAY,EAAE,GAAG,KAAK,IAAI,MAAM,MAAM,CAAC,CAAC;AAC5D,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,MAAc,EAAE,KAAa;IACrE,OAAO,GAAG,KAAK,IAAI,MAAM,MAAM,CAAC;AAClC,CAAC"}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module @soulbatical/tetra-core/storage
|
|
3
|
-
*
|
|
4
|
-
* Shared storage module — Supabase streaming proxy, upload, and image processing.
|
|
5
|
-
*
|
|
6
|
-
* Usage:
|
|
7
|
-
* ```typescript
|
|
8
|
-
* import {
|
|
9
|
-
* addStorageProxyRoutes,
|
|
10
|
-
* addStorageUploadRoutes,
|
|
11
|
-
* ImageProcessingService,
|
|
12
|
-
* } from '@soulbatical/tetra-core';
|
|
13
|
-
*
|
|
14
|
-
* const publicRouter = Router();
|
|
15
|
-
* addStorageProxyRoutes(publicRouter, {
|
|
16
|
-
* config: { allowedBuckets: ['ad-creatives'] },
|
|
17
|
-
* });
|
|
18
|
-
* app.use('/api/public/storage', publicRouter);
|
|
19
|
-
* ```
|
|
20
|
-
*/
|
|
21
|
-
export { addStorageProxyRoutes, addStorageUploadRoutes } from './routes.js';
|
|
22
|
-
export { StorageProxyService } from './StorageProxyService.js';
|
|
23
|
-
export { ImageProcessingService, STANDARD_IMAGE_SIZES } from './ImageProcessingService.js';
|
|
24
|
-
export { StorageUploadService } from './StorageUploadService.js';
|
|
25
|
-
export type { StorageConfig, StorageProxyOptions, StorageUploadOptions, UploadResult, ImageSize, ProcessedImageResult, ImageProcessingOptions, } from './types.js';
|
|
26
|
-
export type { FileUploadOptions, MultiSizeUploadOptions, PrivateUploadOptions, PrivateUploadResult, MultiSizeUploadResult, } from './StorageUploadService.js';
|
|
27
|
-
export { getCreativeUrl, getCreativeStoragePath, getAdLibraryThumbnailUrl, getAdLibraryThumbnailPath } from './creative-urls.js';
|
|
28
|
-
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/shared/storage/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAC5E,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAC3F,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACjE,YAAY,EACV,aAAa,EACb,mBAAmB,EACnB,oBAAoB,EACpB,YAAY,EACZ,SAAS,EACT,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,iBAAiB,EACjB,sBAAsB,EACtB,oBAAoB,EACpB,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAAE,wBAAwB,EAAE,yBAAyB,EAAE,MAAM,oBAAoB,CAAC"}
|