@soulbatical/tetra-core 0.1.48 → 0.1.50

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.
@@ -0,0 +1,43 @@
1
+ /**
2
+ * StorageDropzone — drag & drop upload component
3
+ *
4
+ * Ready-to-use upload zone with drag & drop, file validation,
5
+ * progress indicator, and preview. Framework-agnostic styling via className props.
6
+ *
7
+ * Usage:
8
+ * ```tsx
9
+ * import { StorageDropzone } from '@soulbatical/tetra-core/frontend';
10
+ *
11
+ * <StorageDropzone
12
+ * apiBaseUrl={process.env.NEXT_PUBLIC_API_URL}
13
+ * bucket="images"
14
+ * acceptedTypes={['image/jpeg', 'image/png']}
15
+ * maxSizeBytes={10 * 1024 * 1024}
16
+ * onComplete={(result) => setImageUrl(result.proxyUrl)}
17
+ * />
18
+ * ```
19
+ */
20
+ import React from 'react';
21
+ import { type UseStorageUploadOptions, type UploadResult } from './useStorageUpload.js';
22
+ export interface StorageDropzoneProps extends UseStorageUploadOptions {
23
+ /** Allow multiple file selection */
24
+ multiple?: boolean;
25
+ /** Custom label text */
26
+ label?: string;
27
+ /** Sublabel text (e.g. "Max 10MB, JPEG or PNG") */
28
+ sublabel?: string;
29
+ /** CSS class for outer container */
30
+ className?: string;
31
+ /** CSS class when dragging over */
32
+ dragActiveClassName?: string;
33
+ /** CSS class when uploading */
34
+ uploadingClassName?: string;
35
+ /** Render custom content inside the dropzone */
36
+ children?: React.ReactNode;
37
+ /** Render the result after upload (e.g. preview image) */
38
+ renderResult?: (result: UploadResult) => React.ReactNode;
39
+ /** Disable the dropzone */
40
+ disabled?: boolean;
41
+ }
42
+ export declare function StorageDropzone({ multiple, label, sublabel, className, dragActiveClassName, uploadingClassName, children, renderResult, disabled, ...uploadOptions }: StorageDropzoneProps): import("react/jsx-runtime").JSX.Element;
43
+ //# sourceMappingURL=StorageDropzone.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StorageDropzone.d.ts","sourceRoot":"","sources":["../../../src/frontend/storage/StorageDropzone.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAwC,MAAM,OAAO,CAAC;AAC7D,OAAO,EAAoB,KAAK,uBAAuB,EAAE,KAAK,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1G,MAAM,WAAW,oBAAqB,SAAQ,uBAAuB;IACnE,oCAAoC;IACpC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,wBAAwB;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oCAAoC;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mCAAmC;IACnC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,+BAA+B;IAC/B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,gDAAgD;IAChD,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,0DAA0D;IAC1D,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,KAAK,CAAC,SAAS,CAAC;IACzD,2BAA2B;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,wBAAgB,eAAe,CAAC,EAC9B,QAAgB,EAChB,KAAK,EACL,QAAQ,EACR,SAAc,EACd,mBAAwB,EACxB,kBAAuB,EACvB,QAAQ,EACR,YAAY,EACZ,QAAgB,EAChB,GAAG,aAAa,EACjB,EAAE,oBAAoB,2CAwItB"}
@@ -0,0 +1,98 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ /**
3
+ * StorageDropzone — drag & drop upload component
4
+ *
5
+ * Ready-to-use upload zone with drag & drop, file validation,
6
+ * progress indicator, and preview. Framework-agnostic styling via className props.
7
+ *
8
+ * Usage:
9
+ * ```tsx
10
+ * import { StorageDropzone } from '@soulbatical/tetra-core/frontend';
11
+ *
12
+ * <StorageDropzone
13
+ * apiBaseUrl={process.env.NEXT_PUBLIC_API_URL}
14
+ * bucket="images"
15
+ * acceptedTypes={['image/jpeg', 'image/png']}
16
+ * maxSizeBytes={10 * 1024 * 1024}
17
+ * onComplete={(result) => setImageUrl(result.proxyUrl)}
18
+ * />
19
+ * ```
20
+ */
21
+ import { useState, useCallback, useRef } from 'react';
22
+ import { useStorageUpload } from './useStorageUpload.js';
23
+ export function StorageDropzone({ multiple = false, label, sublabel, className = '', dragActiveClassName = '', uploadingClassName = '', children, renderResult, disabled = false, ...uploadOptions }) {
24
+ const { upload, uploadMultiple, uploading, progress, error, result, validate } = useStorageUpload(uploadOptions);
25
+ const [isDragActive, setIsDragActive] = useState(false);
26
+ const inputRef = useRef(null);
27
+ const handleFiles = useCallback(async (files) => {
28
+ const fileArray = Array.from(files);
29
+ if (fileArray.length === 0)
30
+ return;
31
+ if (multiple) {
32
+ await uploadMultiple(fileArray);
33
+ }
34
+ else {
35
+ await upload(fileArray[0]);
36
+ }
37
+ }, [upload, uploadMultiple, multiple]);
38
+ const handleDragOver = useCallback((e) => {
39
+ e.preventDefault();
40
+ e.stopPropagation();
41
+ if (!disabled && !uploading)
42
+ setIsDragActive(true);
43
+ }, [disabled, uploading]);
44
+ const handleDragLeave = useCallback((e) => {
45
+ e.preventDefault();
46
+ e.stopPropagation();
47
+ setIsDragActive(false);
48
+ }, []);
49
+ const handleDrop = useCallback((e) => {
50
+ e.preventDefault();
51
+ e.stopPropagation();
52
+ setIsDragActive(false);
53
+ if (!disabled && !uploading && e.dataTransfer.files.length > 0) {
54
+ handleFiles(e.dataTransfer.files);
55
+ }
56
+ }, [disabled, uploading, handleFiles]);
57
+ const handleClick = useCallback(() => {
58
+ if (!disabled && !uploading) {
59
+ inputRef.current?.click();
60
+ }
61
+ }, [disabled, uploading]);
62
+ const handleInputChange = useCallback((e) => {
63
+ if (e.target.files && e.target.files.length > 0) {
64
+ handleFiles(e.target.files);
65
+ e.target.value = ''; // Reset so same file can be re-selected
66
+ }
67
+ }, [handleFiles]);
68
+ const acceptStr = uploadOptions.acceptedTypes?.join(',') || undefined;
69
+ // Build maxSize label
70
+ const maxSizeLabel = uploadOptions.maxSizeBytes
71
+ ? `${(uploadOptions.maxSizeBytes / 1024 / 1024).toFixed(0)}MB`
72
+ : null;
73
+ const defaultSublabel = [
74
+ maxSizeLabel && `Max ${maxSizeLabel}`,
75
+ uploadOptions.acceptedTypes && uploadOptions.acceptedTypes.map(t => t.split('/')[1]?.toUpperCase()).join(', '),
76
+ ].filter(Boolean).join(' · ') || undefined;
77
+ const containerClass = [
78
+ className,
79
+ isDragActive && dragActiveClassName,
80
+ uploading && uploadingClassName,
81
+ ].filter(Boolean).join(' ');
82
+ return (_jsxs("div", { role: "button", tabIndex: 0, onClick: handleClick, onKeyDown: (e) => e.key === 'Enter' && handleClick(), onDragOver: handleDragOver, onDragLeave: handleDragLeave, onDrop: handleDrop, className: containerClass, style: !className ? {
83
+ border: `2px dashed ${isDragActive ? '#3b82f6' : '#d1d5db'}`,
84
+ borderRadius: '8px',
85
+ padding: '2rem',
86
+ textAlign: 'center',
87
+ cursor: disabled || uploading ? 'default' : 'pointer',
88
+ opacity: disabled ? 0.5 : 1,
89
+ transition: 'border-color 0.2s, background-color 0.2s',
90
+ backgroundColor: isDragActive ? '#eff6ff' : 'transparent',
91
+ } : undefined, children: [_jsx("input", { ref: inputRef, type: "file", accept: acceptStr, multiple: multiple, onChange: handleInputChange, style: { display: 'none' } }), children || (_jsx(_Fragment, { children: uploading ? (_jsxs("div", { children: [_jsxs("div", { style: { marginBottom: '0.5rem' }, children: ["Uploading... ", progress, "%"] }), _jsx("div", { style: {
92
+ width: '100%', height: '4px', backgroundColor: '#e5e7eb', borderRadius: '2px',
93
+ }, children: _jsx("div", { style: {
94
+ width: `${progress}%`, height: '100%', backgroundColor: '#3b82f6',
95
+ borderRadius: '2px', transition: 'width 0.3s',
96
+ } }) })] })) : result && renderResult ? (renderResult(result)) : (_jsxs(_Fragment, { children: [_jsx("div", { style: { marginBottom: '0.25rem' }, children: label || (isDragActive ? 'Drop files here' : 'Click or drag files to upload') }), (sublabel || defaultSublabel) && (_jsx("div", { style: { fontSize: '0.875rem', color: '#6b7280' }, children: sublabel || defaultSublabel }))] })) })), error && (_jsx("div", { style: { color: '#ef4444', fontSize: '0.875rem', marginTop: '0.5rem' }, children: error }))] }));
97
+ }
98
+ //# sourceMappingURL=StorageDropzone.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StorageDropzone.js","sourceRoot":"","sources":["../../../src/frontend/storage/StorageDropzone.tsx"],"names":[],"mappings":"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"}
@@ -1,5 +1,7 @@
1
1
  export { configureStorageUrls } from './storageUrl.js';
