@gallop.software/studio 1.0.6 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-FDWPNRNZ.mjs → chunk-CIS6B4SP.mjs} +7 -2
- package/dist/chunk-CIS6B4SP.mjs.map +1 -0
- package/dist/{chunk-WJJHVPLT.js → chunk-VSOTEQ7X.js} +7 -2
- package/dist/chunk-VSOTEQ7X.js.map +1 -0
- package/dist/handlers/index.js +78 -71
- package/dist/handlers/index.js.map +1 -1
- package/dist/handlers/index.mjs +55 -48
- package/dist/handlers/index.mjs.map +1 -1
- package/dist/index.d.mts +13 -7
- package/dist/index.d.ts +13 -7
- package/dist/index.js +2 -2
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
- package/dist/chunk-FDWPNRNZ.mjs.map +0 -1
- package/dist/chunk-WJJHVPLT.js.map +0 -1
|
@@ -19,9 +19,14 @@ function getAllThumbnailPaths(originalPath) {
|
|
|
19
19
|
getThumbnailPath(originalPath, "sm")
|
|
20
20
|
];
|
|
21
21
|
}
|
|
22
|
+
function isProcessed(entry) {
|
|
23
|
+
if (!entry) return false;
|
|
24
|
+
return !!(entry.f || entry.lg || entry.md || entry.sm);
|
|
25
|
+
}
|
|
22
26
|
|
|
23
27
|
export {
|
|
24
28
|
getThumbnailPath,
|
|
25
|
-
getAllThumbnailPaths
|
|
29
|
+
getAllThumbnailPaths,
|
|
30
|
+
isProcessed
|
|
26
31
|
};
|
|
27
|
-
//# sourceMappingURL=chunk-
|
|
32
|
+
//# sourceMappingURL=chunk-CIS6B4SP.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["/**\n * Dimensions tuple [width, height]\n */\nexport type Dimensions = [number, number]\n\n/**\n * Meta entry - works for images and non-images\n * o: original dimensions, b: blurhash, c: CDN index\n * sm/md/lg/f: thumbnail dimensions (presence implies processed)\n */\nexport interface MetaEntry {\n o?: Dimensions // original dimensions [width, height]\n b?: string // blurhash\n sm?: Dimensions // small thumbnail (300px width)\n md?: Dimensions // medium thumbnail (700px width)\n lg?: Dimensions // large thumbnail (1400px width)\n f?: Dimensions // full size (capped at 2560px width)\n c?: number // CDN index - index into _cdns array\n}\n\n/**\n * Full meta schema including special keys\n * _cdns: Array of CDN base URLs\n * Other keys: file paths from public folder\n */\nexport interface FullMeta {\n _cdns?: string[] // Array of CDN base URLs\n [key: string]: MetaEntry | string[] | undefined\n}\n\n/**\n * Meta schema - keyed by path from public folder\n * Example: { \"/portfolio/photo.jpg\": { o: [2400, 1600], b: \"...\", sm: [300, 200], ... } }\n */\nexport type LeanMeta = Record<string, MetaEntry>\n\n// Alias for compatibility\nexport type LeanImageEntry = MetaEntry\n\n/**\n * File/folder item for browser\n */\nexport interface FileItem {\n name: string\n path: string\n type: 'file' | 'folder'\n size?: number\n dimensions?: { width: number; height: number }\n isProcessed?: boolean\n cdnPushed?: boolean\n cdnBaseUrl?: string // CDN base URL when pushed to cloud\n isRemote?: boolean // true if CDN URL doesn't match R2 (external import)\n isProtected?: boolean // true for images folder and its contents (cannot select/modify)\n // Folder-specific properties\n fileCount?: number\n totalSize?: number\n // For showing thumbnails - path to -sm version if exists\n thumbnail?: string\n // Whether a processed thumbnail exists\n hasThumbnail?: boolean\n}\n\n/**\n * Studio configuration\n */\nexport interface StudioConfig {\n r2AccountId?: string\n r2AccessKeyId?: string\n r2SecretAccessKey?: string\n r2BucketName?: string\n r2PublicUrl?: string\n thumbnailSizes?: {\n small: number\n medium: number\n large: number\n }\n}\n\n/**\n * Get thumbnail path from original image path\n */\nexport function getThumbnailPath(originalPath: string, size: 'sm' | 'md' | 'lg' | 'full'): string {\n if (size === 'full') {\n const ext = originalPath.match(/\\.\\w+$/)?.[0] || '.jpg'\n const base = originalPath.replace(/\\.\\w+$/, '')\n const outputExt = ext.toLowerCase() === '.png' ? '.png' : '.jpg'\n return `/images${base}${outputExt}`\n }\n const ext = originalPath.match(/\\.\\w+$/)?.[0] || '.jpg'\n const base = originalPath.replace(/\\.\\w+$/, '')\n const outputExt = ext.toLowerCase() === '.png' ? '.png' : '.jpg'\n return `/images${base}-${size}${outputExt}`\n}\n\n/**\n * Get all thumbnail paths for an image\n */\nexport function getAllThumbnailPaths(originalPath: string): string[] {\n return [\n getThumbnailPath(originalPath, 'full'),\n getThumbnailPath(originalPath, 'lg'),\n getThumbnailPath(originalPath, 'md'),\n getThumbnailPath(originalPath, 'sm'),\n ]\n}\n\n/**\n * Check if an image entry is processed (has any thumbnail dimensions)\n */\nexport function isProcessed(entry: MetaEntry | undefined): boolean {\n if (!entry) return false\n return !!(entry.f || entry.lg || entry.md || entry.sm)\n}\n"],"mappings":";AAiFO,SAAS,iBAAiB,cAAsB,MAA2C;AAChG,MAAI,SAAS,QAAQ;AACnB,UAAMA,OAAM,aAAa,MAAM,QAAQ,IAAI,CAAC,KAAK;AACjD,UAAMC,QAAO,aAAa,QAAQ,UAAU,EAAE;AAC9C,UAAMC,aAAYF,KAAI,YAAY,MAAM,SAAS,SAAS;AAC1D,WAAO,UAAUC,KAAI,GAAGC,UAAS;AAAA,EACnC;AACA,QAAM,MAAM,aAAa,MAAM,QAAQ,IAAI,CAAC,KAAK;AACjD,QAAM,OAAO,aAAa,QAAQ,UAAU,EAAE;AAC9C,QAAM,YAAY,IAAI,YAAY,MAAM,SAAS,SAAS;AAC1D,SAAO,UAAU,IAAI,IAAI,IAAI,GAAG,SAAS;AAC3C;AAKO,SAAS,qBAAqB,cAAgC;AACnE,SAAO;AAAA,IACL,iBAAiB,cAAc,MAAM;AAAA,IACrC,iBAAiB,cAAc,IAAI;AAAA,IACnC,iBAAiB,cAAc,IAAI;AAAA,IACnC,iBAAiB,cAAc,IAAI;AAAA,EACrC;AACF;AAKO,SAAS,YAAY,OAAuC;AACjE,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO,CAAC,EAAE,MAAM,KAAK,MAAM,MAAM,MAAM,MAAM,MAAM;AACrD;","names":["ext","base","outputExt"]}
|
|
@@ -19,9 +19,14 @@ function getAllThumbnailPaths(originalPath) {
|
|
|
19
19
|
getThumbnailPath(originalPath, "sm")
|
|
20
20
|
];
|
|
21
21
|
}
|
|
22
|
+
function isProcessed(entry) {
|
|
23
|
+
if (!entry) return false;
|
|
24
|
+
return !!(entry.f || entry.lg || entry.md || entry.sm);
|
|
25
|
+
}
|
|
26
|
+
|
|
22
27
|
|
|
23
28
|
|
|
24
29
|
|
|
25
30
|
|
|
26
|
-
exports.getThumbnailPath = getThumbnailPath; exports.getAllThumbnailPaths = getAllThumbnailPaths;
|
|
27
|
-
//# sourceMappingURL=chunk-
|
|
31
|
+
exports.getThumbnailPath = getThumbnailPath; exports.getAllThumbnailPaths = getAllThumbnailPaths; exports.isProcessed = isProcessed;
|
|
32
|
+
//# sourceMappingURL=chunk-VSOTEQ7X.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/chrisb/Sites/studio/dist/chunk-VSOTEQ7X.js","../src/types.ts"],"names":["ext","base","outputExt"],"mappings":"AAAA;ACiFO,SAAS,gBAAA,CAAiB,YAAA,EAAsB,IAAA,EAA2C;AAChG,EAAA,GAAA,CAAI,KAAA,IAAS,MAAA,EAAQ;AACnB,IAAA,MAAMA,KAAAA,kBAAM,YAAA,mBAAa,KAAA,mBAAM,QAAQ,CAAA,4BAAA,CAAI,CAAC,IAAA,GAAK,MAAA;AACjD,IAAA,MAAMC,MAAAA,EAAO,YAAA,CAAa,OAAA,CAAQ,QAAA,EAAU,EAAE,CAAA;AAC9C,IAAA,MAAMC,WAAAA,EAAYF,IAAAA,CAAI,WAAA,CAAY,EAAA,IAAM,OAAA,EAAS,OAAA,EAAS,MAAA;AAC1D,IAAA,OAAO,CAAA,OAAA,EAAUC,KAAI,CAAA,EAAA;AACvB,EAAA;AACyB,EAAA;AACZ,EAAA;AACS,EAAA;AACG,EAAA;AAC3B;AAKgB;AACP,EAAA;AACY,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACnB,EAAA;AACF;AAK4B;AACP,EAAA;AACE,EAAA;AACvB;ADvF2B;AACA;AACA;AACA;AACA;AACA","file":"/Users/chrisb/Sites/studio/dist/chunk-VSOTEQ7X.js","sourcesContent":[null,"/**\n * Dimensions tuple [width, height]\n */\nexport type Dimensions = [number, number]\n\n/**\n * Meta entry - works for images and non-images\n * o: original dimensions, b: blurhash, c: CDN index\n * sm/md/lg/f: thumbnail dimensions (presence implies processed)\n */\nexport interface MetaEntry {\n o?: Dimensions // original dimensions [width, height]\n b?: string // blurhash\n sm?: Dimensions // small thumbnail (300px width)\n md?: Dimensions // medium thumbnail (700px width)\n lg?: Dimensions // large thumbnail (1400px width)\n f?: Dimensions // full size (capped at 2560px width)\n c?: number // CDN index - index into _cdns array\n}\n\n/**\n * Full meta schema including special keys\n * _cdns: Array of CDN base URLs\n * Other keys: file paths from public folder\n */\nexport interface FullMeta {\n _cdns?: string[] // Array of CDN base URLs\n [key: string]: MetaEntry | string[] | undefined\n}\n\n/**\n * Meta schema - keyed by path from public folder\n * Example: { \"/portfolio/photo.jpg\": { o: [2400, 1600], b: \"...\", sm: [300, 200], ... } }\n */\nexport type LeanMeta = Record<string, MetaEntry>\n\n// Alias for compatibility\nexport type LeanImageEntry = MetaEntry\n\n/**\n * File/folder item for browser\n */\nexport interface FileItem {\n name: string\n path: string\n type: 'file' | 'folder'\n size?: number\n dimensions?: { width: number; height: number }\n isProcessed?: boolean\n cdnPushed?: boolean\n cdnBaseUrl?: string // CDN base URL when pushed to cloud\n isRemote?: boolean // true if CDN URL doesn't match R2 (external import)\n isProtected?: boolean // true for images folder and its contents (cannot select/modify)\n // Folder-specific properties\n fileCount?: number\n totalSize?: number\n // For showing thumbnails - path to -sm version if exists\n thumbnail?: string\n // Whether a processed thumbnail exists\n hasThumbnail?: boolean\n}\n\n/**\n * Studio configuration\n */\nexport interface StudioConfig {\n r2AccountId?: string\n r2AccessKeyId?: string\n r2SecretAccessKey?: string\n r2BucketName?: string\n r2PublicUrl?: string\n thumbnailSizes?: {\n small: number\n medium: number\n large: number\n }\n}\n\n/**\n * Get thumbnail path from original image path\n */\nexport function getThumbnailPath(originalPath: string, size: 'sm' | 'md' | 'lg' | 'full'): string {\n if (size === 'full') {\n const ext = originalPath.match(/\\.\\w+$/)?.[0] || '.jpg'\n const base = originalPath.replace(/\\.\\w+$/, '')\n const outputExt = ext.toLowerCase() === '.png' ? '.png' : '.jpg'\n return `/images${base}${outputExt}`\n }\n const ext = originalPath.match(/\\.\\w+$/)?.[0] || '.jpg'\n const base = originalPath.replace(/\\.\\w+$/, '')\n const outputExt = ext.toLowerCase() === '.png' ? '.png' : '.jpg'\n return `/images${base}-${size}${outputExt}`\n}\n\n/**\n * Get all thumbnail paths for an image\n */\nexport function getAllThumbnailPaths(originalPath: string): string[] {\n return [\n getThumbnailPath(originalPath, 'full'),\n getThumbnailPath(originalPath, 'lg'),\n getThumbnailPath(originalPath, 'md'),\n getThumbnailPath(originalPath, 'sm'),\n ]\n}\n\n/**\n * Check if an image entry is processed (has any thumbnail dimensions)\n */\nexport function isProcessed(entry: MetaEntry | undefined): boolean {\n if (!entry) return false\n return !!(entry.f || entry.lg || entry.md || entry.sm)\n}\n"]}
|
package/dist/handlers/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
var _chunkVSOTEQ7Xjs = require('../chunk-VSOTEQ7X.js');
|
|
5
6
|
|
|
6
7
|
// src/handlers/index.ts
|
|
7
8
|
var _server = require('next/server');
|
|
@@ -106,16 +107,18 @@ function getContentType(filePath) {
|
|
|
106
107
|
|
|
107
108
|
var _sharp = require('sharp'); var _sharp2 = _interopRequireDefault(_sharp);
|
|
108
109
|
var _blurhash = require('blurhash');
|
|
110
|
+
var FULL_MAX_WIDTH = 2560;
|
|
109
111
|
var DEFAULT_SIZES = {
|
|
110
|
-
small: { width: 300, suffix: "-sm" },
|
|
111
|
-
medium: { width: 700, suffix: "-md" },
|
|
112
|
-
large: { width: 1400, suffix: "-lg" }
|
|
112
|
+
small: { width: 300, suffix: "-sm", key: "sm" },
|
|
113
|
+
medium: { width: 700, suffix: "-md", key: "md" },
|
|
114
|
+
large: { width: 1400, suffix: "-lg", key: "lg" }
|
|
113
115
|
};
|
|
114
116
|
async function processImage(buffer, imageKey) {
|
|
115
117
|
const sharpInstance = _sharp2.default.call(void 0, buffer);
|
|
116
118
|
const metadata = await sharpInstance.metadata();
|
|
117
119
|
const originalWidth = metadata.width || 0;
|
|
118
120
|
const originalHeight = metadata.height || 0;
|
|
121
|
+
const ratio = originalHeight / originalWidth;
|
|
119
122
|
const keyWithoutSlash = imageKey.startsWith("/") ? imageKey.slice(1) : imageKey;
|
|
120
123
|
const baseName = _path2.default.basename(keyWithoutSlash, _path2.default.extname(keyWithoutSlash));
|
|
121
124
|
const ext = _path2.default.extname(keyWithoutSlash).toLowerCase();
|
|
@@ -124,19 +127,34 @@ async function processImage(buffer, imageKey) {
|
|
|
124
127
|
await _fs.promises.mkdir(imagesPath, { recursive: true });
|
|
125
128
|
const isPng = ext === ".png";
|
|
126
129
|
const outputExt = isPng ? ".png" : ".jpg";
|
|
130
|
+
const entry = {
|
|
131
|
+
o: [originalWidth, originalHeight]
|
|
132
|
+
};
|
|
127
133
|
const fullFileName = imageDir === "." ? `${baseName}${outputExt}` : `${imageDir}/${baseName}${outputExt}`;
|
|
128
134
|
const fullPath = _path2.default.join(process.cwd(), "public", "images", fullFileName);
|
|
129
|
-
|
|
130
|
-
|
|
135
|
+
let fullWidth = originalWidth;
|
|
136
|
+
let fullHeight = originalHeight;
|
|
137
|
+
if (originalWidth > FULL_MAX_WIDTH) {
|
|
138
|
+
fullWidth = FULL_MAX_WIDTH;
|
|
139
|
+
fullHeight = Math.round(FULL_MAX_WIDTH * ratio);
|
|
140
|
+
if (isPng) {
|
|
141
|
+
await _sharp2.default.call(void 0, buffer).resize(fullWidth, fullHeight).png({ quality: 85 }).toFile(fullPath);
|
|
142
|
+
} else {
|
|
143
|
+
await _sharp2.default.call(void 0, buffer).resize(fullWidth, fullHeight).jpeg({ quality: 85 }).toFile(fullPath);
|
|
144
|
+
}
|
|
131
145
|
} else {
|
|
132
|
-
|
|
146
|
+
if (isPng) {
|
|
147
|
+
await _sharp2.default.call(void 0, buffer).png({ quality: 85 }).toFile(fullPath);
|
|
148
|
+
} else {
|
|
149
|
+
await _sharp2.default.call(void 0, buffer).jpeg({ quality: 85 }).toFile(fullPath);
|
|
150
|
+
}
|
|
133
151
|
}
|
|
152
|
+
entry.f = [fullWidth, fullHeight];
|
|
134
153
|
for (const [, sizeConfig] of Object.entries(DEFAULT_SIZES)) {
|
|
135
|
-
const { width: maxWidth, suffix } = sizeConfig;
|
|
154
|
+
const { width: maxWidth, suffix, key } = sizeConfig;
|
|
136
155
|
if (originalWidth <= maxWidth) {
|
|
137
156
|
continue;
|
|
138
157
|
}
|
|
139
|
-
const ratio = originalHeight / originalWidth;
|
|
140
158
|
const newHeight = Math.round(maxWidth * ratio);
|
|
141
159
|
const sizeFileName = `${baseName}${suffix}${outputExt}`;
|
|
142
160
|
const sizeFilePath = imageDir === "." ? sizeFileName : `${imageDir}/${sizeFileName}`;
|
|
@@ -146,14 +164,11 @@ async function processImage(buffer, imageKey) {
|
|
|
146
164
|
} else {
|
|
147
165
|
await _sharp2.default.call(void 0, buffer).resize(maxWidth, newHeight).jpeg({ quality: 80 }).toFile(sizePath);
|
|
148
166
|
}
|
|
167
|
+
entry[key] = [maxWidth, newHeight];
|
|
149
168
|
}
|
|
150
169
|
const { data, info } = await _sharp2.default.call(void 0, buffer).resize(32, 32, { fit: "inside" }).ensureAlpha().raw().toBuffer({ resolveWithObject: true });
|
|
151
|
-
|
|
152
|
-
return
|
|
153
|
-
w: originalWidth,
|
|
154
|
-
h: originalHeight,
|
|
155
|
-
b: blurhash
|
|
156
|
-
};
|
|
170
|
+
entry.b = _blurhash.encode.call(void 0, new Uint8ClampedArray(data), info.width, info.height, 4, 4);
|
|
171
|
+
return entry;
|
|
157
172
|
}
|
|
158
173
|
|
|
159
174
|
// src/handlers/utils/cdn.ts
|
|
@@ -219,7 +234,7 @@ async function uploadToCdn(imageKey) {
|
|
|
219
234
|
const bucketName = process.env.CLOUDFLARE_R2_BUCKET_NAME;
|
|
220
235
|
if (!bucketName) throw new Error("R2 bucket not configured");
|
|
221
236
|
const r2 = getR2Client();
|
|
222
|
-
for (const thumbPath of
|
|
237
|
+
for (const thumbPath of _chunkVSOTEQ7Xjs.getAllThumbnailPaths.call(void 0, imageKey)) {
|
|
223
238
|
const localPath = _path2.default.join(process.cwd(), "public", thumbPath);
|
|
224
239
|
try {
|
|
225
240
|
const fileBuffer = await _fs.promises.readFile(localPath);
|
|
@@ -236,7 +251,7 @@ async function uploadToCdn(imageKey) {
|
|
|
236
251
|
}
|
|
237
252
|
}
|
|
238
253
|
async function deleteLocalThumbnails(imageKey) {
|
|
239
|
-
for (const thumbPath of
|
|
254
|
+
for (const thumbPath of _chunkVSOTEQ7Xjs.getAllThumbnailPaths.call(void 0, imageKey)) {
|
|
240
255
|
const localPath = _path2.default.join(process.cwd(), "public", thumbPath);
|
|
241
256
|
try {
|
|
242
257
|
await _fs.promises.unlink(localPath);
|
|
@@ -281,7 +296,7 @@ async function deleteFromCdn(imageKey, hasThumbnails) {
|
|
|
281
296
|
} catch (e4) {
|
|
282
297
|
}
|
|
283
298
|
if (hasThumbnails) {
|
|
284
|
-
for (const thumbPath of
|
|
299
|
+
for (const thumbPath of _chunkVSOTEQ7Xjs.getAllThumbnailPaths.call(void 0, imageKey)) {
|
|
285
300
|
try {
|
|
286
301
|
await r2.send(
|
|
287
302
|
new (0, _clients3.DeleteObjectCommand)({
|
|
@@ -400,8 +415,9 @@ async function handleList(request) {
|
|
|
400
415
|
let thumbnail;
|
|
401
416
|
let hasThumbnail = false;
|
|
402
417
|
let fileSize;
|
|
403
|
-
|
|
404
|
-
|
|
418
|
+
const entryIsProcessed = _chunkVSOTEQ7Xjs.isProcessed.call(void 0, entry);
|
|
419
|
+
if (isImage && entryIsProcessed) {
|
|
420
|
+
const thumbPath = _chunkVSOTEQ7Xjs.getThumbnailPath.call(void 0, key, "sm");
|
|
405
421
|
if (isPushedToCloud && entry.c !== void 0) {
|
|
406
422
|
const cdnUrl = cdnUrls[entry.c];
|
|
407
423
|
if (cdnUrl) {
|
|
@@ -443,12 +459,12 @@ async function handleList(request) {
|
|
|
443
459
|
size: fileSize,
|
|
444
460
|
thumbnail,
|
|
445
461
|
hasThumbnail,
|
|
446
|
-
isProcessed:
|
|
462
|
+
isProcessed: entryIsProcessed,
|
|
447
463
|
cdnPushed: isPushedToCloud,
|
|
448
464
|
cdnBaseUrl: fileCdnUrl,
|
|
449
465
|
isRemote,
|
|
450
466
|
isProtected: isInsideImagesFolder,
|
|
451
|
-
dimensions: entry.
|
|
467
|
+
dimensions: entry.o ? { width: entry.o[0], height: entry.o[1] } : void 0
|
|
452
468
|
});
|
|
453
469
|
}
|
|
454
470
|
}
|
|
@@ -480,8 +496,9 @@ async function handleSearch(request) {
|
|
|
480
496
|
const isRemote = isPushedToCloud && (!r2PublicUrl || fileCdnUrl !== r2PublicUrl);
|
|
481
497
|
let thumbnail;
|
|
482
498
|
let hasThumbnail = false;
|
|
483
|
-
|
|
484
|
-
|
|
499
|
+
const entryIsProcessed = _chunkVSOTEQ7Xjs.isProcessed.call(void 0, entry);
|
|
500
|
+
if (isImage && entryIsProcessed) {
|
|
501
|
+
const thumbPath = _chunkVSOTEQ7Xjs.getThumbnailPath.call(void 0, key, "sm");
|
|
485
502
|
if (isPushedToCloud && entry.c !== void 0) {
|
|
486
503
|
const cdnUrl = cdnUrls[entry.c];
|
|
487
504
|
if (cdnUrl) {
|
|
@@ -514,11 +531,11 @@ async function handleSearch(request) {
|
|
|
514
531
|
type: "file",
|
|
515
532
|
thumbnail,
|
|
516
533
|
hasThumbnail,
|
|
517
|
-
isProcessed:
|
|
534
|
+
isProcessed: entryIsProcessed,
|
|
518
535
|
cdnPushed: isPushedToCloud,
|
|
519
536
|
cdnBaseUrl: fileCdnUrl,
|
|
520
537
|
isRemote,
|
|
521
|
-
dimensions: entry.
|
|
538
|
+
dimensions: entry.o ? { width: entry.o[0], height: entry.o[1] } : void 0
|
|
522
539
|
});
|
|
523
540
|
}
|
|
524
541
|
return _server.NextResponse.json({ items });
|
|
@@ -736,7 +753,7 @@ async function handleDelete(request) {
|
|
|
736
753
|
for (const key of Object.keys(meta)) {
|
|
737
754
|
if (key.startsWith(prefix) || key === imageKey) {
|
|
738
755
|
if (!meta[key].c) {
|
|
739
|
-
for (const thumbPath of
|
|
756
|
+
for (const thumbPath of _chunkVSOTEQ7Xjs.getAllThumbnailPaths.call(void 0, key)) {
|
|
740
757
|
const absoluteThumbPath = _path2.default.join(process.cwd(), "public", thumbPath);
|
|
741
758
|
try {
|
|
742
759
|
await _fs.promises.unlink(absoluteThumbPath);
|
|
@@ -752,7 +769,7 @@ async function handleDelete(request) {
|
|
|
752
769
|
const isInImagesFolder = itemPath.startsWith("public/images/");
|
|
753
770
|
if (!isInImagesFolder && entry) {
|
|
754
771
|
if (!isPushedToCloud) {
|
|
755
|
-
for (const thumbPath of
|
|
772
|
+
for (const thumbPath of _chunkVSOTEQ7Xjs.getAllThumbnailPaths.call(void 0, imageKey)) {
|
|
756
773
|
const absoluteThumbPath = _path2.default.join(process.cwd(), "public", thumbPath);
|
|
757
774
|
try {
|
|
758
775
|
await _fs.promises.unlink(absoluteThumbPath);
|
|
@@ -864,8 +881,8 @@ async function handleRename(request) {
|
|
|
864
881
|
const newKey = "/" + newRelativePath;
|
|
865
882
|
if (meta[oldKey]) {
|
|
866
883
|
const entry = meta[oldKey];
|
|
867
|
-
const oldThumbPaths =
|
|
868
|
-
const newThumbPaths =
|
|
884
|
+
const oldThumbPaths = _chunkVSOTEQ7Xjs.getAllThumbnailPaths.call(void 0, oldKey);
|
|
885
|
+
const newThumbPaths = _chunkVSOTEQ7Xjs.getAllThumbnailPaths.call(void 0, newKey);
|
|
869
886
|
for (let i = 0; i < oldThumbPaths.length; i++) {
|
|
870
887
|
const oldThumbPath = _path2.default.join(process.cwd(), "public", oldThumbPaths[i]);
|
|
871
888
|
const newThumbPath = _path2.default.join(process.cwd(), "public", newThumbPaths[i]);
|
|
@@ -949,7 +966,7 @@ async function handleMoveStream(request) {
|
|
|
949
966
|
const fileCdnUrl = isInCloud && entry.c !== void 0 ? cdnUrls[entry.c] : void 0;
|
|
950
967
|
const isRemote = isInCloud && (!r2PublicUrl || fileCdnUrl !== r2PublicUrl);
|
|
951
968
|
const isPushedToR2 = isInCloud && r2PublicUrl && fileCdnUrl === r2PublicUrl;
|
|
952
|
-
const hasProcessedThumbnails =
|
|
969
|
+
const hasProcessedThumbnails = _chunkVSOTEQ7Xjs.isProcessed.call(void 0, entry);
|
|
953
970
|
try {
|
|
954
971
|
if (isRemote && isImage) {
|
|
955
972
|
const remoteUrl = `${fileCdnUrl}${oldKey}`;
|
|
@@ -957,9 +974,8 @@ async function handleMoveStream(request) {
|
|
|
957
974
|
await _fs.promises.mkdir(_path2.default.dirname(newAbsolutePath), { recursive: true });
|
|
958
975
|
await _fs.promises.writeFile(newAbsolutePath, buffer);
|
|
959
976
|
const newEntry = {
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
b: _optionalChain([entry, 'optionalAccess', _22 => _22.b])
|
|
977
|
+
o: _optionalChain([entry, 'optionalAccess', _19 => _19.o]),
|
|
978
|
+
b: _optionalChain([entry, 'optionalAccess', _20 => _20.b])
|
|
963
979
|
};
|
|
964
980
|
delete meta[oldKey];
|
|
965
981
|
meta[newKey] = newEntry;
|
|
@@ -968,17 +984,13 @@ async function handleMoveStream(request) {
|
|
|
968
984
|
const buffer = await downloadFromCdn(oldKey);
|
|
969
985
|
await _fs.promises.mkdir(_path2.default.dirname(newAbsolutePath), { recursive: true });
|
|
970
986
|
await _fs.promises.writeFile(newAbsolutePath, buffer);
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
b: _optionalChain([entry, 'optionalAccess', _25 => _25.b])
|
|
987
|
+
let newEntry = {
|
|
988
|
+
o: _optionalChain([entry, 'optionalAccess', _21 => _21.o]),
|
|
989
|
+
b: _optionalChain([entry, 'optionalAccess', _22 => _22.b])
|
|
975
990
|
};
|
|
976
991
|
if (hasProcessedThumbnails) {
|
|
977
992
|
const processedEntry = await processImage(buffer, newKey);
|
|
978
|
-
newEntry
|
|
979
|
-
newEntry.h = processedEntry.h;
|
|
980
|
-
newEntry.b = processedEntry.b;
|
|
981
|
-
newEntry.p = 1;
|
|
993
|
+
newEntry = { ...newEntry, ...processedEntry };
|
|
982
994
|
}
|
|
983
995
|
await uploadOriginalToCdn(newKey);
|
|
984
996
|
if (hasProcessedThumbnails) {
|
|
@@ -992,7 +1004,7 @@ async function handleMoveStream(request) {
|
|
|
992
1004
|
if (hasProcessedThumbnails) {
|
|
993
1005
|
await deleteLocalThumbnails(newKey);
|
|
994
1006
|
}
|
|
995
|
-
newEntry.c = _optionalChain([entry, 'optionalAccess',
|
|
1007
|
+
newEntry.c = _optionalChain([entry, 'optionalAccess', _23 => _23.c]);
|
|
996
1008
|
delete meta[oldKey];
|
|
997
1009
|
meta[newKey] = newEntry;
|
|
998
1010
|
moved.push(itemPath);
|
|
@@ -1017,8 +1029,8 @@ async function handleMoveStream(request) {
|
|
|
1017
1029
|
await _fs.promises.rename(absolutePath, newAbsolutePath);
|
|
1018
1030
|
const stats = await _fs.promises.stat(newAbsolutePath);
|
|
1019
1031
|
if (stats.isFile() && isImage && entry) {
|
|
1020
|
-
const oldThumbPaths =
|
|
1021
|
-
const newThumbPaths =
|
|
1032
|
+
const oldThumbPaths = _chunkVSOTEQ7Xjs.getAllThumbnailPaths.call(void 0, oldKey);
|
|
1033
|
+
const newThumbPaths = _chunkVSOTEQ7Xjs.getAllThumbnailPaths.call(void 0, newKey);
|
|
1022
1034
|
for (let j = 0; j < oldThumbPaths.length; j++) {
|
|
1023
1035
|
const oldThumbPath = _path2.default.join(process.cwd(), "public", oldThumbPaths[j]);
|
|
1024
1036
|
const newThumbPath = _path2.default.join(process.cwd(), "public", newThumbPaths[j]);
|
|
@@ -1082,7 +1094,7 @@ async function handleSync(request) {
|
|
|
1082
1094
|
const accessKeyId = process.env.CLOUDFLARE_R2_ACCESS_KEY_ID;
|
|
1083
1095
|
const secretAccessKey = process.env.CLOUDFLARE_R2_SECRET_ACCESS_KEY;
|
|
1084
1096
|
const bucketName = process.env.CLOUDFLARE_R2_BUCKET_NAME;
|
|
1085
|
-
const publicUrl = _optionalChain([process, 'access',
|
|
1097
|
+
const publicUrl = _optionalChain([process, 'access', _24 => _24.env, 'access', _25 => _25.CLOUDFLARE_R2_PUBLIC_URL, 'optionalAccess', _26 => _26.replace, 'call', _27 => _27(/\/\s*$/, "")]);
|
|
1086
1098
|
if (!accountId || !accessKeyId || !secretAccessKey || !bucketName || !publicUrl) {
|
|
1087
1099
|
return _server.NextResponse.json(
|
|
1088
1100
|
{ error: "R2 not configured. Set CLOUDFLARE_R2_* environment variables." },
|
|
@@ -1144,8 +1156,8 @@ async function handleSync(request) {
|
|
|
1144
1156
|
})
|
|
1145
1157
|
);
|
|
1146
1158
|
urlsToPurge.push(`${publicUrl}${imageKey}`);
|
|
1147
|
-
if (!isRemote && entry
|
|
1148
|
-
for (const thumbPath of
|
|
1159
|
+
if (!isRemote && _chunkVSOTEQ7Xjs.isProcessed.call(void 0, entry)) {
|
|
1160
|
+
for (const thumbPath of _chunkVSOTEQ7Xjs.getAllThumbnailPaths.call(void 0, imageKey)) {
|
|
1149
1161
|
const localPath = _path2.default.join(process.cwd(), "public", thumbPath);
|
|
1150
1162
|
try {
|
|
1151
1163
|
const fileBuffer = await _fs.promises.readFile(localPath);
|
|
@@ -1165,7 +1177,7 @@ async function handleSync(request) {
|
|
|
1165
1177
|
entry.c = cdnIndex;
|
|
1166
1178
|
if (!isRemote) {
|
|
1167
1179
|
const originalLocalPath = _path2.default.join(process.cwd(), "public", imageKey);
|
|
1168
|
-
for (const thumbPath of
|
|
1180
|
+
for (const thumbPath of _chunkVSOTEQ7Xjs.getAllThumbnailPaths.call(void 0, imageKey)) {
|
|
1169
1181
|
const localPath = _path2.default.join(process.cwd(), "public", thumbPath);
|
|
1170
1182
|
try {
|
|
1171
1183
|
await _fs.promises.unlink(localPath);
|
|
@@ -1198,7 +1210,7 @@ async function handleSync(request) {
|
|
|
1198
1210
|
}
|
|
1199
1211
|
}
|
|
1200
1212
|
async function handleReprocess(request) {
|
|
1201
|
-
const publicUrl = _optionalChain([process, 'access',
|
|
1213
|
+
const publicUrl = _optionalChain([process, 'access', _28 => _28.env, 'access', _29 => _29.CLOUDFLARE_R2_PUBLIC_URL, 'optionalAccess', _30 => _30.replace, 'call', _31 => _31(/\/\s*$/, "")]);
|
|
1202
1214
|
try {
|
|
1203
1215
|
const { imageKeys } = await request.json();
|
|
1204
1216
|
if (!imageKeys || !Array.isArray(imageKeys) || imageKeys.length === 0) {
|
|
@@ -1216,7 +1228,7 @@ async function handleReprocess(request) {
|
|
|
1216
1228
|
try {
|
|
1217
1229
|
let buffer;
|
|
1218
1230
|
const entry = getMetaEntry(meta, imageKey);
|
|
1219
|
-
const existingCdnIndex = _optionalChain([entry, 'optionalAccess',
|
|
1231
|
+
const existingCdnIndex = _optionalChain([entry, 'optionalAccess', _32 => _32.c]);
|
|
1220
1232
|
const existingCdnUrl = existingCdnIndex !== void 0 ? cdnUrls[existingCdnIndex] : void 0;
|
|
1221
1233
|
const isInOurR2 = existingCdnUrl === publicUrl;
|
|
1222
1234
|
const isRemote = existingCdnIndex !== void 0 && !isInOurR2;
|
|
@@ -1240,11 +1252,10 @@ async function handleReprocess(request) {
|
|
|
1240
1252
|
}
|
|
1241
1253
|
}
|
|
1242
1254
|
const updatedEntry = await processImage(buffer, imageKey);
|
|
1243
|
-
updatedEntry.p = 1;
|
|
1244
1255
|
if (isInOurR2) {
|
|
1245
1256
|
updatedEntry.c = existingCdnIndex;
|
|
1246
1257
|
await uploadToCdn(imageKey);
|
|
1247
|
-
for (const thumbPath of
|
|
1258
|
+
for (const thumbPath of _chunkVSOTEQ7Xjs.getAllThumbnailPaths.call(void 0, imageKey)) {
|
|
1248
1259
|
urlsToPurge.push(`${publicUrl}${thumbPath}`);
|
|
1249
1260
|
}
|
|
1250
1261
|
await deleteLocalThumbnails(imageKey);
|
|
@@ -1276,7 +1287,7 @@ async function handleReprocess(request) {
|
|
|
1276
1287
|
}
|
|
1277
1288
|
}
|
|
1278
1289
|
async function handleProcessAllStream() {
|
|
1279
|
-
const publicUrl = _optionalChain([process, 'access',
|
|
1290
|
+
const publicUrl = _optionalChain([process, 'access', _33 => _33.env, 'access', _34 => _34.CLOUDFLARE_R2_PUBLIC_URL, 'optionalAccess', _35 => _35.replace, 'call', _36 => _36(/\/\s*$/, "")]);
|
|
1280
1291
|
const encoder = new TextEncoder();
|
|
1281
1292
|
const stream = new ReadableStream({
|
|
1282
1293
|
async start(controller) {
|
|
@@ -1297,7 +1308,7 @@ async function handleProcessAllStream() {
|
|
|
1297
1308
|
for (const [key, entry] of getFileEntries(meta)) {
|
|
1298
1309
|
const fileName = _path2.default.basename(key);
|
|
1299
1310
|
if (!isImageFile(fileName)) continue;
|
|
1300
|
-
if (!entry
|
|
1311
|
+
if (!_chunkVSOTEQ7Xjs.isProcessed.call(void 0, entry)) {
|
|
1301
1312
|
imagesToProcess.push({ key, entry });
|
|
1302
1313
|
} else {
|
|
1303
1314
|
alreadyProcessed++;
|
|
@@ -1347,10 +1358,10 @@ async function handleProcessAllStream() {
|
|
|
1347
1358
|
await _fs.promises.writeFile(destPath, buffer);
|
|
1348
1359
|
meta[key] = {
|
|
1349
1360
|
...entry,
|
|
1350
|
-
|
|
1351
|
-
h: 0,
|
|
1361
|
+
o: [0, 0],
|
|
1352
1362
|
b: "",
|
|
1353
|
-
|
|
1363
|
+
f: [0, 0]
|
|
1364
|
+
// SVG has "full" to indicate processed
|
|
1354
1365
|
};
|
|
1355
1366
|
if (isRemote) {
|
|
1356
1367
|
delete meta[key].c;
|
|
@@ -1359,13 +1370,12 @@ async function handleProcessAllStream() {
|
|
|
1359
1370
|
const processedEntry = await processImage(buffer, key);
|
|
1360
1371
|
meta[key] = {
|
|
1361
1372
|
...processedEntry,
|
|
1362
|
-
p: 1,
|
|
1363
1373
|
...isInOurR2 ? { c: existingCdnIndex } : {}
|
|
1364
1374
|
};
|
|
1365
1375
|
}
|
|
1366
1376
|
if (isInOurR2) {
|
|
1367
1377
|
await uploadToCdn(key);
|
|
1368
|
-
for (const thumbPath of
|
|
1378
|
+
for (const thumbPath of _chunkVSOTEQ7Xjs.getAllThumbnailPaths.call(void 0, key)) {
|
|
1369
1379
|
urlsToPurge.push(`${publicUrl}${thumbPath}`);
|
|
1370
1380
|
}
|
|
1371
1381
|
await deleteLocalThumbnails(key);
|
|
@@ -1384,7 +1394,7 @@ async function handleProcessAllStream() {
|
|
|
1384
1394
|
const trackedPaths = /* @__PURE__ */ new Set();
|
|
1385
1395
|
for (const [imageKey, entry] of getFileEntries(meta)) {
|
|
1386
1396
|
if (entry.c === void 0) {
|
|
1387
|
-
for (const thumbPath of
|
|
1397
|
+
for (const thumbPath of _chunkVSOTEQ7Xjs.getAllThumbnailPaths.call(void 0, imageKey)) {
|
|
1388
1398
|
trackedPaths.add(thumbPath);
|
|
1389
1399
|
}
|
|
1390
1400
|
}
|
|
@@ -1556,7 +1566,7 @@ async function handleScanStream() {
|
|
|
1556
1566
|
if (isImage) {
|
|
1557
1567
|
const ext = _path2.default.extname(relativePath).toLowerCase();
|
|
1558
1568
|
if (ext === ".svg") {
|
|
1559
|
-
meta[imageKey] = {
|
|
1569
|
+
meta[imageKey] = { o: [0, 0], b: "" };
|
|
1560
1570
|
} else {
|
|
1561
1571
|
try {
|
|
1562
1572
|
const buffer = await _fs.promises.readFile(fullPath);
|
|
@@ -1564,12 +1574,11 @@ async function handleScanStream() {
|
|
|
1564
1574
|
const { data, info } = await _sharp2.default.call(void 0, buffer).resize(32, 32, { fit: "inside" }).ensureAlpha().raw().toBuffer({ resolveWithObject: true });
|
|
1565
1575
|
const blurhash = _blurhash.encode.call(void 0, new Uint8ClampedArray(data), info.width, info.height, 4, 4);
|
|
1566
1576
|
meta[imageKey] = {
|
|
1567
|
-
|
|
1568
|
-
h: metadata.height || 0,
|
|
1577
|
+
o: [metadata.width || 0, metadata.height || 0],
|
|
1569
1578
|
b: blurhash
|
|
1570
1579
|
};
|
|
1571
1580
|
} catch (e37) {
|
|
1572
|
-
meta[imageKey] = {
|
|
1581
|
+
meta[imageKey] = { o: [0, 0] };
|
|
1573
1582
|
}
|
|
1574
1583
|
}
|
|
1575
1584
|
} else {
|
|
@@ -1586,8 +1595,8 @@ async function handleScanStream() {
|
|
|
1586
1595
|
const expectedThumbnails = /* @__PURE__ */ new Set();
|
|
1587
1596
|
const fileEntries = getFileEntries(meta);
|
|
1588
1597
|
for (const [imageKey, entry] of fileEntries) {
|
|
1589
|
-
if (entry.c === void 0 &&
|
|
1590
|
-
for (const thumbPath of
|
|
1598
|
+
if (entry.c === void 0 && _chunkVSOTEQ7Xjs.isProcessed.call(void 0, entry)) {
|
|
1599
|
+
for (const thumbPath of _chunkVSOTEQ7Xjs.getAllThumbnailPaths.call(void 0, imageKey)) {
|
|
1591
1600
|
expectedThumbnails.add(thumbPath);
|
|
1592
1601
|
}
|
|
1593
1602
|
}
|
|
@@ -1719,8 +1728,7 @@ async function processRemoteImage(url) {
|
|
|
1719
1728
|
const { data, info } = await _sharp2.default.call(void 0, buffer).resize(32, 32, { fit: "inside" }).ensureAlpha().raw().toBuffer({ resolveWithObject: true });
|
|
1720
1729
|
const blurhash = _blurhash.encode.call(void 0, new Uint8ClampedArray(data), info.width, info.height, 4, 4);
|
|
1721
1730
|
return {
|
|
1722
|
-
|
|
1723
|
-
h: metadata.height || 0,
|
|
1731
|
+
o: [metadata.width || 0, metadata.height || 0],
|
|
1724
1732
|
b: blurhash
|
|
1725
1733
|
};
|
|
1726
1734
|
}
|
|
@@ -1766,8 +1774,7 @@ async function handleImportUrls(request) {
|
|
|
1766
1774
|
const cdnIndex = getOrAddCdnIndex(meta, base);
|
|
1767
1775
|
const imageData = await processRemoteImage(url);
|
|
1768
1776
|
setMetaEntry(meta, path9, {
|
|
1769
|
-
|
|
1770
|
-
h: imageData.h,
|
|
1777
|
+
o: imageData.o,
|
|
1771
1778
|
b: imageData.b,
|
|
1772
1779
|
c: cdnIndex
|
|
1773
1780
|
});
|