@gallop.software/studio 0.1.80 → 0.1.82
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/{StudioUI-VPNL5NMI.mjs → StudioUI-VJVOSOPD.mjs} +485 -402
- package/dist/StudioUI-VJVOSOPD.mjs.map +1 -0
- package/dist/{StudioUI-OAZ7CTB4.js → StudioUI-YFDO5MGG.js} +420 -337
- package/dist/StudioUI-YFDO5MGG.js.map +1 -0
- package/dist/{handlers.d.ts → handlers/index.d.mts} +1 -1
- package/dist/{handlers.d.mts → handlers/index.d.ts} +1 -1
- package/dist/{handlers.js → handlers/index.js} +611 -579
- package/dist/handlers/index.js.map +1 -0
- package/dist/{handlers.mjs → handlers/index.mjs} +620 -588
- package/dist/handlers/index.mjs.map +1 -0
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +4 -4
- package/dist/StudioUI-OAZ7CTB4.js.map +0 -1
- package/dist/StudioUI-VPNL5NMI.mjs.map +0 -1
- package/dist/handlers.js.map +0 -1
- package/dist/handlers.mjs.map +0 -1
|
@@ -1,80 +1,212 @@
|
|
|
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
|
-
var _chunkCN5NRNWBjs = require('
|
|
3
|
+
var _chunkCN5NRNWBjs = require('../chunk-CN5NRNWB.js');
|
|
4
4
|
|
|
5
|
-
// src/handlers.ts
|
|
5
|
+
// src/handlers/index.ts
|
|
6
6
|
var _server = require('next/server');
|
|
7
|
+
|
|
8
|
+
// src/handlers/list.ts
|
|
9
|
+
|
|
7
10
|
var _fs = require('fs');
|
|
8
11
|
var _path = require('path'); var _path2 = _interopRequireDefault(_path);
|
|
9
12
|
var _sharp = require('sharp'); var _sharp2 = _interopRequireDefault(_sharp);
|
|
13
|
+
|
|
14
|
+
// src/handlers/utils/meta.ts
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
async function loadMeta() {
|
|
18
|
+
const metaPath = _path2.default.join(process.cwd(), "_data", "_meta.json");
|
|
19
|
+
try {
|
|
20
|
+
const content = await _fs.promises.readFile(metaPath, "utf-8");
|
|
21
|
+
return JSON.parse(content);
|
|
22
|
+
} catch (e) {
|
|
23
|
+
return {};
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
async function saveMeta(meta) {
|
|
27
|
+
const dataDir = _path2.default.join(process.cwd(), "_data");
|
|
28
|
+
await _fs.promises.mkdir(dataDir, { recursive: true });
|
|
29
|
+
const metaPath = _path2.default.join(dataDir, "_meta.json");
|
|
30
|
+
await _fs.promises.writeFile(metaPath, JSON.stringify(meta, null, 2));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// src/handlers/utils/files.ts
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
function isImageFile(filename) {
|
|
37
|
+
const ext = _path2.default.extname(filename).toLowerCase();
|
|
38
|
+
return [".jpg", ".jpeg", ".png", ".gif", ".webp", ".svg", ".ico", ".bmp", ".tiff", ".tif"].includes(ext);
|
|
39
|
+
}
|
|
40
|
+
function isMediaFile(filename) {
|
|
41
|
+
const ext = _path2.default.extname(filename).toLowerCase();
|
|
42
|
+
if ([".jpg", ".jpeg", ".png", ".gif", ".webp", ".svg", ".ico", ".bmp", ".tiff", ".tif"].includes(ext)) return true;
|
|
43
|
+
if ([".mp4", ".webm", ".mov", ".avi", ".mkv", ".m4v"].includes(ext)) return true;
|
|
44
|
+
if ([".mp3", ".wav", ".ogg", ".m4a", ".flac", ".aac"].includes(ext)) return true;
|
|
45
|
+
if ([".pdf"].includes(ext)) return true;
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
function getContentType(filePath) {
|
|
49
|
+
const ext = _path2.default.extname(filePath).toLowerCase();
|
|
50
|
+
switch (ext) {
|
|
51
|
+
case ".jpg":
|
|
52
|
+
case ".jpeg":
|
|
53
|
+
return "image/jpeg";
|
|
54
|
+
case ".png":
|
|
55
|
+
return "image/png";
|
|
56
|
+
case ".gif":
|
|
57
|
+
return "image/gif";
|
|
58
|
+
case ".webp":
|
|
59
|
+
return "image/webp";
|
|
60
|
+
case ".svg":
|
|
61
|
+
return "image/svg+xml";
|
|
62
|
+
default:
|
|
63
|
+
return "application/octet-stream";
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async function getFolderStats(folderPath) {
|
|
67
|
+
let fileCount = 0;
|
|
68
|
+
let totalSize = 0;
|
|
69
|
+
async function scanFolder(dir) {
|
|
70
|
+
try {
|
|
71
|
+
const entries = await _fs.promises.readdir(dir, { withFileTypes: true });
|
|
72
|
+
for (const entry of entries) {
|
|
73
|
+
if (entry.name.startsWith(".")) continue;
|
|
74
|
+
const fullPath = _path2.default.join(dir, entry.name);
|
|
75
|
+
if (entry.isDirectory()) {
|
|
76
|
+
await scanFolder(fullPath);
|
|
77
|
+
} else if (isMediaFile(entry.name)) {
|
|
78
|
+
fileCount++;
|
|
79
|
+
const stats = await _fs.promises.stat(fullPath);
|
|
80
|
+
totalSize += stats.size;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
} catch (e2) {
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
await scanFolder(folderPath);
|
|
87
|
+
return { fileCount, totalSize };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// src/handlers/utils/thumbnails.ts
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
|
|
10
94
|
var _blurhash = require('blurhash');
|
|
11
|
-
var _clients3 = require('@aws-sdk/client-s3');
|
|
12
95
|
var DEFAULT_SIZES = {
|
|
13
96
|
small: { width: 300, suffix: "-sm" },
|
|
14
97
|
medium: { width: 700, suffix: "-md" },
|
|
15
98
|
large: { width: 1400, suffix: "-lg" }
|
|
16
99
|
};
|
|
17
|
-
async function
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
if (
|
|
33
|
-
|
|
100
|
+
async function processImage(buffer, imageKey) {
|
|
101
|
+
const sharpInstance = _sharp2.default.call(void 0, buffer);
|
|
102
|
+
const metadata = await sharpInstance.metadata();
|
|
103
|
+
const originalWidth = metadata.width || 0;
|
|
104
|
+
const originalHeight = metadata.height || 0;
|
|
105
|
+
const keyWithoutSlash = imageKey.startsWith("/") ? imageKey.slice(1) : imageKey;
|
|
106
|
+
const baseName = _path2.default.basename(keyWithoutSlash, _path2.default.extname(keyWithoutSlash));
|
|
107
|
+
const ext = _path2.default.extname(keyWithoutSlash).toLowerCase();
|
|
108
|
+
const imageDir = _path2.default.dirname(keyWithoutSlash);
|
|
109
|
+
const imagesPath = _path2.default.join(process.cwd(), "public", "images", imageDir === "." ? "" : imageDir);
|
|
110
|
+
await _fs.promises.mkdir(imagesPath, { recursive: true });
|
|
111
|
+
const isPng = ext === ".png";
|
|
112
|
+
const outputExt = isPng ? ".png" : ".jpg";
|
|
113
|
+
const fullFileName = imageDir === "." ? `${baseName}${outputExt}` : `${imageDir}/${baseName}${outputExt}`;
|
|
114
|
+
const fullPath = _path2.default.join(process.cwd(), "public", "images", fullFileName);
|
|
115
|
+
if (isPng) {
|
|
116
|
+
await _sharp2.default.call(void 0, buffer).png({ quality: 85 }).toFile(fullPath);
|
|
117
|
+
} else {
|
|
118
|
+
await _sharp2.default.call(void 0, buffer).jpeg({ quality: 85 }).toFile(fullPath);
|
|
34
119
|
}
|
|
35
|
-
|
|
36
|
-
|
|
120
|
+
for (const [, sizeConfig] of Object.entries(DEFAULT_SIZES)) {
|
|
121
|
+
const { width: maxWidth, suffix } = sizeConfig;
|
|
122
|
+
if (originalWidth <= maxWidth) {
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
const ratio = originalHeight / originalWidth;
|
|
126
|
+
const newHeight = Math.round(maxWidth * ratio);
|
|
127
|
+
const sizeFileName = `${baseName}${suffix}${outputExt}`;
|
|
128
|
+
const sizeFilePath = imageDir === "." ? sizeFileName : `${imageDir}/${sizeFileName}`;
|
|
129
|
+
const sizePath = _path2.default.join(process.cwd(), "public", "images", sizeFilePath);
|
|
130
|
+
if (isPng) {
|
|
131
|
+
await _sharp2.default.call(void 0, buffer).resize(maxWidth, newHeight).png({ quality: 80 }).toFile(sizePath);
|
|
132
|
+
} else {
|
|
133
|
+
await _sharp2.default.call(void 0, buffer).resize(maxWidth, newHeight).jpeg({ quality: 80 }).toFile(sizePath);
|
|
134
|
+
}
|
|
37
135
|
}
|
|
38
|
-
|
|
136
|
+
const { data, info } = await _sharp2.default.call(void 0, buffer).resize(32, 32, { fit: "inside" }).ensureAlpha().raw().toBuffer({ resolveWithObject: true });
|
|
137
|
+
const blurhash = _blurhash.encode.call(void 0, new Uint8ClampedArray(data), info.width, info.height, 4, 4);
|
|
138
|
+
return {
|
|
139
|
+
w: originalWidth,
|
|
140
|
+
h: originalHeight,
|
|
141
|
+
blur: blurhash
|
|
142
|
+
};
|
|
39
143
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if (
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
if (route === "sync") {
|
|
53
|
-
return handleSync(request);
|
|
54
|
-
}
|
|
55
|
-
if (route === "reprocess") {
|
|
56
|
-
return handleReprocess(request);
|
|
57
|
-
}
|
|
58
|
-
if (route === "process-all") {
|
|
59
|
-
return handleProcessAllStream();
|
|
60
|
-
}
|
|
61
|
-
if (route === "create-folder") {
|
|
62
|
-
return handleCreateFolder(request);
|
|
144
|
+
|
|
145
|
+
// src/handlers/utils/cdn.ts
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
var _clients3 = require('@aws-sdk/client-s3');
|
|
149
|
+
function getR2Client() {
|
|
150
|
+
const accountId = process.env.CLOUDFLARE_R2_ACCOUNT_ID;
|
|
151
|
+
const accessKeyId = process.env.CLOUDFLARE_R2_ACCESS_KEY_ID;
|
|
152
|
+
const secretAccessKey = process.env.CLOUDFLARE_R2_SECRET_ACCESS_KEY;
|
|
153
|
+
if (!accountId || !accessKeyId || !secretAccessKey) {
|
|
154
|
+
throw new Error("R2 not configured");
|
|
63
155
|
}
|
|
64
|
-
|
|
65
|
-
|
|
156
|
+
return new (0, _clients3.S3Client)({
|
|
157
|
+
region: "auto",
|
|
158
|
+
endpoint: `https://${accountId}.r2.cloudflarestorage.com`,
|
|
159
|
+
credentials: { accessKeyId, secretAccessKey }
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
async function downloadFromCdn(originalPath) {
|
|
163
|
+
const bucketName = process.env.CLOUDFLARE_R2_BUCKET_NAME;
|
|
164
|
+
if (!bucketName) throw new Error("R2 bucket not configured");
|
|
165
|
+
const r2 = getR2Client();
|
|
166
|
+
const response = await r2.send(
|
|
167
|
+
new (0, _clients3.GetObjectCommand)({
|
|
168
|
+
Bucket: bucketName,
|
|
169
|
+
Key: originalPath.replace(/^\//, "")
|
|
170
|
+
})
|
|
171
|
+
);
|
|
172
|
+
const stream = response.Body;
|
|
173
|
+
const chunks = [];
|
|
174
|
+
for await (const chunk of stream) {
|
|
175
|
+
chunks.push(Buffer.from(chunk));
|
|
66
176
|
}
|
|
67
|
-
|
|
68
|
-
|
|
177
|
+
return Buffer.concat(chunks);
|
|
178
|
+
}
|
|
179
|
+
async function uploadToCdn(imageKey) {
|
|
180
|
+
const bucketName = process.env.CLOUDFLARE_R2_BUCKET_NAME;
|
|
181
|
+
if (!bucketName) throw new Error("R2 bucket not configured");
|
|
182
|
+
const r2 = getR2Client();
|
|
183
|
+
for (const thumbPath of _chunkCN5NRNWBjs.getAllThumbnailPaths.call(void 0, imageKey)) {
|
|
184
|
+
const localPath = _path2.default.join(process.cwd(), "public", thumbPath);
|
|
185
|
+
try {
|
|
186
|
+
const fileBuffer = await _fs.promises.readFile(localPath);
|
|
187
|
+
await r2.send(
|
|
188
|
+
new (0, _clients3.PutObjectCommand)({
|
|
189
|
+
Bucket: bucketName,
|
|
190
|
+
Key: thumbPath.replace(/^\//, ""),
|
|
191
|
+
Body: fileBuffer,
|
|
192
|
+
ContentType: getContentType(thumbPath)
|
|
193
|
+
})
|
|
194
|
+
);
|
|
195
|
+
} catch (e3) {
|
|
196
|
+
}
|
|
69
197
|
}
|
|
70
|
-
return _server.NextResponse.json({ error: "Not found" }, { status: 404 });
|
|
71
198
|
}
|
|
72
|
-
async function
|
|
73
|
-
|
|
74
|
-
|
|
199
|
+
async function deleteLocalThumbnails(imageKey) {
|
|
200
|
+
for (const thumbPath of _chunkCN5NRNWBjs.getAllThumbnailPaths.call(void 0, imageKey)) {
|
|
201
|
+
const localPath = _path2.default.join(process.cwd(), "public", thumbPath);
|
|
202
|
+
try {
|
|
203
|
+
await _fs.promises.unlink(localPath);
|
|
204
|
+
} catch (e4) {
|
|
205
|
+
}
|
|
75
206
|
}
|
|
76
|
-
return handleDelete(request);
|
|
77
207
|
}
|
|
208
|
+
|
|
209
|
+
// src/handlers/list.ts
|
|
78
210
|
async function handleList(request) {
|
|
79
211
|
const searchParams = request.nextUrl.searchParams;
|
|
80
212
|
const requestedPath = searchParams.get("path") || "public";
|
|
@@ -120,7 +252,7 @@ async function handleList(request) {
|
|
|
120
252
|
await _fs.promises.access(thumbnailPath);
|
|
121
253
|
thumbnail = `/${thumbnailDir}/${thumbnailName}`;
|
|
122
254
|
hasThumbnail = true;
|
|
123
|
-
} catch (
|
|
255
|
+
} catch (e5) {
|
|
124
256
|
thumbnail = itemPath.replace("public", "");
|
|
125
257
|
hasThumbnail = false;
|
|
126
258
|
}
|
|
@@ -131,7 +263,7 @@ async function handleList(request) {
|
|
|
131
263
|
if (metadata.width && metadata.height) {
|
|
132
264
|
dimensions = { width: metadata.width, height: metadata.height };
|
|
133
265
|
}
|
|
134
|
-
} catch (
|
|
266
|
+
} catch (e6) {
|
|
135
267
|
}
|
|
136
268
|
}
|
|
137
269
|
}
|
|
@@ -186,7 +318,7 @@ async function handleSearch(request) {
|
|
|
186
318
|
await _fs.promises.access(thumbnailPath);
|
|
187
319
|
thumbnail = `/${thumbnailDir}/${thumbnailName}`;
|
|
188
320
|
hasThumbnail = true;
|
|
189
|
-
} catch (
|
|
321
|
+
} catch (e7) {
|
|
190
322
|
thumbnail = `/${itemRelPath}`;
|
|
191
323
|
hasThumbnail = false;
|
|
192
324
|
}
|
|
@@ -196,7 +328,7 @@ async function handleSearch(request) {
|
|
|
196
328
|
if (metadata.width && metadata.height) {
|
|
197
329
|
dimensions = { width: metadata.width, height: metadata.height };
|
|
198
330
|
}
|
|
199
|
-
} catch (
|
|
331
|
+
} catch (e8) {
|
|
200
332
|
}
|
|
201
333
|
}
|
|
202
334
|
items.push({
|
|
@@ -211,7 +343,7 @@ async function handleSearch(request) {
|
|
|
211
343
|
}
|
|
212
344
|
}
|
|
213
345
|
}
|
|
214
|
-
} catch (
|
|
346
|
+
} catch (e9) {
|
|
215
347
|
}
|
|
216
348
|
}
|
|
217
349
|
await searchDir(publicDir, "");
|
|
@@ -221,29 +353,113 @@ async function handleSearch(request) {
|
|
|
221
353
|
return _server.NextResponse.json({ error: "Failed to search" }, { status: 500 });
|
|
222
354
|
}
|
|
223
355
|
}
|
|
224
|
-
async function
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
356
|
+
async function handleListFolders() {
|
|
357
|
+
try {
|
|
358
|
+
const publicDir = _path2.default.join(process.cwd(), "public");
|
|
359
|
+
const folders = [];
|
|
360
|
+
async function scanDir(dir, relativePath, depth) {
|
|
361
|
+
try {
|
|
362
|
+
const entries = await _fs.promises.readdir(dir, { withFileTypes: true });
|
|
363
|
+
for (const entry of entries) {
|
|
364
|
+
if (!entry.isDirectory()) continue;
|
|
365
|
+
if (entry.name.startsWith(".")) continue;
|
|
366
|
+
const folderRelativePath = relativePath ? `${relativePath}/${entry.name}` : entry.name;
|
|
367
|
+
folders.push({
|
|
368
|
+
path: `public/${folderRelativePath}`,
|
|
369
|
+
name: entry.name,
|
|
370
|
+
depth
|
|
371
|
+
});
|
|
372
|
+
await scanDir(_path2.default.join(dir, entry.name), folderRelativePath, depth + 1);
|
|
239
373
|
}
|
|
374
|
+
} catch (e10) {
|
|
240
375
|
}
|
|
241
|
-
} catch (e6) {
|
|
242
376
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
377
|
+
folders.push({ path: "public", name: "public", depth: 0 });
|
|
378
|
+
await scanDir(publicDir, "", 1);
|
|
379
|
+
return _server.NextResponse.json({ folders });
|
|
380
|
+
} catch (error) {
|
|
381
|
+
console.error("Failed to list folders:", error);
|
|
382
|
+
return _server.NextResponse.json({ error: "Failed to list folders" }, { status: 500 });
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
async function handleCountImages() {
|
|
386
|
+
try {
|
|
387
|
+
const allImages = [];
|
|
388
|
+
async function scanPublicFolder(dir, relativePath = "") {
|
|
389
|
+
try {
|
|
390
|
+
const entries = await _fs.promises.readdir(dir, { withFileTypes: true });
|
|
391
|
+
for (const entry of entries) {
|
|
392
|
+
if (entry.name.startsWith(".")) continue;
|
|
393
|
+
const fullPath = _path2.default.join(dir, entry.name);
|
|
394
|
+
const relPath = relativePath ? `${relativePath}/${entry.name}` : entry.name;
|
|
395
|
+
if (relPath === "images" || relPath.startsWith("images/")) continue;
|
|
396
|
+
if (entry.isDirectory()) {
|
|
397
|
+
await scanPublicFolder(fullPath, relPath);
|
|
398
|
+
} else if (isImageFile(entry.name)) {
|
|
399
|
+
allImages.push(relPath);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
} catch (e11) {
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
const publicDir = _path2.default.join(process.cwd(), "public");
|
|
406
|
+
await scanPublicFolder(publicDir);
|
|
407
|
+
return _server.NextResponse.json({
|
|
408
|
+
count: allImages.length,
|
|
409
|
+
images: allImages
|
|
410
|
+
});
|
|
411
|
+
} catch (error) {
|
|
412
|
+
console.error("Failed to count images:", error);
|
|
413
|
+
return _server.NextResponse.json({ error: "Failed to count images" }, { status: 500 });
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
async function handleFolderImages(request) {
|
|
417
|
+
try {
|
|
418
|
+
const searchParams = request.nextUrl.searchParams;
|
|
419
|
+
const foldersParam = searchParams.get("folders");
|
|
420
|
+
if (!foldersParam) {
|
|
421
|
+
return _server.NextResponse.json({ error: "No folders provided" }, { status: 400 });
|
|
422
|
+
}
|
|
423
|
+
const folders = foldersParam.split(",");
|
|
424
|
+
const allImages = [];
|
|
425
|
+
async function scanFolder(dir, relativePath = "") {
|
|
426
|
+
try {
|
|
427
|
+
const entries = await _fs.promises.readdir(dir, { withFileTypes: true });
|
|
428
|
+
for (const entry of entries) {
|
|
429
|
+
if (entry.name.startsWith(".")) continue;
|
|
430
|
+
const fullPath = _path2.default.join(dir, entry.name);
|
|
431
|
+
const relPath = relativePath ? `${relativePath}/${entry.name}` : entry.name;
|
|
432
|
+
if (entry.isDirectory()) {
|
|
433
|
+
await scanFolder(fullPath, relPath);
|
|
434
|
+
} else if (isImageFile(entry.name)) {
|
|
435
|
+
allImages.push(relPath);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
} catch (e12) {
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
for (const folder of folders) {
|
|
442
|
+
const relativePath = folder.replace(/^public\/?/, "");
|
|
443
|
+
if (relativePath === "images" || relativePath.startsWith("images/")) continue;
|
|
444
|
+
const folderPath = _path2.default.join(process.cwd(), folder);
|
|
445
|
+
await scanFolder(folderPath, relativePath);
|
|
446
|
+
}
|
|
447
|
+
return _server.NextResponse.json({
|
|
448
|
+
count: allImages.length,
|
|
449
|
+
images: allImages
|
|
450
|
+
});
|
|
451
|
+
} catch (error) {
|
|
452
|
+
console.error("Failed to get folder images:", error);
|
|
453
|
+
return _server.NextResponse.json({ error: "Failed to get folder images" }, { status: 500 });
|
|
454
|
+
}
|
|
246
455
|
}
|
|
456
|
+
|
|
457
|
+
// src/handlers/files.ts
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
|
|
247
463
|
async function handleUpload(request) {
|
|
248
464
|
try {
|
|
249
465
|
const formData = await request.formData();
|
|
@@ -379,7 +595,7 @@ async function handleDelete(request) {
|
|
|
379
595
|
const absoluteThumbPath = _path2.default.join(process.cwd(), "public", thumbPath);
|
|
380
596
|
try {
|
|
381
597
|
await _fs.promises.unlink(absoluteThumbPath);
|
|
382
|
-
} catch (
|
|
598
|
+
} catch (e13) {
|
|
383
599
|
}
|
|
384
600
|
}
|
|
385
601
|
delete meta[imageKey];
|
|
@@ -403,198 +619,313 @@ async function handleDelete(request) {
|
|
|
403
619
|
return _server.NextResponse.json({ error: "Failed to delete files" }, { status: 500 });
|
|
404
620
|
}
|
|
405
621
|
}
|
|
406
|
-
async function
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
{ error: "
|
|
415
|
-
|
|
416
|
-
);
|
|
622
|
+
async function handleCreateFolder(request) {
|
|
623
|
+
try {
|
|
624
|
+
const { parentPath, name } = await request.json();
|
|
625
|
+
if (!name || typeof name !== "string") {
|
|
626
|
+
return _server.NextResponse.json({ error: "Folder name is required" }, { status: 400 });
|
|
627
|
+
}
|
|
628
|
+
const sanitizedName = name.replace(/[<>:"/\\|?*]/g, "").trim();
|
|
629
|
+
if (!sanitizedName) {
|
|
630
|
+
return _server.NextResponse.json({ error: "Invalid folder name" }, { status: 400 });
|
|
631
|
+
}
|
|
632
|
+
const safePath = (parentPath || "public").replace(/\.\./g, "");
|
|
633
|
+
const folderPath = _path2.default.join(process.cwd(), safePath, sanitizedName);
|
|
634
|
+
if (!folderPath.startsWith(_path2.default.join(process.cwd(), "public"))) {
|
|
635
|
+
return _server.NextResponse.json({ error: "Invalid path" }, { status: 400 });
|
|
636
|
+
}
|
|
637
|
+
try {
|
|
638
|
+
await _fs.promises.access(folderPath);
|
|
639
|
+
return _server.NextResponse.json({ error: "A folder with this name already exists" }, { status: 400 });
|
|
640
|
+
} catch (e14) {
|
|
641
|
+
}
|
|
642
|
+
await _fs.promises.mkdir(folderPath, { recursive: true });
|
|
643
|
+
return _server.NextResponse.json({ success: true, path: _path2.default.join(safePath, sanitizedName) });
|
|
644
|
+
} catch (error) {
|
|
645
|
+
console.error("Failed to create folder:", error);
|
|
646
|
+
return _server.NextResponse.json({ error: "Failed to create folder" }, { status: 500 });
|
|
417
647
|
}
|
|
648
|
+
}
|
|
649
|
+
async function handleRename(request) {
|
|
418
650
|
try {
|
|
419
|
-
const {
|
|
420
|
-
if (!
|
|
421
|
-
return _server.NextResponse.json({ error: "
|
|
651
|
+
const { oldPath, newName } = await request.json();
|
|
652
|
+
if (!oldPath || !newName) {
|
|
653
|
+
return _server.NextResponse.json({ error: "Path and new name are required" }, { status: 400 });
|
|
422
654
|
}
|
|
423
|
-
const
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
const
|
|
430
|
-
const
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
for (
|
|
459
|
-
const
|
|
655
|
+
const sanitizedName = newName.replace(/[<>:"/\\|?*]/g, "").trim();
|
|
656
|
+
if (!sanitizedName) {
|
|
657
|
+
return _server.NextResponse.json({ error: "Invalid name" }, { status: 400 });
|
|
658
|
+
}
|
|
659
|
+
const safePath = oldPath.replace(/\.\./g, "");
|
|
660
|
+
const absoluteOldPath = _path2.default.join(process.cwd(), safePath);
|
|
661
|
+
const parentDir = _path2.default.dirname(absoluteOldPath);
|
|
662
|
+
const absoluteNewPath = _path2.default.join(parentDir, sanitizedName);
|
|
663
|
+
if (!absoluteOldPath.startsWith(_path2.default.join(process.cwd(), "public"))) {
|
|
664
|
+
return _server.NextResponse.json({ error: "Invalid path" }, { status: 400 });
|
|
665
|
+
}
|
|
666
|
+
try {
|
|
667
|
+
await _fs.promises.access(absoluteOldPath);
|
|
668
|
+
} catch (e15) {
|
|
669
|
+
return _server.NextResponse.json({ error: "File or folder not found" }, { status: 404 });
|
|
670
|
+
}
|
|
671
|
+
try {
|
|
672
|
+
await _fs.promises.access(absoluteNewPath);
|
|
673
|
+
return _server.NextResponse.json({ error: "An item with this name already exists" }, { status: 400 });
|
|
674
|
+
} catch (e16) {
|
|
675
|
+
}
|
|
676
|
+
const stats = await _fs.promises.stat(absoluteOldPath);
|
|
677
|
+
const isFile = stats.isFile();
|
|
678
|
+
const isImage = isFile && isImageFile(_path2.default.basename(oldPath));
|
|
679
|
+
await _fs.promises.rename(absoluteOldPath, absoluteNewPath);
|
|
680
|
+
if (isImage) {
|
|
681
|
+
const meta = await loadMeta();
|
|
682
|
+
const oldRelativePath = safePath.replace(/^public\//, "");
|
|
683
|
+
const newRelativePath = _path2.default.join(_path2.default.dirname(oldRelativePath), sanitizedName);
|
|
684
|
+
const oldKey = "/" + oldRelativePath;
|
|
685
|
+
const newKey = "/" + newRelativePath;
|
|
686
|
+
if (meta[oldKey]) {
|
|
687
|
+
const entry = meta[oldKey];
|
|
688
|
+
const oldThumbPaths = _chunkCN5NRNWBjs.getAllThumbnailPaths.call(void 0, oldKey);
|
|
689
|
+
const newThumbPaths = _chunkCN5NRNWBjs.getAllThumbnailPaths.call(void 0, newKey);
|
|
690
|
+
for (let i = 0; i < oldThumbPaths.length; i++) {
|
|
691
|
+
const oldThumbPath = _path2.default.join(process.cwd(), "public", oldThumbPaths[i]);
|
|
692
|
+
const newThumbPath = _path2.default.join(process.cwd(), "public", newThumbPaths[i]);
|
|
693
|
+
await _fs.promises.mkdir(_path2.default.dirname(newThumbPath), { recursive: true });
|
|
460
694
|
try {
|
|
461
|
-
await _fs.promises.
|
|
462
|
-
} catch (
|
|
695
|
+
await _fs.promises.rename(oldThumbPath, newThumbPath);
|
|
696
|
+
} catch (e17) {
|
|
463
697
|
}
|
|
464
698
|
}
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
console.error(`Failed to sync ${imageKey}:`, error);
|
|
468
|
-
errors.push(imageKey);
|
|
699
|
+
delete meta[oldKey];
|
|
700
|
+
meta[newKey] = entry;
|
|
469
701
|
}
|
|
702
|
+
await saveMeta(meta);
|
|
470
703
|
}
|
|
471
|
-
|
|
472
|
-
return _server.NextResponse.json({
|
|
473
|
-
success: true,
|
|
474
|
-
synced,
|
|
475
|
-
errors: errors.length > 0 ? errors : void 0
|
|
476
|
-
});
|
|
704
|
+
const newPath = _path2.default.join(_path2.default.dirname(safePath), sanitizedName);
|
|
705
|
+
return _server.NextResponse.json({ success: true, newPath });
|
|
477
706
|
} catch (error) {
|
|
478
|
-
console.error("Failed to
|
|
479
|
-
return _server.NextResponse.json({ error: "Failed to
|
|
707
|
+
console.error("Failed to rename:", error);
|
|
708
|
+
return _server.NextResponse.json({ error: "Failed to rename" }, { status: 500 });
|
|
480
709
|
}
|
|
481
710
|
}
|
|
482
|
-
async function
|
|
711
|
+
async function handleMove(request) {
|
|
483
712
|
try {
|
|
484
|
-
const {
|
|
485
|
-
if (!
|
|
486
|
-
return _server.NextResponse.json({ error: "
|
|
713
|
+
const { paths, destination } = await request.json();
|
|
714
|
+
if (!paths || !Array.isArray(paths) || paths.length === 0) {
|
|
715
|
+
return _server.NextResponse.json({ error: "Paths are required" }, { status: 400 });
|
|
487
716
|
}
|
|
488
|
-
|
|
489
|
-
|
|
717
|
+
if (!destination || typeof destination !== "string") {
|
|
718
|
+
return _server.NextResponse.json({ error: "Destination is required" }, { status: 400 });
|
|
719
|
+
}
|
|
720
|
+
const safeDestination = destination.replace(/\.\./g, "");
|
|
721
|
+
const absoluteDestination = _path2.default.join(process.cwd(), safeDestination);
|
|
722
|
+
if (!absoluteDestination.startsWith(_path2.default.join(process.cwd(), "public"))) {
|
|
723
|
+
return _server.NextResponse.json({ error: "Invalid destination" }, { status: 400 });
|
|
724
|
+
}
|
|
725
|
+
try {
|
|
726
|
+
const destStats = await _fs.promises.stat(absoluteDestination);
|
|
727
|
+
if (!destStats.isDirectory()) {
|
|
728
|
+
return _server.NextResponse.json({ error: "Destination is not a folder" }, { status: 400 });
|
|
729
|
+
}
|
|
730
|
+
} catch (e18) {
|
|
731
|
+
return _server.NextResponse.json({ error: "Destination folder not found" }, { status: 404 });
|
|
732
|
+
}
|
|
733
|
+
const moved = [];
|
|
490
734
|
const errors = [];
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
735
|
+
const meta = await loadMeta();
|
|
736
|
+
let metaChanged = false;
|
|
737
|
+
for (const itemPath of paths) {
|
|
738
|
+
const safePath = itemPath.replace(/\.\./g, "");
|
|
739
|
+
const absolutePath = _path2.default.join(process.cwd(), safePath);
|
|
740
|
+
const itemName = _path2.default.basename(safePath);
|
|
741
|
+
const newAbsolutePath = _path2.default.join(absoluteDestination, itemName);
|
|
742
|
+
if (absoluteDestination.startsWith(absolutePath + _path2.default.sep)) {
|
|
743
|
+
errors.push(`Cannot move ${itemName} into itself`);
|
|
744
|
+
continue;
|
|
745
|
+
}
|
|
746
|
+
try {
|
|
747
|
+
await _fs.promises.access(absolutePath);
|
|
748
|
+
} catch (e19) {
|
|
749
|
+
errors.push(`${itemName} not found`);
|
|
750
|
+
continue;
|
|
751
|
+
}
|
|
752
|
+
try {
|
|
753
|
+
await _fs.promises.access(newAbsolutePath);
|
|
754
|
+
errors.push(`${itemName} already exists in destination`);
|
|
755
|
+
continue;
|
|
756
|
+
} catch (e20) {
|
|
757
|
+
}
|
|
758
|
+
try {
|
|
759
|
+
await _fs.promises.rename(absolutePath, newAbsolutePath);
|
|
760
|
+
const stats = await _fs.promises.stat(newAbsolutePath);
|
|
761
|
+
if (stats.isFile() && isImageFile(itemName)) {
|
|
762
|
+
const oldRelativePath = safePath.replace(/^public\//, "");
|
|
763
|
+
const newRelativePath = _path2.default.join(safeDestination.replace(/^public\//, ""), itemName);
|
|
764
|
+
const oldKey = "/" + oldRelativePath;
|
|
765
|
+
const newKey = "/" + newRelativePath;
|
|
766
|
+
if (meta[oldKey]) {
|
|
767
|
+
const entry = meta[oldKey];
|
|
768
|
+
const oldThumbPaths = _chunkCN5NRNWBjs.getAllThumbnailPaths.call(void 0, oldKey);
|
|
769
|
+
const newThumbPaths = _chunkCN5NRNWBjs.getAllThumbnailPaths.call(void 0, newKey);
|
|
770
|
+
for (let i = 0; i < oldThumbPaths.length; i++) {
|
|
771
|
+
const oldThumbPath = _path2.default.join(process.cwd(), "public", oldThumbPaths[i]);
|
|
772
|
+
const newThumbPath = _path2.default.join(process.cwd(), "public", newThumbPaths[i]);
|
|
773
|
+
await _fs.promises.mkdir(_path2.default.dirname(newThumbPath), { recursive: true });
|
|
774
|
+
try {
|
|
775
|
+
await _fs.promises.rename(oldThumbPath, newThumbPath);
|
|
776
|
+
} catch (e21) {
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
delete meta[oldKey];
|
|
780
|
+
meta[newKey] = entry;
|
|
781
|
+
metaChanged = true;
|
|
503
782
|
}
|
|
504
783
|
}
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
await uploadToCdn(imageKey);
|
|
509
|
-
await deleteLocalThumbnails(imageKey);
|
|
510
|
-
}
|
|
511
|
-
meta[imageKey] = updatedEntry;
|
|
512
|
-
processed.push(imageKey);
|
|
513
|
-
} catch (error) {
|
|
514
|
-
console.error(`Failed to reprocess ${imageKey}:`, error);
|
|
515
|
-
errors.push(imageKey);
|
|
784
|
+
moved.push(itemPath);
|
|
785
|
+
} catch (e22) {
|
|
786
|
+
errors.push(`Failed to move ${itemName}`);
|
|
516
787
|
}
|
|
517
788
|
}
|
|
518
|
-
|
|
789
|
+
if (metaChanged) {
|
|
790
|
+
await saveMeta(meta);
|
|
791
|
+
}
|
|
519
792
|
return _server.NextResponse.json({
|
|
520
|
-
success:
|
|
521
|
-
|
|
793
|
+
success: errors.length === 0,
|
|
794
|
+
moved,
|
|
522
795
|
errors: errors.length > 0 ? errors : void 0
|
|
523
796
|
});
|
|
524
797
|
} catch (error) {
|
|
525
|
-
console.error("Failed to
|
|
526
|
-
return _server.NextResponse.json({ error: "Failed to
|
|
798
|
+
console.error("Failed to move:", error);
|
|
799
|
+
return _server.NextResponse.json({ error: "Failed to move items" }, { status: 500 });
|
|
527
800
|
}
|
|
528
801
|
}
|
|
529
|
-
|
|
802
|
+
|
|
803
|
+
// src/handlers/images.ts
|
|
804
|
+
|
|
805
|
+
|
|
806
|
+
|
|
807
|
+
|
|
808
|
+
async function handleSync(request) {
|
|
809
|
+
const accountId = process.env.CLOUDFLARE_R2_ACCOUNT_ID;
|
|
810
|
+
const accessKeyId = process.env.CLOUDFLARE_R2_ACCESS_KEY_ID;
|
|
811
|
+
const secretAccessKey = process.env.CLOUDFLARE_R2_SECRET_ACCESS_KEY;
|
|
812
|
+
const bucketName = process.env.CLOUDFLARE_R2_BUCKET_NAME;
|
|
813
|
+
const publicUrl = process.env.CLOUDFLARE_R2_PUBLIC_URL;
|
|
814
|
+
if (!accountId || !accessKeyId || !secretAccessKey || !bucketName || !publicUrl) {
|
|
815
|
+
return _server.NextResponse.json(
|
|
816
|
+
{ error: "R2 not configured. Set CLOUDFLARE_R2_* environment variables." },
|
|
817
|
+
{ status: 400 }
|
|
818
|
+
);
|
|
819
|
+
}
|
|
530
820
|
try {
|
|
531
|
-
const
|
|
532
|
-
|
|
821
|
+
const { imageKeys } = await request.json();
|
|
822
|
+
if (!imageKeys || !Array.isArray(imageKeys) || imageKeys.length === 0) {
|
|
823
|
+
return _server.NextResponse.json({ error: "No image keys provided" }, { status: 400 });
|
|
824
|
+
}
|
|
825
|
+
const meta = await loadMeta();
|
|
826
|
+
const r2 = new (0, _clients3.S3Client)({
|
|
827
|
+
region: "auto",
|
|
828
|
+
endpoint: `https://${accountId}.r2.cloudflarestorage.com`,
|
|
829
|
+
credentials: { accessKeyId, secretAccessKey }
|
|
830
|
+
});
|
|
831
|
+
const synced = [];
|
|
832
|
+
const errors = [];
|
|
833
|
+
for (const imageKey of imageKeys) {
|
|
834
|
+
const entry = meta[imageKey];
|
|
835
|
+
if (!entry) {
|
|
836
|
+
errors.push(`Image not found in meta: ${imageKey}`);
|
|
837
|
+
continue;
|
|
838
|
+
}
|
|
839
|
+
if (entry.s) {
|
|
840
|
+
synced.push(imageKey);
|
|
841
|
+
continue;
|
|
842
|
+
}
|
|
533
843
|
try {
|
|
534
|
-
const
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
844
|
+
for (const thumbPath of _chunkCN5NRNWBjs.getAllThumbnailPaths.call(void 0, imageKey)) {
|
|
845
|
+
const localPath = _path2.default.join(process.cwd(), "public", thumbPath);
|
|
846
|
+
try {
|
|
847
|
+
const fileBuffer = await _fs.promises.readFile(localPath);
|
|
848
|
+
await r2.send(
|
|
849
|
+
new (0, _clients3.PutObjectCommand)({
|
|
850
|
+
Bucket: bucketName,
|
|
851
|
+
Key: thumbPath.replace(/^\//, ""),
|
|
852
|
+
Body: fileBuffer,
|
|
853
|
+
ContentType: getContentType(thumbPath)
|
|
854
|
+
})
|
|
855
|
+
);
|
|
856
|
+
} catch (e23) {
|
|
544
857
|
}
|
|
545
858
|
}
|
|
546
|
-
|
|
859
|
+
entry.s = 1;
|
|
860
|
+
for (const thumbPath of _chunkCN5NRNWBjs.getAllThumbnailPaths.call(void 0, imageKey)) {
|
|
861
|
+
const localPath = _path2.default.join(process.cwd(), "public", thumbPath);
|
|
862
|
+
try {
|
|
863
|
+
await _fs.promises.unlink(localPath);
|
|
864
|
+
} catch (e24) {
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
synced.push(imageKey);
|
|
868
|
+
} catch (error) {
|
|
869
|
+
console.error(`Failed to sync ${imageKey}:`, error);
|
|
870
|
+
errors.push(imageKey);
|
|
547
871
|
}
|
|
548
872
|
}
|
|
549
|
-
|
|
550
|
-
await scanPublicFolder(publicDir);
|
|
873
|
+
await saveMeta(meta);
|
|
551
874
|
return _server.NextResponse.json({
|
|
552
|
-
|
|
553
|
-
|
|
875
|
+
success: true,
|
|
876
|
+
synced,
|
|
877
|
+
errors: errors.length > 0 ? errors : void 0
|
|
554
878
|
});
|
|
555
879
|
} catch (error) {
|
|
556
|
-
console.error("Failed to
|
|
557
|
-
return _server.NextResponse.json({ error: "Failed to
|
|
880
|
+
console.error("Failed to sync:", error);
|
|
881
|
+
return _server.NextResponse.json({ error: "Failed to sync to CDN" }, { status: 500 });
|
|
558
882
|
}
|
|
559
883
|
}
|
|
560
|
-
async function
|
|
884
|
+
async function handleReprocess(request) {
|
|
561
885
|
try {
|
|
562
|
-
const
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
return _server.NextResponse.json({ error: "No folders provided" }, { status: 400 });
|
|
886
|
+
const { imageKeys } = await request.json();
|
|
887
|
+
if (!imageKeys || !Array.isArray(imageKeys) || imageKeys.length === 0) {
|
|
888
|
+
return _server.NextResponse.json({ error: "No image keys provided" }, { status: 400 });
|
|
566
889
|
}
|
|
567
|
-
const
|
|
568
|
-
const
|
|
569
|
-
|
|
890
|
+
const meta = await loadMeta();
|
|
891
|
+
const processed = [];
|
|
892
|
+
const errors = [];
|
|
893
|
+
for (const imageKey of imageKeys) {
|
|
570
894
|
try {
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
895
|
+
let buffer;
|
|
896
|
+
const entry = meta[imageKey];
|
|
897
|
+
const originalPath = _path2.default.join(process.cwd(), "public", imageKey);
|
|
898
|
+
try {
|
|
899
|
+
buffer = await _fs.promises.readFile(originalPath);
|
|
900
|
+
} catch (e25) {
|
|
901
|
+
if (_optionalChain([entry, 'optionalAccess', _5 => _5.s])) {
|
|
902
|
+
buffer = await downloadFromCdn(imageKey);
|
|
903
|
+
} else {
|
|
904
|
+
throw new Error(`File not found: ${imageKey}`);
|
|
580
905
|
}
|
|
581
906
|
}
|
|
582
|
-
|
|
907
|
+
const updatedEntry = await processImage(buffer, imageKey);
|
|
908
|
+
if (_optionalChain([entry, 'optionalAccess', _6 => _6.s])) {
|
|
909
|
+
updatedEntry.s = 1;
|
|
910
|
+
await uploadToCdn(imageKey);
|
|
911
|
+
await deleteLocalThumbnails(imageKey);
|
|
912
|
+
}
|
|
913
|
+
meta[imageKey] = updatedEntry;
|
|
914
|
+
processed.push(imageKey);
|
|
915
|
+
} catch (error) {
|
|
916
|
+
console.error(`Failed to reprocess ${imageKey}:`, error);
|
|
917
|
+
errors.push(imageKey);
|
|
583
918
|
}
|
|
584
919
|
}
|
|
585
|
-
|
|
586
|
-
const relativePath = folder.replace(/^public\/?/, "");
|
|
587
|
-
if (relativePath === "images" || relativePath.startsWith("images/")) continue;
|
|
588
|
-
const folderPath = _path2.default.join(process.cwd(), folder);
|
|
589
|
-
await scanFolder(folderPath, relativePath);
|
|
590
|
-
}
|
|
920
|
+
await saveMeta(meta);
|
|
591
921
|
return _server.NextResponse.json({
|
|
592
|
-
|
|
593
|
-
|
|
922
|
+
success: true,
|
|
923
|
+
processed,
|
|
924
|
+
errors: errors.length > 0 ? errors : void 0
|
|
594
925
|
});
|
|
595
926
|
} catch (error) {
|
|
596
|
-
console.error("Failed to
|
|
597
|
-
return _server.NextResponse.json({ error: "Failed to
|
|
927
|
+
console.error("Failed to reprocess:", error);
|
|
928
|
+
return _server.NextResponse.json({ error: "Failed to reprocess images" }, { status: 500 });
|
|
598
929
|
}
|
|
599
930
|
}
|
|
600
931
|
async function handleProcessAllStream() {
|
|
@@ -626,7 +957,7 @@ async function handleProcessAllStream() {
|
|
|
626
957
|
allImages.push({ key: relPath, fullPath });
|
|
627
958
|
}
|
|
628
959
|
}
|
|
629
|
-
} catch (
|
|
960
|
+
} catch (e26) {
|
|
630
961
|
}
|
|
631
962
|
}
|
|
632
963
|
const publicDir = _path2.default.join(process.cwd(), "public");
|
|
@@ -701,7 +1032,7 @@ async function handleProcessAllStream() {
|
|
|
701
1032
|
}
|
|
702
1033
|
}
|
|
703
1034
|
}
|
|
704
|
-
} catch (
|
|
1035
|
+
} catch (e27) {
|
|
705
1036
|
}
|
|
706
1037
|
}
|
|
707
1038
|
const imagesDir = _path2.default.join(process.cwd(), "public", "images");
|
|
@@ -722,7 +1053,7 @@ async function handleProcessAllStream() {
|
|
|
722
1053
|
await _fs.promises.rmdir(dir);
|
|
723
1054
|
}
|
|
724
1055
|
return isEmpty;
|
|
725
|
-
} catch (
|
|
1056
|
+
} catch (e28) {
|
|
726
1057
|
return true;
|
|
727
1058
|
}
|
|
728
1059
|
}
|
|
@@ -750,371 +1081,72 @@ async function handleProcessAllStream() {
|
|
|
750
1081
|
}
|
|
751
1082
|
});
|
|
752
1083
|
}
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
return
|
|
758
|
-
} catch (e16) {
|
|
759
|
-
return {};
|
|
1084
|
+
|
|
1085
|
+
// src/handlers/index.ts
|
|
1086
|
+
async function GET(request) {
|
|
1087
|
+
if (process.env.NODE_ENV !== "development") {
|
|
1088
|
+
return _server.NextResponse.json({ error: "Not available in production" }, { status: 403 });
|
|
760
1089
|
}
|
|
1090
|
+
const pathname = request.nextUrl.pathname;
|
|
1091
|
+
const route = pathname.replace(/^\/api\/studio\/?/, "");
|
|
1092
|
+
if (route === "list-folders") {
|
|
1093
|
+
return handleListFolders();
|
|
1094
|
+
}
|
|
1095
|
+
if (route === "list" || route.startsWith("list")) {
|
|
1096
|
+
return handleList(request);
|
|
1097
|
+
}
|
|
1098
|
+
if (route === "count-images") {
|
|
1099
|
+
return handleCountImages();
|
|
1100
|
+
}
|
|
1101
|
+
if (route === "folder-images") {
|
|
1102
|
+
return handleFolderImages(request);
|
|
1103
|
+
}
|
|
1104
|
+
if (route === "search") {
|
|
1105
|
+
return handleSearch(request);
|
|
1106
|
+
}
|
|
1107
|
+
return _server.NextResponse.json({ error: "Not found" }, { status: 404 });
|
|
761
1108
|
}
|
|
762
|
-
async function
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
const metaPath = _path2.default.join(dataDir, "_meta.json");
|
|
766
|
-
await _fs.promises.writeFile(metaPath, JSON.stringify(meta, null, 2));
|
|
767
|
-
}
|
|
768
|
-
function isImageFile(filename) {
|
|
769
|
-
const ext = _path2.default.extname(filename).toLowerCase();
|
|
770
|
-
return [".jpg", ".jpeg", ".png", ".gif", ".webp", ".svg", ".ico", ".bmp", ".tiff", ".tif"].includes(ext);
|
|
771
|
-
}
|
|
772
|
-
function isMediaFile(filename) {
|
|
773
|
-
const ext = _path2.default.extname(filename).toLowerCase();
|
|
774
|
-
if ([".jpg", ".jpeg", ".png", ".gif", ".webp", ".svg", ".ico", ".bmp", ".tiff", ".tif"].includes(ext)) return true;
|
|
775
|
-
if ([".mp4", ".webm", ".mov", ".avi", ".mkv", ".m4v"].includes(ext)) return true;
|
|
776
|
-
if ([".mp3", ".wav", ".ogg", ".m4a", ".flac", ".aac"].includes(ext)) return true;
|
|
777
|
-
if ([".pdf"].includes(ext)) return true;
|
|
778
|
-
return false;
|
|
779
|
-
}
|
|
780
|
-
function getContentType(filePath) {
|
|
781
|
-
const ext = _path2.default.extname(filePath).toLowerCase();
|
|
782
|
-
switch (ext) {
|
|
783
|
-
case ".jpg":
|
|
784
|
-
case ".jpeg":
|
|
785
|
-
return "image/jpeg";
|
|
786
|
-
case ".png":
|
|
787
|
-
return "image/png";
|
|
788
|
-
case ".gif":
|
|
789
|
-
return "image/gif";
|
|
790
|
-
case ".webp":
|
|
791
|
-
return "image/webp";
|
|
792
|
-
case ".svg":
|
|
793
|
-
return "image/svg+xml";
|
|
794
|
-
default:
|
|
795
|
-
return "application/octet-stream";
|
|
796
|
-
}
|
|
797
|
-
}
|
|
798
|
-
async function processImage(buffer, imageKey) {
|
|
799
|
-
const sharpInstance = _sharp2.default.call(void 0, buffer);
|
|
800
|
-
const metadata = await sharpInstance.metadata();
|
|
801
|
-
const originalWidth = metadata.width || 0;
|
|
802
|
-
const originalHeight = metadata.height || 0;
|
|
803
|
-
const keyWithoutSlash = imageKey.startsWith("/") ? imageKey.slice(1) : imageKey;
|
|
804
|
-
const baseName = _path2.default.basename(keyWithoutSlash, _path2.default.extname(keyWithoutSlash));
|
|
805
|
-
const ext = _path2.default.extname(keyWithoutSlash).toLowerCase();
|
|
806
|
-
const imageDir = _path2.default.dirname(keyWithoutSlash);
|
|
807
|
-
const imagesPath = _path2.default.join(process.cwd(), "public", "images", imageDir === "." ? "" : imageDir);
|
|
808
|
-
await _fs.promises.mkdir(imagesPath, { recursive: true });
|
|
809
|
-
const isPng = ext === ".png";
|
|
810
|
-
const outputExt = isPng ? ".png" : ".jpg";
|
|
811
|
-
const fullFileName = imageDir === "." ? `${baseName}${outputExt}` : `${imageDir}/${baseName}${outputExt}`;
|
|
812
|
-
const fullPath = _path2.default.join(process.cwd(), "public", "images", fullFileName);
|
|
813
|
-
if (isPng) {
|
|
814
|
-
await _sharp2.default.call(void 0, buffer).png({ quality: 85 }).toFile(fullPath);
|
|
815
|
-
} else {
|
|
816
|
-
await _sharp2.default.call(void 0, buffer).jpeg({ quality: 85 }).toFile(fullPath);
|
|
817
|
-
}
|
|
818
|
-
for (const [, sizeConfig] of Object.entries(DEFAULT_SIZES)) {
|
|
819
|
-
const { width: maxWidth, suffix } = sizeConfig;
|
|
820
|
-
if (originalWidth <= maxWidth) {
|
|
821
|
-
continue;
|
|
822
|
-
}
|
|
823
|
-
const ratio = originalHeight / originalWidth;
|
|
824
|
-
const newHeight = Math.round(maxWidth * ratio);
|
|
825
|
-
const sizeFileName = `${baseName}${suffix}${outputExt}`;
|
|
826
|
-
const sizeFilePath = imageDir === "." ? sizeFileName : `${imageDir}/${sizeFileName}`;
|
|
827
|
-
const sizePath = _path2.default.join(process.cwd(), "public", "images", sizeFilePath);
|
|
828
|
-
if (isPng) {
|
|
829
|
-
await _sharp2.default.call(void 0, buffer).resize(maxWidth, newHeight).png({ quality: 80 }).toFile(sizePath);
|
|
830
|
-
} else {
|
|
831
|
-
await _sharp2.default.call(void 0, buffer).resize(maxWidth, newHeight).jpeg({ quality: 80 }).toFile(sizePath);
|
|
832
|
-
}
|
|
1109
|
+
async function POST(request) {
|
|
1110
|
+
if (process.env.NODE_ENV !== "development") {
|
|
1111
|
+
return _server.NextResponse.json({ error: "Not available in production" }, { status: 403 });
|
|
833
1112
|
}
|
|
834
|
-
const
|
|
835
|
-
const
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
h: originalHeight,
|
|
839
|
-
blur: blurhash
|
|
840
|
-
};
|
|
841
|
-
}
|
|
842
|
-
async function downloadFromCdn(originalPath) {
|
|
843
|
-
const accountId = process.env.CLOUDFLARE_R2_ACCOUNT_ID;
|
|
844
|
-
const accessKeyId = process.env.CLOUDFLARE_R2_ACCESS_KEY_ID;
|
|
845
|
-
const secretAccessKey = process.env.CLOUDFLARE_R2_SECRET_ACCESS_KEY;
|
|
846
|
-
const bucketName = process.env.CLOUDFLARE_R2_BUCKET_NAME;
|
|
847
|
-
if (!accountId || !accessKeyId || !secretAccessKey || !bucketName) {
|
|
848
|
-
throw new Error("R2 not configured");
|
|
1113
|
+
const pathname = request.nextUrl.pathname;
|
|
1114
|
+
const route = pathname.replace(/^\/api\/studio\/?/, "");
|
|
1115
|
+
if (route === "upload") {
|
|
1116
|
+
return handleUpload(request);
|
|
849
1117
|
}
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
endpoint: `https://${accountId}.r2.cloudflarestorage.com`,
|
|
853
|
-
credentials: { accessKeyId, secretAccessKey }
|
|
854
|
-
});
|
|
855
|
-
const response = await r2.send(
|
|
856
|
-
new (0, _clients3.GetObjectCommand)({
|
|
857
|
-
Bucket: bucketName,
|
|
858
|
-
Key: originalPath.replace(/^\//, "")
|
|
859
|
-
})
|
|
860
|
-
);
|
|
861
|
-
const stream = response.Body;
|
|
862
|
-
const chunks = [];
|
|
863
|
-
for await (const chunk of stream) {
|
|
864
|
-
chunks.push(Buffer.from(chunk));
|
|
1118
|
+
if (route === "delete") {
|
|
1119
|
+
return handleDelete(request);
|
|
865
1120
|
}
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
async function uploadToCdn(imageKey) {
|
|
869
|
-
const accountId = process.env.CLOUDFLARE_R2_ACCOUNT_ID;
|
|
870
|
-
const accessKeyId = process.env.CLOUDFLARE_R2_ACCESS_KEY_ID;
|
|
871
|
-
const secretAccessKey = process.env.CLOUDFLARE_R2_SECRET_ACCESS_KEY;
|
|
872
|
-
const bucketName = process.env.CLOUDFLARE_R2_BUCKET_NAME;
|
|
873
|
-
if (!accountId || !accessKeyId || !secretAccessKey || !bucketName) {
|
|
874
|
-
throw new Error("R2 not configured");
|
|
1121
|
+
if (route === "sync") {
|
|
1122
|
+
return handleSync(request);
|
|
875
1123
|
}
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
endpoint: `https://${accountId}.r2.cloudflarestorage.com`,
|
|
879
|
-
credentials: { accessKeyId, secretAccessKey }
|
|
880
|
-
});
|
|
881
|
-
for (const thumbPath of _chunkCN5NRNWBjs.getAllThumbnailPaths.call(void 0, imageKey)) {
|
|
882
|
-
const localPath = _path2.default.join(process.cwd(), "public", thumbPath);
|
|
883
|
-
try {
|
|
884
|
-
const fileBuffer = await _fs.promises.readFile(localPath);
|
|
885
|
-
await r2.send(
|
|
886
|
-
new (0, _clients3.PutObjectCommand)({
|
|
887
|
-
Bucket: bucketName,
|
|
888
|
-
Key: thumbPath.replace(/^\//, ""),
|
|
889
|
-
Body: fileBuffer,
|
|
890
|
-
ContentType: getContentType(thumbPath)
|
|
891
|
-
})
|
|
892
|
-
);
|
|
893
|
-
} catch (e17) {
|
|
894
|
-
}
|
|
1124
|
+
if (route === "reprocess") {
|
|
1125
|
+
return handleReprocess(request);
|
|
895
1126
|
}
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
for (const thumbPath of _chunkCN5NRNWBjs.getAllThumbnailPaths.call(void 0, imageKey)) {
|
|
899
|
-
const localPath = _path2.default.join(process.cwd(), "public", thumbPath);
|
|
900
|
-
try {
|
|
901
|
-
await _fs.promises.unlink(localPath);
|
|
902
|
-
} catch (e18) {
|
|
903
|
-
}
|
|
1127
|
+
if (route === "process-all") {
|
|
1128
|
+
return handleProcessAllStream();
|
|
904
1129
|
}
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
try {
|
|
908
|
-
const { parentPath, name } = await request.json();
|
|
909
|
-
if (!name || typeof name !== "string") {
|
|
910
|
-
return _server.NextResponse.json({ error: "Folder name is required" }, { status: 400 });
|
|
911
|
-
}
|
|
912
|
-
const sanitizedName = name.replace(/[<>:"/\\|?*]/g, "").trim();
|
|
913
|
-
if (!sanitizedName) {
|
|
914
|
-
return _server.NextResponse.json({ error: "Invalid folder name" }, { status: 400 });
|
|
915
|
-
}
|
|
916
|
-
const safePath = (parentPath || "public").replace(/\.\./g, "");
|
|
917
|
-
const folderPath = _path2.default.join(process.cwd(), safePath, sanitizedName);
|
|
918
|
-
if (!folderPath.startsWith(_path2.default.join(process.cwd(), "public"))) {
|
|
919
|
-
return _server.NextResponse.json({ error: "Invalid path" }, { status: 400 });
|
|
920
|
-
}
|
|
921
|
-
try {
|
|
922
|
-
await _fs.promises.access(folderPath);
|
|
923
|
-
return _server.NextResponse.json({ error: "A folder with this name already exists" }, { status: 400 });
|
|
924
|
-
} catch (e19) {
|
|
925
|
-
}
|
|
926
|
-
await _fs.promises.mkdir(folderPath, { recursive: true });
|
|
927
|
-
return _server.NextResponse.json({ success: true, path: _path2.default.join(safePath, sanitizedName) });
|
|
928
|
-
} catch (error) {
|
|
929
|
-
console.error("Failed to create folder:", error);
|
|
930
|
-
return _server.NextResponse.json({ error: "Failed to create folder" }, { status: 500 });
|
|
1130
|
+
if (route === "create-folder") {
|
|
1131
|
+
return handleCreateFolder(request);
|
|
931
1132
|
}
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
try {
|
|
935
|
-
const { oldPath, newName } = await request.json();
|
|
936
|
-
if (!oldPath || !newName) {
|
|
937
|
-
return _server.NextResponse.json({ error: "Path and new name are required" }, { status: 400 });
|
|
938
|
-
}
|
|
939
|
-
const sanitizedName = newName.replace(/[<>:"/\\|?*]/g, "").trim();
|
|
940
|
-
if (!sanitizedName) {
|
|
941
|
-
return _server.NextResponse.json({ error: "Invalid name" }, { status: 400 });
|
|
942
|
-
}
|
|
943
|
-
const safePath = oldPath.replace(/\.\./g, "");
|
|
944
|
-
const absoluteOldPath = _path2.default.join(process.cwd(), safePath);
|
|
945
|
-
const parentDir = _path2.default.dirname(absoluteOldPath);
|
|
946
|
-
const absoluteNewPath = _path2.default.join(parentDir, sanitizedName);
|
|
947
|
-
if (!absoluteOldPath.startsWith(_path2.default.join(process.cwd(), "public"))) {
|
|
948
|
-
return _server.NextResponse.json({ error: "Invalid path" }, { status: 400 });
|
|
949
|
-
}
|
|
950
|
-
try {
|
|
951
|
-
await _fs.promises.access(absoluteOldPath);
|
|
952
|
-
} catch (e20) {
|
|
953
|
-
return _server.NextResponse.json({ error: "File or folder not found" }, { status: 404 });
|
|
954
|
-
}
|
|
955
|
-
try {
|
|
956
|
-
await _fs.promises.access(absoluteNewPath);
|
|
957
|
-
return _server.NextResponse.json({ error: "An item with this name already exists" }, { status: 400 });
|
|
958
|
-
} catch (e21) {
|
|
959
|
-
}
|
|
960
|
-
const stats = await _fs.promises.stat(absoluteOldPath);
|
|
961
|
-
const isFile = stats.isFile();
|
|
962
|
-
const isImage = isFile && isImageFile(_path2.default.basename(oldPath));
|
|
963
|
-
await _fs.promises.rename(absoluteOldPath, absoluteNewPath);
|
|
964
|
-
if (isImage) {
|
|
965
|
-
const meta = await loadMeta();
|
|
966
|
-
const oldRelativePath = safePath.replace(/^public\//, "");
|
|
967
|
-
const newRelativePath = _path2.default.join(_path2.default.dirname(oldRelativePath), sanitizedName);
|
|
968
|
-
const oldKey = "/" + oldRelativePath;
|
|
969
|
-
const newKey = "/" + newRelativePath;
|
|
970
|
-
if (meta[oldKey]) {
|
|
971
|
-
const entry = meta[oldKey];
|
|
972
|
-
const oldThumbPaths = _chunkCN5NRNWBjs.getAllThumbnailPaths.call(void 0, oldKey);
|
|
973
|
-
const newThumbPaths = _chunkCN5NRNWBjs.getAllThumbnailPaths.call(void 0, newKey);
|
|
974
|
-
for (let i = 0; i < oldThumbPaths.length; i++) {
|
|
975
|
-
const oldThumbPath = _path2.default.join(process.cwd(), "public", oldThumbPaths[i]);
|
|
976
|
-
const newThumbPath = _path2.default.join(process.cwd(), "public", newThumbPaths[i]);
|
|
977
|
-
await _fs.promises.mkdir(_path2.default.dirname(newThumbPath), { recursive: true });
|
|
978
|
-
try {
|
|
979
|
-
await _fs.promises.rename(oldThumbPath, newThumbPath);
|
|
980
|
-
} catch (e22) {
|
|
981
|
-
}
|
|
982
|
-
}
|
|
983
|
-
delete meta[oldKey];
|
|
984
|
-
meta[newKey] = entry;
|
|
985
|
-
}
|
|
986
|
-
await saveMeta(meta);
|
|
987
|
-
}
|
|
988
|
-
const newPath = _path2.default.join(_path2.default.dirname(safePath), sanitizedName);
|
|
989
|
-
return _server.NextResponse.json({ success: true, newPath });
|
|
990
|
-
} catch (error) {
|
|
991
|
-
console.error("Failed to rename:", error);
|
|
992
|
-
return _server.NextResponse.json({ error: "Failed to rename" }, { status: 500 });
|
|
1133
|
+
if (route === "rename") {
|
|
1134
|
+
return handleRename(request);
|
|
993
1135
|
}
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
try {
|
|
997
|
-
const { paths, destination } = await request.json();
|
|
998
|
-
if (!paths || !Array.isArray(paths) || paths.length === 0) {
|
|
999
|
-
return _server.NextResponse.json({ error: "Paths are required" }, { status: 400 });
|
|
1000
|
-
}
|
|
1001
|
-
if (!destination || typeof destination !== "string") {
|
|
1002
|
-
return _server.NextResponse.json({ error: "Destination is required" }, { status: 400 });
|
|
1003
|
-
}
|
|
1004
|
-
const safeDestination = destination.replace(/\.\./g, "");
|
|
1005
|
-
const absoluteDestination = _path2.default.join(process.cwd(), safeDestination);
|
|
1006
|
-
if (!absoluteDestination.startsWith(_path2.default.join(process.cwd(), "public"))) {
|
|
1007
|
-
return _server.NextResponse.json({ error: "Invalid destination" }, { status: 400 });
|
|
1008
|
-
}
|
|
1009
|
-
try {
|
|
1010
|
-
const destStats = await _fs.promises.stat(absoluteDestination);
|
|
1011
|
-
if (!destStats.isDirectory()) {
|
|
1012
|
-
return _server.NextResponse.json({ error: "Destination is not a folder" }, { status: 400 });
|
|
1013
|
-
}
|
|
1014
|
-
} catch (e23) {
|
|
1015
|
-
return _server.NextResponse.json({ error: "Destination folder not found" }, { status: 404 });
|
|
1016
|
-
}
|
|
1017
|
-
const moved = [];
|
|
1018
|
-
const errors = [];
|
|
1019
|
-
const meta = await loadMeta();
|
|
1020
|
-
let metaChanged = false;
|
|
1021
|
-
for (const itemPath of paths) {
|
|
1022
|
-
const safePath = itemPath.replace(/\.\./g, "");
|
|
1023
|
-
const absolutePath = _path2.default.join(process.cwd(), safePath);
|
|
1024
|
-
const itemName = _path2.default.basename(safePath);
|
|
1025
|
-
const newAbsolutePath = _path2.default.join(absoluteDestination, itemName);
|
|
1026
|
-
if (absoluteDestination.startsWith(absolutePath + _path2.default.sep)) {
|
|
1027
|
-
errors.push(`Cannot move ${itemName} into itself`);
|
|
1028
|
-
continue;
|
|
1029
|
-
}
|
|
1030
|
-
try {
|
|
1031
|
-
await _fs.promises.access(absolutePath);
|
|
1032
|
-
} catch (e24) {
|
|
1033
|
-
errors.push(`${itemName} not found`);
|
|
1034
|
-
continue;
|
|
1035
|
-
}
|
|
1036
|
-
try {
|
|
1037
|
-
await _fs.promises.access(newAbsolutePath);
|
|
1038
|
-
errors.push(`${itemName} already exists in destination`);
|
|
1039
|
-
continue;
|
|
1040
|
-
} catch (e25) {
|
|
1041
|
-
}
|
|
1042
|
-
try {
|
|
1043
|
-
await _fs.promises.rename(absolutePath, newAbsolutePath);
|
|
1044
|
-
const stats = await _fs.promises.stat(newAbsolutePath);
|
|
1045
|
-
if (stats.isFile() && isImageFile(itemName)) {
|
|
1046
|
-
const oldRelativePath = safePath.replace(/^public\//, "");
|
|
1047
|
-
const newRelativePath = _path2.default.join(safeDestination.replace(/^public\//, ""), itemName);
|
|
1048
|
-
const oldKey = "/" + oldRelativePath;
|
|
1049
|
-
const newKey = "/" + newRelativePath;
|
|
1050
|
-
if (meta[oldKey]) {
|
|
1051
|
-
const entry = meta[oldKey];
|
|
1052
|
-
const oldThumbPaths = _chunkCN5NRNWBjs.getAllThumbnailPaths.call(void 0, oldKey);
|
|
1053
|
-
const newThumbPaths = _chunkCN5NRNWBjs.getAllThumbnailPaths.call(void 0, newKey);
|
|
1054
|
-
for (let i = 0; i < oldThumbPaths.length; i++) {
|
|
1055
|
-
const oldThumbPath = _path2.default.join(process.cwd(), "public", oldThumbPaths[i]);
|
|
1056
|
-
const newThumbPath = _path2.default.join(process.cwd(), "public", newThumbPaths[i]);
|
|
1057
|
-
await _fs.promises.mkdir(_path2.default.dirname(newThumbPath), { recursive: true });
|
|
1058
|
-
try {
|
|
1059
|
-
await _fs.promises.rename(oldThumbPath, newThumbPath);
|
|
1060
|
-
} catch (e26) {
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1063
|
-
delete meta[oldKey];
|
|
1064
|
-
meta[newKey] = entry;
|
|
1065
|
-
metaChanged = true;
|
|
1066
|
-
}
|
|
1067
|
-
}
|
|
1068
|
-
moved.push(itemPath);
|
|
1069
|
-
} catch (error) {
|
|
1070
|
-
errors.push(`Failed to move ${itemName}`);
|
|
1071
|
-
}
|
|
1072
|
-
}
|
|
1073
|
-
if (metaChanged) {
|
|
1074
|
-
await saveMeta(meta);
|
|
1075
|
-
}
|
|
1076
|
-
return _server.NextResponse.json({
|
|
1077
|
-
success: errors.length === 0,
|
|
1078
|
-
moved,
|
|
1079
|
-
errors: errors.length > 0 ? errors : void 0
|
|
1080
|
-
});
|
|
1081
|
-
} catch (error) {
|
|
1082
|
-
console.error("Failed to move:", error);
|
|
1083
|
-
return _server.NextResponse.json({ error: "Failed to move items" }, { status: 500 });
|
|
1136
|
+
if (route === "move") {
|
|
1137
|
+
return handleMove(request);
|
|
1084
1138
|
}
|
|
1139
|
+
return _server.NextResponse.json({ error: "Not found" }, { status: 404 });
|
|
1085
1140
|
}
|
|
1086
|
-
async function
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
const folders = [];
|
|
1090
|
-
async function scanDir(dir, relativePath, depth) {
|
|
1091
|
-
try {
|
|
1092
|
-
const entries = await _fs.promises.readdir(dir, { withFileTypes: true });
|
|
1093
|
-
for (const entry of entries) {
|
|
1094
|
-
if (!entry.isDirectory()) continue;
|
|
1095
|
-
if (entry.name.startsWith(".")) continue;
|
|
1096
|
-
const folderRelativePath = relativePath ? `${relativePath}/${entry.name}` : entry.name;
|
|
1097
|
-
folders.push({
|
|
1098
|
-
path: `public/${folderRelativePath}`,
|
|
1099
|
-
name: entry.name,
|
|
1100
|
-
depth
|
|
1101
|
-
});
|
|
1102
|
-
await scanDir(_path2.default.join(dir, entry.name), folderRelativePath, depth + 1);
|
|
1103
|
-
}
|
|
1104
|
-
} catch (e27) {
|
|
1105
|
-
}
|
|
1106
|
-
}
|
|
1107
|
-
folders.push({ path: "public", name: "public", depth: 0 });
|
|
1108
|
-
await scanDir(publicDir, "", 1);
|
|
1109
|
-
return _server.NextResponse.json({ folders });
|
|
1110
|
-
} catch (error) {
|
|
1111
|
-
console.error("Failed to list folders:", error);
|
|
1112
|
-
return _server.NextResponse.json({ error: "Failed to list folders" }, { status: 500 });
|
|
1141
|
+
async function DELETE(request) {
|
|
1142
|
+
if (process.env.NODE_ENV !== "development") {
|
|
1143
|
+
return _server.NextResponse.json({ error: "Not available in production" }, { status: 403 });
|
|
1113
1144
|
}
|
|
1145
|
+
return handleDelete(request);
|
|
1114
1146
|
}
|
|
1115
1147
|
|
|
1116
1148
|
|
|
1117
1149
|
|
|
1118
1150
|
|
|
1119
1151
|
exports.DELETE = DELETE; exports.GET = GET; exports.POST = POST;
|
|
1120
|
-
//# sourceMappingURL=
|
|
1152
|
+
//# sourceMappingURL=index.js.map
|