2
- export type { StorageUrlHelpers, PhotoSize, Photo } from './storageUrl.js';
2
+ export type { StorageUrlHelpers, PhotoSize, Photo, ImageTransformParams } from './storageUrl.js';
3
3
  export { useStorageUpload } from './useStorageUpload.js';
4
4
  export type { UseStorageUploadOptions, UseStorageUploadReturn, UploadResult } from './useStorageUpload.js';
5
+ export { StorageDropzone } from './StorageDropzone.js';
6
+ export type { StorageDropzoneProps } from './StorageDropzone.js';
5
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/frontend/storage/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AACvD,YAAY,EAAE,iBAAiB,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAC3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,YAAY,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/frontend/storage/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AACvD,YAAY,EAAE,iBAAiB,EAAE,SAAS,EAAE,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AACjG,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,YAAY,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC3G,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,YAAY,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC"}
@@ -1,3 +1,4 @@
1
1
  export { configureStorageUrls } from './storageUrl.js';
2
2
  export { useStorageUpload } from './useStorageUpload.js';
3
+ export { StorageDropzone } from './StorageDropzone.js';
3
4
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/frontend/storage/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAEvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/frontend/storage/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAEvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEzD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC"}
@@ -1,13 +1,27 @@
1
1
  /**
2
2
  * Storage URL Builders — construct proxy URLs for stored images
3
3
  *
4
+ * Supports on-the-fly image transforms via query parameters:
5
+ * ?w=400 — resize to max width
6
+ * ?h=300 — resize to max height
7
+ * ?q=80 — quality 1-100
8
+ * ?f=webp — output format
9
+ *
4
10
  * Usage:
5
11
  * ```typescript
6
12
  * import { configureStorageUrls } from '@soulbatical/tetra-core/frontend';
7
13
  *
8
14
  * const storage = configureStorageUrls('https://api.myapp.com');
9
- * storage.buildStorageUrl('ad-creatives', orgId, 'abc.png');
10
- * // "https://api.myapp.com/api/public/storage/ad-creatives/{orgId}/abc.png"
15
+ *
16
+ * // Original size
17
+ * storage.buildStorageUrl('images', orgId, 'photo.png');
18
+ *
19
+ * // Thumbnail (400px wide, webp)
20
+ * storage.buildStorageUrl('images', orgId, 'photo.png', { w: 400, f: 'webp' });
21
+ *
22
+ * // Responsive srcset with on-the-fly resize
23
+ * storage.buildImageSrcSet('images', orgId, 'photo.png');
24
+ * // → ".../photo.png?w=150&f=webp 150w, .../photo.png?w=600&f=webp 600w, ..."
11
25
  * ```
12
26
  */
13
27
  export type PhotoSize = 'thumb' | 'medium' | 'large' | 'original';
@@ -16,13 +30,25 @@ export interface Photo {
16
30
  storage_prefix?: string;
17
31
  storage_path?: string;
18
32
  }
33
+ export interface ImageTransformParams {
34
+ /** Max width in pixels */
35
+ w?: number;
36
+ /** Max height in pixels */
37
+ h?: number;
38
+ /** Quality 1-100 (default: 80) */
39
+ q?: number;
40
+ /** Output format */
41
+ f?: 'webp' | 'jpeg' | 'png';
42
+ }
19
43
  export interface StorageUrlHelpers {
20
- /** Build a proxy URL for any stored file */
21
- buildStorageUrl(bucket: string, orgId: string, fileId: string): string;
44
+ /** Build a proxy URL for any stored file, with optional image transforms */
45
+ buildStorageUrl(bucket: string, orgId: string, fileId: string, transform?: ImageTransformParams): string;
22
46
  /** Build a photo URL for a specific size (expects {size}_{filename} convention) */
23
47
  buildPhotoUrl(bucket: string, photo: Photo, size: PhotoSize): string;
24
- /** Build a srcSet string for responsive images */
48
+ /** Build a srcSet string for multi-size photos (pre-generated sizes) */
25
49
  buildPhotoSrcSet(bucket: string, photo: Photo): string;
50
+ /** Build a srcSet string using on-the-fly resize (no pre-generated sizes needed) */
51
+ buildImageSrcSet(bucket: string, orgId: string, fileId: string, widths?: number[]): string;
26
52
  }
27
53
  /**
28
54
  * Configure storage URL helpers with a base API URL.
@@ -1 +1 @@
1
- {"version":3,"file":"storageUrl.d.ts","sourceRoot":"","sources":["../../../src/frontend/storage/storageUrl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,UAAU,CAAC;AAElE,MAAM,WAAW,KAAK;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,4CAA4C;IAC5C,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IACvE,mFAAmF;IACnF,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,GAAG,MAAM,CAAC;IACrE,kDAAkD;IAClD,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,MAAM,CAAC;CACxD;AASD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,iBAAiB,CA4B1E"}
1
+ {"version":3,"file":"storageUrl.d.ts","sourceRoot":"","sources":["../../../src/frontend/storage/storageUrl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,UAAU,CAAC;AAElE,MAAM,WAAW,KAAK;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,oBAAoB;IACnC,0BAA0B;IAC1B,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,2BAA2B;IAC3B,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,kCAAkC;IAClC,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,oBAAoB;IACpB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;CAC7B;AAED,MAAM,WAAW,iBAAiB;IAChC,4EAA4E;IAC5E,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,oBAAoB,GAAG,MAAM,CAAC;IACzG,mFAAmF;IACnF,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,GAAG,MAAM,CAAC;IACrE,wEAAwE;IACxE,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,MAAM,CAAC;IACvD,oFAAoF;IACpF,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;CAC5F;AAsBD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,iBAAiB,CAiC1E"}
@@ -1,13 +1,27 @@
1
1
  /**
2
2
  * Storage URL Builders — construct proxy URLs for stored images
3
3
  *
4
+ * Supports on-the-fly image transforms via query parameters:
5
+ * ?w=400 — resize to max width
6
+ * ?h=300 — resize to max height
7
+ * ?q=80 — quality 1-100
8
+ * ?f=webp — output format
9
+ *
4
10
  * Usage:
5
11
  * ```typescript
6
12
  * import { configureStorageUrls } from '@soulbatical/tetra-core/frontend';
7
13
  *
8
14
  * const storage = configureStorageUrls('https://api.myapp.com');
9
- * storage.buildStorageUrl('ad-creatives', orgId, 'abc.png');
10
- * // "https://api.myapp.com/api/public/storage/ad-creatives/{orgId}/abc.png"
15
+ *
16
+ * // Original size
17
+ * storage.buildStorageUrl('images', orgId, 'photo.png');
18
+ *
19
+ * // Thumbnail (400px wide, webp)
20
+ * storage.buildStorageUrl('images', orgId, 'photo.png', { w: 400, f: 'webp' });
21
+ *
22
+ * // Responsive srcset with on-the-fly resize
23
+ * storage.buildImageSrcSet('images', orgId, 'photo.png');
24
+ * // → ".../photo.png?w=150&f=webp 150w, .../photo.png?w=600&f=webp 600w, ..."
11
25
  * ```
12
26
  */
13
27
  const SIZE_WIDTHS = {
@@ -16,18 +30,33 @@ const SIZE_WIDTHS = {
16
30
  large: 1200,
17
31
  original: 2000,
18
32
  };
33
+ const DEFAULT_SRCSET_WIDTHS = [150, 400, 800, 1200, 2000];
34
+ function buildTransformQuery(transform) {
35
+ if (!transform)
36
+ return '';
37
+ const params = new URLSearchParams();
38
+ if (transform.w)
39
+ params.set('w', String(transform.w));
40
+ if (transform.h)
41
+ params.set('h', String(transform.h));
42
+ if (transform.q)
43
+ params.set('q', String(transform.q));
44
+ if (transform.f)
45
+ params.set('f', transform.f);
46
+ const str = params.toString();
47
+ return str ? `?${str}` : '';
48
+ }
19
49
  /**
20
50
  * Configure storage URL helpers with a base API URL.
21
51
  * Call once at app startup, use the returned helpers everywhere.
22
52
  */
23
53
  export function configureStorageUrls(apiBaseUrl) {
24
54
  const base = apiBaseUrl.replace(/\/$/, '');
25
- function buildStorageUrl(bucket, orgId, fileId) {
26
- return `${base}/api/public/storage/${bucket}/${orgId}/${fileId}`;
55
+ function buildStorageUrl(bucket, orgId, fileId, transform) {
56
+ return `${base}/api/public/storage/${bucket}/${orgId}/${fileId}${buildTransformQuery(transform)}`;
27
57
  }
28
58
  function buildPhotoUrl(bucket, photo, size) {
29
59
  if (photo.storage_path) {
30
- // Full path provided — use directly
31
60
  return `${base}/api/public/storage/${bucket}/${photo.storage_path}`;
32
61
  }
33
62
  const prefix = photo.storage_prefix || '';
@@ -41,6 +70,11 @@ export function configureStorageUrls(apiBaseUrl) {
41
70
  .map((size) => `${buildPhotoUrl(bucket, photo, size)} ${SIZE_WIDTHS[size]}w`)
42
71
  .join(', ');
43
72
  }
44
- return { buildStorageUrl, buildPhotoUrl, buildPhotoSrcSet };
73
+ function buildImageSrcSet(bucket, orgId, fileId, widths) {
74
+ return (widths || DEFAULT_SRCSET_WIDTHS)
75
+ .map((w) => `${buildStorageUrl(bucket, orgId, fileId, { w, f: 'webp', q: 80 })} ${w}w`)
76
+ .join(', ');
77
+ }
78
+ return { buildStorageUrl, buildPhotoUrl, buildPhotoSrcSet, buildImageSrcSet };
45
79
  }
46
80
  //# sourceMappingURL=storageUrl.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"storageUrl.js","sourceRoot":"","sources":["../../../src/frontend/storage/storageUrl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAmBH,MAAM,WAAW,GAA8B;IAC7C,KAAK,EAAE,GAAG;IACV,MAAM,EAAE,GAAG;IACX,KAAK,EAAE,IAAI;IACX,QAAQ,EAAE,IAAI;CACf,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,UAAkB;IACrD,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAE3C,SAAS,eAAe,CAAC,MAAc,EAAE,KAAa,EAAE,MAAc;QACpE,OAAO,GAAG,IAAI,uBAAuB,MAAM,IAAI,KAAK,IAAI,MAAM,EAAE,CAAC;IACnE,CAAC;IAED,SAAS,aAAa,CAAC,MAAc,EAAE,KAAY,EAAE,IAAe;QAClE,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC;YACvB,oCAAoC;YACpC,OAAO,GAAG,IAAI,uBAAuB,MAAM,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC;QACtE,CAAC;QAED,MAAM,MAAM,GAAG,KAAK,CAAC,cAAc,IAAI,EAAE,CAAC;QAC1C,MAAM,QAAQ,GAAG,KAAK,CAAC,aAAa,IAAI,EAAE,CAAC;QAC3C,MAAM,aAAa,GAAG,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,QAAQ,EAAE,CAAC;QAC7E,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,aAAa,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC;QAEnE,OAAO,GAAG,IAAI,uBAAuB,MAAM,IAAI,IAAI,EAAE,CAAC;IACxD,CAAC;IAED,SAAS,gBAAgB,CAAC,MAAc,EAAE,KAAY;QACpD,OAAQ,MAAM,CAAC,IAAI,CAAC,WAAW,CAAiB;aAC7C,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,aAAa,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,IAAI,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC;aAC5E,IAAI,CAAC,IAAI,CAAC,CAAC;IAChB,CAAC;IAED,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,gBAAgB,EAAE,CAAC;AAC9D,CAAC"}
1
+ {"version":3,"file":"storageUrl.js","sourceRoot":"","sources":["../../../src/frontend/storage/storageUrl.ts"],"names":[],"mappings":"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"}
@@ -1,20 +1,19 @@
1
1
  /**
2
- * React hook for uploading files to storage
2
+ * React hook for uploading files to storage — with progress, validation, and abort
3
+ *
4
+ * Inspired by UploadThing's API, adapted for Tetra's proxy-based storage.
3
5
  *
4
6
  * Usage:
5
7
  * ```typescript
6
- * import { useStorageUpload } from '@soulbatical/tetra-core/frontend';
7
- *
8
- * const { upload, uploading, error, result } = useStorageUpload({
8
+ * const { upload, uploading, progress, error, result } = useStorageUpload({
9
9
  * apiBaseUrl: process.env.NEXT_PUBLIC_API_URL,
10
- * bucket: 'ad-creatives',
11
- * orgId: user.activeOrganizationId,
10
+ * bucket: 'images',
11
+ * onProgress: (p) => console.log(`${p}%`),
12
+ * onComplete: (result) => console.log(result.proxyUrl),
13
+ * onError: (err) => alert(err),
14
+ * maxSizeBytes: 10 * 1024 * 1024,
15
+ * acceptedTypes: ['image/jpeg', 'image/png', 'image/webp'],
12
16
  * });
13
- *
14
- * const handleFile = async (file: File) => {
15
- * const uploaded = await upload(file);
16
- * console.log(uploaded.proxyUrl);
17
- * };
18
17
  * ```
19
18
  */
20
19
  export interface UploadResult {
@@ -23,19 +22,53 @@ export interface UploadResult {
23
22
  proxyUrl: string;
24
23
  contentType: string;
25
24
  size: number;
25
+ /** Multi-size data (if generateMultipleSizes was requested) */
26
+ base_filename?: string;
27
+ storage_prefix?: string;
28
+ sizes?: Record<string, {
29
+ path: string;
30
+ publicUrl: string;
31
+ }>;
26
32
  }
27
33
  export interface UseStorageUploadOptions {
34
+ /** API base URL (e.g. https://api.myapp.com) */
28
35
  apiBaseUrl: string;
36
+ /** Target bucket name */
29
37
  bucket: string;
30
- orgId: string;
38
+ /** Organization ID (for path scoping) */
39
+ orgId?: string;
31
40
  /** Upload endpoint path — defaults to '/api/admin/storage/upload' */
32
41
  uploadPath?: string;
42
+ /** Generate multiple image sizes (thumb, medium, large, original) */
43
+ generateMultipleSizes?: boolean;
44
+ /** Max file size in bytes (validated client-side before upload) */
45
+ maxSizeBytes?: number;
46
+ /** Accepted MIME types (validated client-side) */
47
+ acceptedTypes?: string[];
48
+ /** Storage folder/path prefix */
49
+ folder?: string;
50
+ onProgress?: (percent: number) => void;
51
+ onComplete?: (result: UploadResult) => void;
52
+ onError?: (error: string) => void;
53
+ onBeforeUpload?: (file: File) => boolean | File | void;
33
54
  }
34
55
  export interface UseStorageUploadReturn {
56
+ /** Upload a single file */
35
57
  upload: (file: File) => Promise<UploadResult>;
58
+ /** Upload multiple files sequentially */
59
+ uploadMultiple: (files: File[]) => Promise<UploadResult[]>;
60
+ /** Whether an upload is in progress */
36
61
  uploading: boolean;
62
+ /** Upload progress 0-100 */
63
+ progress: number;
64
+ /** Last error message */
37
65
  error: string | null;
66
+ /** Last upload result */
38
67
  result: UploadResult | null;
68
+ /** Abort current upload */
69
+ abort: () => void;
70
+ /** Validate a file without uploading */
71
+ validate: (file: File) => string | null;
39
72
  }
40
73
  export declare function useStorageUpload(options: UseStorageUploadOptions): UseStorageUploadReturn;
41
74
  //# sourceMappingURL=useStorageUpload.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useStorageUpload.d.ts","sourceRoot":"","sources":["../../../src/frontend/storage/useStorageUpload.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,qEAAqE;IACrE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;IAC9C,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,YAAY,GAAG,IAAI,CAAC;CAC7B;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,sBAAsB,CAyCzF"}
1
+ {"version":3,"file":"useStorageUpload.d.ts","sourceRoot":"","sources":["../../../src/frontend/storage/useStorageUpload.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,+DAA+D;IAC/D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC7D;AAED,MAAM,WAAW,uBAAuB;IACtC,gDAAgD;IAChD,UAAU,EAAE,MAAM,CAAC;IACnB,yBAAyB;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,yCAAyC;IACzC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qEAAqE;IACrE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qEAAqE;IACrE,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,mEAAmE;IACnE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kDAAkD;IAClD,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,iCAAiC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAGhB,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,CAAC;IAC5C,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,GAAG,IAAI,GAAG,IAAI,CAAC;CACxD;AAED,MAAM,WAAW,sBAAsB;IACrC,2BAA2B;IAC3B,MAAM,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;IAC9C,yCAAyC;IACzC,cAAc,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;IAC3D,uCAAuC;IACvC,SAAS,EAAE,OAAO,CAAC;IACnB,4BAA4B;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,yBAAyB;IACzB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,yBAAyB;IACzB,MAAM,EAAE,YAAY,GAAG,IAAI,CAAC;IAC5B,2BAA2B;IAC3B,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,wCAAwC;IACxC,QAAQ,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,MAAM,GAAG,IAAI,CAAC;CACzC;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,sBAAsB,CAqKzF"}
@@ -1,58 +1,164 @@
1
1
  /**
2
- * React hook for uploading files to storage
2
+ * React hook for uploading files to storage — with progress, validation, and abort
3
+ *
4
+ * Inspired by UploadThing's API, adapted for Tetra's proxy-based storage.
3
5
  *
4
6
  * Usage:
5
7
  * ```typescript
6
- * import { useStorageUpload } from '@soulbatical/tetra-core/frontend';
7
- *
8
- * const { upload, uploading, error, result } = useStorageUpload({
8
+ * const { upload, uploading, progress, error, result } = useStorageUpload({
9
9
  * apiBaseUrl: process.env.NEXT_PUBLIC_API_URL,
10
- * bucket: 'ad-creatives',
11
- * orgId: user.activeOrganizationId,
10
+ * bucket: 'images',
11
+ * onProgress: (p) => console.log(`${p}%`),
12
+ * onComplete: (result) => console.log(result.proxyUrl),
13
+ * onError: (err) => alert(err),
14
+ * maxSizeBytes: 10 * 1024 * 1024,
15
+ * acceptedTypes: ['image/jpeg', 'image/png', 'image/webp'],
12
16
  * });
13
- *
14
- * const handleFile = async (file: File) => {
15
- * const uploaded = await upload(file);
16
- * console.log(uploaded.proxyUrl);
17
- * };
18
17
  * ```
19
18
  */
20
- import { useState, useCallback } from 'react';
19
+ import { useState, useCallback, useRef } from 'react';
21
20
  export function useStorageUpload(options) {
22
- const { apiBaseUrl, bucket, orgId, uploadPath = '/api/admin/storage/upload' } = options;
21
+ const { apiBaseUrl, bucket, orgId, uploadPath = '/api/admin/storage/upload', generateMultipleSizes = false, maxSizeBytes, acceptedTypes, folder, onProgress, onComplete, onError, onBeforeUpload, } = options;
23
22
  const [uploading, setUploading] = useState(false);
23
+ const [progress, setProgress] = useState(0);
24
24
  const [error, setError] = useState(null);
25
25
  const [result, setResult] = useState(null);
26
+ const abortRef = useRef(null);
27
+ const validate = useCallback((file) => {
28
+ if (maxSizeBytes && file.size > maxSizeBytes) {
29
+ const maxMB = (maxSizeBytes / 1024 / 1024).toFixed(1);
30
+ return `File too large (${(file.size / 1024 / 1024).toFixed(1)}MB). Max: ${maxMB}MB`;
31
+ }
32
+ if (acceptedTypes && acceptedTypes.length > 0 && !acceptedTypes.includes(file.type)) {
33
+ return `File type "${file.type}" not accepted. Allowed: ${acceptedTypes.join(', ')}`;
34
+ }
35
+ return null;
36
+ }, [maxSizeBytes, acceptedTypes]);
26
37
  const upload = useCallback(async (file) => {
38
+ // Client-side validation
39
+ const validationError = validate(file);
40
+ if (validationError) {
41
+ setError(validationError);
42
+ onError?.(validationError);
43
+ throw new Error(validationError);
44
+ }
45
+ // Before upload hook
46
+ if (onBeforeUpload) {
47
+ const hookResult = onBeforeUpload(file);
48
+ if (hookResult === false) {
49
+ const msg = 'Upload cancelled by onBeforeUpload';
50
+ setError(msg);
51
+ throw new Error(msg);
52
+ }
53
+ if (hookResult instanceof File) {
54
+ file = hookResult;
55
+ }
56
+ }
27
57
  setUploading(true);
58
+ setProgress(0);
28
59
  setError(null);
29
60
  setResult(null);
61
+ const controller = new AbortController();
62
+ abortRef.current = controller;
30
63
  try {
31
- const formData = new FormData();
32
- formData.append('file', file);
33
- formData.append('bucket', bucket);
64
+ // Convert file to base64
65
+ const base64 = await fileToBase64(file);
66
+ const base64Data = base64.split(',')[1]; // Remove data:type;base64, prefix
67
+ const timestamp = Date.now();
68
+ const safeFilename = file.name.replace(/[^a-zA-Z0-9.-]/g, '_');
69
+ const pathPrefix = folder || (orgId ? `uploads/${orgId}` : 'uploads');
70
+ const path = `${pathPrefix}/${timestamp}-${safeFilename}`;
71
+ setProgress(10); // File read complete
72
+ onProgress?.(10);
34
73
  const base = apiBaseUrl.replace(/\/$/, '');
35
- const res = await fetch(`${base}${uploadPath}?bucket=${encodeURIComponent(bucket)}`, {
36
- method: 'POST',
37
- body: formData,
38
- credentials: 'include',
74
+ const body = {
75
+ bucketName: bucket,
76
+ path,
77
+ file: base64Data,
78
+ contentType: file.type,
79
+ generateMultipleSizes,
80
+ };
81
+ // Use XMLHttpRequest for upload progress
82
+ const uploadResult = await new Promise((resolve, reject) => {
83
+ const xhr = new XMLHttpRequest();
84
+ xhr.upload.addEventListener('progress', (e) => {
85
+ if (e.lengthComputable) {
86
+ const pct = Math.round(10 + (e.loaded / e.total) * 80); // 10-90%
87
+ setProgress(pct);
88
+ onProgress?.(pct);
89
+ }
90
+ });
91
+ xhr.addEventListener('load', () => {
92
+ try {
93
+ const json = JSON.parse(xhr.responseText);
94
+ if (xhr.status >= 200 && xhr.status < 300 && json.success) {
95
+ setProgress(100);
96
+ onProgress?.(100);
97
+ const uploadResult = {
98
+ bucket,
99
+ path: json.data.path || path,
100
+ proxyUrl: json.data.publicUrl || json.data.proxyUrl || '',
101
+ contentType: file.type,
102
+ size: file.size,
103
+ ...(json.data.base_filename && {
104
+ base_filename: json.data.base_filename,
105
+ storage_prefix: json.data.storage_prefix,
106
+ sizes: json.data.sizes,
107
+ }),
108
+ };
109
+ resolve(uploadResult);
110
+ }
111
+ else {
112
+ reject(new Error(json.message || json.error || 'Upload failed'));
113
+ }
114
+ }
115
+ catch {
116
+ reject(new Error('Invalid server response'));
117
+ }
118
+ });
119
+ xhr.addEventListener('error', () => reject(new Error('Network error')));
120
+ xhr.addEventListener('abort', () => reject(new Error('Upload aborted')));
121
+ // Abort support
122
+ controller.signal.addEventListener('abort', () => xhr.abort());
123
+ xhr.open('POST', `${base}${uploadPath}`);
124
+ xhr.setRequestHeader('Content-Type', 'application/json');
125
+ xhr.withCredentials = true;
126
+ xhr.send(JSON.stringify(body));
39
127
  });
40
- const json = await res.json();
41
- if (!res.ok || !json.success) {
42
- throw new Error(json.error || 'Upload failed');
43
- }
44
- setResult(json.data);
45
- return json.data;
128
+ setResult(uploadResult);
129
+ onComplete?.(uploadResult);
130
+ return uploadResult;
46
131
  }
47
132
  catch (err) {
48
133
  const message = err instanceof Error ? err.message : 'Upload failed';
49
134
  setError(message);
135
+ onError?.(message);
50
136
  throw err;
51
137
  }
52
138
  finally {
53
139
  setUploading(false);
140
+ abortRef.current = null;
141
+ }
142
+ }, [apiBaseUrl, bucket, orgId, uploadPath, generateMultipleSizes, maxSizeBytes, acceptedTypes, folder, onProgress, onComplete, onError, onBeforeUpload, validate]);
143
+ const uploadMultiple = useCallback(async (files) => {
144
+ const results = [];
145
+ for (const file of files) {
146
+ const r = await upload(file);
147
+ results.push(r);
54
148
  }
55
- }, [apiBaseUrl, bucket, orgId, uploadPath]);
56
- return { upload, uploading, error, result };
149
+ return results;
150
+ }, [upload]);
151
+ const abort = useCallback(() => {
152
+ abortRef.current?.abort();
153
+ }, []);
154
+ return { upload, uploadMultiple, uploading, progress, error, result, abort, validate };
155
+ }
156
+ function fileToBase64(file) {
157
+ return new Promise((resolve, reject) => {
158
+ const reader = new FileReader();
159
+ reader.readAsDataURL(file);
160
+ reader.onload = () => resolve(reader.result);
161
+ reader.onerror = (error) => reject(error);
162
+ });
57
163
  }
58
164
  //# sourceMappingURL=useStorageUpload.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useStorageUpload.js","sourceRoot":"","sources":["../../../src/frontend/storage/useStorageUpload.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AAyB9C,MAAM,UAAU,gBAAgB,CAAC,OAAgC;IAC/D,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,2BAA2B,EAAE,GAAG,OAAO,CAAC;IACxF,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAClD,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IACxD,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAsB,IAAI,CAAC,CAAC;IAEhE,MAAM,MAAM,GAAG,WAAW,CAAC,KAAK,EAAE,IAAU,EAAyB,EAAE;QACrE,YAAY,CAAC,IAAI,CAAC,CAAC;QACnB,QAAQ,CAAC,IAAI,CAAC,CAAC;QACf,SAAS,CAAC,IAAI,CAAC,CAAC;QAEhB,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,IAAI,QAAQ,EAAE,CAAC;YAChC,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;YAC9B,QAAQ,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;YAElC,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YAC3C,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,GAAG,UAAU,WAAW,kBAAkB,CAAC,MAAM,CAAC,EAAE,EAAE;gBACnF,MAAM,EAAE,MAAM;gBACd,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,SAAS;aACvB,CAAC,CAAC;YAEH,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAsD,CAAC;YAElF,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;gBAC7B,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,eAAe,CAAC,CAAC;YACjD,CAAC;YAED,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACrB,OAAO,IAAI,CAAC,IAAI,CAAC;QACnB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;YACrE,QAAQ,CAAC,OAAO,CAAC,CAAC;YAClB,MAAM,GAAG,CAAC;QACZ,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC;IACH,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC;IAE5C,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;AAC9C,CAAC"}
1
+ {"version":3,"file":"useStorageUpload.js","sourceRoot":"","sources":["../../../src/frontend/storage/useStorageUpload.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"mailgun.js","sourceRoot":"","sources":["../../../src/shared/email/mailgun.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mailgun.js","sourceRoot":"","sources":["../../../src/shared/email/mailgun.ts"],"names":[],"mappings":"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"}
@@ -3,17 +3,38 @@
3
3
  *
4
4
  * Sets proper CORS/CORP/Cache headers so images work cross-origin.
5
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.
6
15
  */
7
16
  import type { Response } from 'express';
8
17
  import type { StorageConfig } from './types.js';
18
+ export interface ImageTransformOptions {
19
+ width?: number;
20
+ height?: number;
21
+ quality?: number;
22
+ format?: 'webp' | 'jpeg' | 'png';
23
+ }
9
24
  export declare class StorageProxyService {
10
25
  private readonly supabaseUrl;
11
26
  private readonly cacheMaxAge;
12
27
  constructor(config: StorageConfig);
28
+ /**
29
+ * Parse transform options from query parameters.
30
+ */
31
+ static parseTransformOptions(query: Record<string, any>): ImageTransformOptions | null;
13
32
  /**
14
33
  * Stream a file from Supabase storage to the response.
15
- * Sets Content-Type, Cache-Control, CORP, and CORS headers.
34
+ * Optionally transforms images on-the-fly with sharp.
16
35
  */
17
- streamFile(bucket: string, orgId: string, fileId: string, res: Response): Promise<void>;
36
+ streamFile(bucket: string, orgId: string, fileId: string, res: Response, transform?: ImageTransformOptions | null): Promise<void>;
37
+ private setHeaders;
38
+ private transformImage;
18
39
  }
19
40
  //# sourceMappingURL=StorageProxyService.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"StorageProxyService.d.ts","sourceRoot":"","sources":["../../../src/shared/storage/StorageProxyService.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAKhD,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;gBAEzB,MAAM,EAAE,aAAa;IAKjC;;;OAGG;IACG,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;CA+C9F"}
1
+ {"version":3,"file":"StorageProxyService.d.ts","sourceRoot":"","sources":["../../../src/shared/storage/StorageProxyService.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAKhD,MAAM,WAAW,qBAAqB;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;CAClC;AA0BD,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;gBAEzB,MAAM,EAAE,aAAa;IAKjC;;OAEG;IACH,MAAM,CAAC,qBAAqB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,qBAAqB,GAAG,IAAI;IAgBtF;;;OAGG;IACG,UAAU,CACd,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,QAAQ,EACb,SAAS,CAAC,EAAE,qBAAqB,GAAG,IAAI,GACvC,OAAO,CAAC,IAAI,CAAC;IAoFhB,OAAO,CAAC,UAAU;YAOJ,cAAc;CAiC7B"}
@@ -3,9 +3,41 @@
3
3
  *
4
4
  * Sets proper CORS/CORP/Cache headers so images work cross-origin.
5
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.
6
15
  */
7
16
  import { createLogger } from '../../utils/logger.js';
8
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
+ }
9
41
  export class StorageProxyService {
10
42
  supabaseUrl;
11
43
  cacheMaxAge;
@@ -13,16 +45,45 @@ export class StorageProxyService {
13
45
  this.supabaseUrl = config.supabaseUrl || process.env.SUPABASE_URL || '';
14
46
  this.cacheMaxAge = config.cacheMaxAge ?? 3600;
15
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
+ }
16
65
  /**
17
66
  * Stream a file from Supabase storage to the response.
18
- * Sets Content-Type, Cache-Control, CORP, and CORS headers.
67
+ * Optionally transforms images on-the-fly with sharp.
19
68
  */
20
- async streamFile(bucket, orgId, fileId, res) {
69
+ async streamFile(bucket, orgId, fileId, res, transform) {
21
70
  if (!this.supabaseUrl) {
22
71
  res.status(503).json({ success: false, error: 'Storage not configured' });
23
72
  return;
24
73
  }
25
74
  const url = `${this.supabaseUrl}/storage/v1/object/public/${bucket}/${orgId}/${fileId}`;
75
+ // Check transform cache
76
+ if (transform) {
77
+ const cacheKey = `${url}:w${transform.width || ''}:h${transform.height || ''}:q${transform.quality || ''}:f${transform.format || ''}`;
78
+ const cached = getCached(cacheKey);
79
+ if (cached) {
80
+ this.setHeaders(res, cached.contentType);
81
+ res.setHeader('Content-Length', cached.buffer.length.toString());
82
+ res.setHeader('X-Transform-Cache', 'hit');
83
+ res.end(cached.buffer);
84
+ return;
85
+ }
86
+ }
26
87
  try {
27
88
  const upstream = await fetch(url);
28
89
  if (!upstream.ok) {
@@ -33,10 +94,31 @@ export class StorageProxyService {
33
94
  return;
34
95
  }
35
96
  const contentType = upstream.headers.get('content-type') || 'application/octet-stream';
36
- res.setHeader('Content-Type', contentType);
37
- res.setHeader('Cache-Control', `public, max-age=${this.cacheMaxAge}, immutable`);
38
- res.setHeader('Cross-Origin-Resource-Policy', 'cross-origin');
39
- res.setHeader('Access-Control-Allow-Origin', '*');
97
+ const isImage = contentType.startsWith('image/');
98
+ // If transform requested and it's an image, try sharp
99
+ if (transform && isImage) {
100
+ try {
101
+ const buffer = Buffer.from(await upstream.arrayBuffer());
102
+ const transformed = await this.transformImage(buffer, transform);
103
+ const outType = transform.format
104
+ ? `image/${transform.format}`
105
+ : contentType;
106
+ // Cache the result
107
+ const cacheKey = `${url}:w${transform.width || ''}:h${transform.height || ''}:q${transform.quality || ''}:f${transform.format || ''}`;
108
+ setCache(cacheKey, transformed, outType);
109
+ this.setHeaders(res, outType);
110
+ res.setHeader('Content-Length', transformed.length.toString());
111
+ res.setHeader('X-Transform', 'applied');
112
+ res.end(transformed);
113
+ return;
114
+ }
115
+ catch (transformError) {
116
+ // Sharp not available or transform failed — fall through to stream original
117
+ logger.debug({ error: transformError }, 'Transform failed, streaming original');
118
+ }
119
+ }
120
+ // Stream original
121
+ this.setHeaders(res, contentType);
40
122
  const contentLength = upstream.headers.get('content-length');
41
123
  if (contentLength) {
42
124
  res.setHeader('Content-Length', contentLength);
@@ -59,5 +141,39 @@ export class StorageProxyService {
59
141
  res.status(502).json({ success: false, error: 'Failed to fetch from storage' });
60
142
  }
61
143
  }
144
+ setHeaders(res, contentType) {
145
+ res.setHeader('Content-Type', contentType);
146
+ res.setHeader('Cache-Control', `public, max-age=${this.cacheMaxAge}, immutable`);
147
+ res.setHeader('Cross-Origin-Resource-Policy', 'cross-origin');
148
+ res.setHeader('Access-Control-Allow-Origin', '*');
149
+ }
150
+ async transformImage(buffer, options) {
151
+ // Dynamic import — sharp is optional peer dep
152
+ const sharp = (await import('sharp')).default;
153
+ let pipeline = sharp(buffer);
154
+ if (options.width || options.height) {
155
+ pipeline = pipeline.resize(options.width, options.height, {
156
+ fit: 'inside',
157
+ withoutEnlargement: true,
158
+ });
159
+ }
160
+ const quality = options.quality || 80;
161
+ switch (options.format) {
162
+ case 'webp':
163
+ pipeline = pipeline.webp({ quality });
164
+ break;
165
+ case 'jpeg':
166
+ pipeline = pipeline.jpeg({ quality, progressive: true });
167
+ break;
168
+ case 'png':
169
+ pipeline = pipeline.png({ quality });
170
+ break;
171
+ default:
172
+ // Auto-detect from original — apply quality
173
+ pipeline = pipeline.jpeg({ quality, progressive: true });
174
+ break;
175
+ }
176
+ return pipeline.toBuffer();
177
+ }
62
178
  }
63
179
  //# sourceMappingURL=StorageProxyService.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"StorageProxyService.js","sourceRoot":"","sources":["../../../src/shared/storage/StorageProxyService.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"StorageProxyService.js","sourceRoot":"","sources":["../../../src/shared/storage/StorageProxyService.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../../src/shared/storage/routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,MAAM,EAAqB,MAAM,SAAS,CAAC;AAGpD,OAAO,KAAK,EAAiB,mBAAmB,EAAE,oBAAoB,EAAgB,MAAM,YAAY,CAAC;AAKzG;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,mBAAmB,GAAG,IAAI,CAwBxF;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,oBAAoB,GAAG,IAAI,CA8F1F"}
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../../src/shared/storage/routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,MAAM,EAAqB,MAAM,SAAS,CAAC;AAGpD,OAAO,KAAK,EAAiB,mBAAmB,EAAE,oBAAoB,EAAgB,MAAM,YAAY,CAAC;AAKzG;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,mBAAmB,GAAG,IAAI,CA2BxF;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,oBAAoB,GAAG,IAAI,CA8F1F"}
@@ -47,7 +47,9 @@ export function addStorageProxyRoutes(router, options) {
47
47
  res.status(400).json({ success: false, error: 'Invalid bucket' });
48
48
  return;
49
49
  }
50
- await service.streamFile(bucket, orgId, fileId, res);
50
+ // Parse optional image transform params: ?w=400&h=300&q=80&f=webp
51
+ const transform = StorageProxyService.parseTransformOptions(req.query);
52
+ await service.streamFile(bucket, orgId, fileId, res, transform);
51
53
  });
52
54
  logger.info({ buckets: config.allowedBuckets }, 'Storage proxy routes mounted');
53
55
  }
@@ -1 +1 @@
1
- {"version":3,"file":"routes.js","sourceRoot":"","sources":["../../../src/shared/storage/routes.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"routes.js","sourceRoot":"","sources":["../../../src/shared/storage/routes.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soulbatical/tetra-core",
3
- "version": "0.1.48",
3
+ "version": "0.1.50",
4
4
  "publishConfig": {
5
5
  "access": "restricted"
6
6
  },