@dyrected/core 2.10.1 → 2.12.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/{app-config-BLC7WoyL.d.cts → app-config-C6hYM8WO.d.cts} +69 -4
- package/dist/{app-config-BLC7WoyL.d.ts → app-config-C6hYM8WO.d.ts} +69 -4
- package/dist/{chunk-H7DVVW2P.js → chunk-VOE2LOLF.js} +20 -0
- package/dist/index.cjs +133 -63
- package/dist/index.d.cts +103 -45
- package/dist/index.d.ts +103 -45
- package/dist/index.js +108 -59
- package/dist/server.cjs +416 -7
- package/dist/server.d.cts +14 -2
- package/dist/server.d.ts +14 -2
- package/dist/server.js +378 -8
- package/dist/{where-sanitizer-47QPJQ6O.js → where-sanitizer-CAAIYSSK.js} +20 -1
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
touchAuthSession,
|
|
31
31
|
transitionWorkflow,
|
|
32
32
|
verifyCollectionToken
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-VOE2LOLF.js";
|
|
34
34
|
import {
|
|
35
35
|
attachRequestMetrics,
|
|
36
36
|
bindObservabilityRuntime,
|
|
@@ -870,7 +870,7 @@ function mergeWhereConstraint(where, constraint) {
|
|
|
870
870
|
|
|
871
871
|
// src/utils/action-mutation.ts
|
|
872
872
|
function resolveActionMutation(mutation, ctx) {
|
|
873
|
-
if (!mutation) return {};
|
|
873
|
+
if (!mutation) return { ...ctx.input ?? {} };
|
|
874
874
|
return resolveValue(mutation, ctx);
|
|
875
875
|
}
|
|
876
876
|
function resolveValue(value, ctx) {
|
|
@@ -1032,7 +1032,7 @@ var CollectionController = class {
|
|
|
1032
1032
|
if (this.collection.admin?.filterable === false) {
|
|
1033
1033
|
where = void 0;
|
|
1034
1034
|
} else {
|
|
1035
|
-
const { sanitizeWhereClause } = await import("./where-sanitizer-
|
|
1035
|
+
const { sanitizeWhereClause } = await import("./where-sanitizer-CAAIYSSK.js");
|
|
1036
1036
|
where = sanitizeWhereClause(where, this.collection.fields);
|
|
1037
1037
|
if (Object.keys(where).length === 0) {
|
|
1038
1038
|
where = void 0;
|
|
@@ -2088,9 +2088,31 @@ var CollectionController = class {
|
|
|
2088
2088
|
const body = await c.req.json().catch(() => null);
|
|
2089
2089
|
if (body?.ids && Array.isArray(body.ids)) {
|
|
2090
2090
|
ids = body.ids;
|
|
2091
|
+
} else if (Array.isArray(body)) {
|
|
2092
|
+
ids = body;
|
|
2091
2093
|
}
|
|
2092
2094
|
} catch {
|
|
2093
2095
|
}
|
|
2096
|
+
if (!ids.length) {
|
|
2097
|
+
try {
|
|
2098
|
+
const parsed = await c.req.parseBody().catch(() => null);
|
|
2099
|
+
if (parsed) {
|
|
2100
|
+
if (Array.isArray(parsed.ids)) {
|
|
2101
|
+
ids = parsed.ids.filter((x) => typeof x === "string");
|
|
2102
|
+
} else if (Array.isArray(parsed["ids[]"])) {
|
|
2103
|
+
ids = parsed["ids[]"].filter((x) => typeof x === "string");
|
|
2104
|
+
} else if (typeof parsed.ids === "string") {
|
|
2105
|
+
try {
|
|
2106
|
+
const json = JSON.parse(parsed.ids);
|
|
2107
|
+
if (Array.isArray(json)) ids = json;
|
|
2108
|
+
} catch {
|
|
2109
|
+
ids = [parsed.ids];
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
2112
|
+
}
|
|
2113
|
+
} catch {
|
|
2114
|
+
}
|
|
2115
|
+
}
|
|
2094
2116
|
if (!ids.length) {
|
|
2095
2117
|
const raw = c.req.queries("ids") ?? c.req.queries("ids[]") ?? [];
|
|
2096
2118
|
ids = raw.filter(Boolean);
|
|
@@ -2227,7 +2249,7 @@ var CollectionController = class {
|
|
|
2227
2249
|
groupBy = queryGroupBy.trim();
|
|
2228
2250
|
}
|
|
2229
2251
|
}
|
|
2230
|
-
const { sanitizeWhereClause } = await import("./where-sanitizer-
|
|
2252
|
+
const { sanitizeWhereClause } = await import("./where-sanitizer-CAAIYSSK.js");
|
|
2231
2253
|
const sanitizedAggregates = {};
|
|
2232
2254
|
for (const [key, op] of Object.entries(rawAggregates)) {
|
|
2233
2255
|
if (!op || typeof op !== "object" || Array.isArray(op)) {
|
|
@@ -2508,6 +2530,7 @@ var MediaController = class {
|
|
|
2508
2530
|
}
|
|
2509
2531
|
const body = await c.req.parseBody();
|
|
2510
2532
|
const file = body["file"];
|
|
2533
|
+
const folderId = body["folderId"] || null;
|
|
2511
2534
|
const focalPointStr = body["focalPoint"];
|
|
2512
2535
|
const focalPoint = focalPointStr ? JSON.parse(focalPointStr) : void 0;
|
|
2513
2536
|
if (!file) {
|
|
@@ -2548,8 +2571,8 @@ var MediaController = class {
|
|
|
2548
2571
|
config: colConfig?.upload,
|
|
2549
2572
|
focalPoint
|
|
2550
2573
|
});
|
|
2551
|
-
imageMetadata = processed.metadata;
|
|
2552
|
-
imageSizes = processed.sizes;
|
|
2574
|
+
imageMetadata = processed.metadata || {};
|
|
2575
|
+
imageSizes = processed.sizes || {};
|
|
2553
2576
|
} catch (err) {
|
|
2554
2577
|
getRequestLogger(c, this.loggerComponent).error({
|
|
2555
2578
|
err,
|
|
@@ -2565,9 +2588,28 @@ var MediaController = class {
|
|
|
2565
2588
|
mimeType: file.type,
|
|
2566
2589
|
prefix
|
|
2567
2590
|
});
|
|
2591
|
+
const width = fileData.width || imageMetadata.width;
|
|
2592
|
+
const height = fileData.height || imageMetadata.height;
|
|
2593
|
+
const aspectRatio = width && height ? width / height : void 0;
|
|
2594
|
+
const customFields = {};
|
|
2595
|
+
for (const [key, val] of Object.entries(body)) {
|
|
2596
|
+
if (!["file", "focalPoint", "folderId"].includes(key) && typeof val === "string") {
|
|
2597
|
+
try {
|
|
2598
|
+
customFields[key] = JSON.parse(val);
|
|
2599
|
+
} catch {
|
|
2600
|
+
customFields[key] = val;
|
|
2601
|
+
}
|
|
2602
|
+
}
|
|
2603
|
+
}
|
|
2568
2604
|
const finalFileData = {
|
|
2605
|
+
...customFields,
|
|
2569
2606
|
...fileData,
|
|
2570
2607
|
...imageMetadata,
|
|
2608
|
+
originalFilename: file.name,
|
|
2609
|
+
folderId,
|
|
2610
|
+
width,
|
|
2611
|
+
height,
|
|
2612
|
+
aspectRatio,
|
|
2571
2613
|
focalPoint,
|
|
2572
2614
|
sizes: {}
|
|
2573
2615
|
};
|
|
@@ -2608,15 +2650,143 @@ var MediaController = class {
|
|
|
2608
2650
|
});
|
|
2609
2651
|
return c.json(doc, 201);
|
|
2610
2652
|
}
|
|
2653
|
+
async replace(c) {
|
|
2654
|
+
const id = c.req.param("id");
|
|
2655
|
+
const config = c.get("config");
|
|
2656
|
+
const storage = config.storage;
|
|
2657
|
+
const imageService = config.image;
|
|
2658
|
+
const db = config.db;
|
|
2659
|
+
if (!storage) {
|
|
2660
|
+
return c.json({ message: "Storage not configured" }, 500);
|
|
2661
|
+
}
|
|
2662
|
+
if (!db) {
|
|
2663
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
2664
|
+
}
|
|
2665
|
+
if (!id) {
|
|
2666
|
+
return c.json({ message: "Missing ID" }, 400);
|
|
2667
|
+
}
|
|
2668
|
+
const existingDoc = await db.findOne({ collection: this.collection, id });
|
|
2669
|
+
if (!existingDoc) {
|
|
2670
|
+
return c.json({ message: "Asset not found" }, 404);
|
|
2671
|
+
}
|
|
2672
|
+
const body = await c.req.parseBody();
|
|
2673
|
+
const file = body["file"];
|
|
2674
|
+
if (!file) {
|
|
2675
|
+
return c.json({ message: "No file uploaded" }, 400);
|
|
2676
|
+
}
|
|
2677
|
+
const siteId = c.get("siteId");
|
|
2678
|
+
let colConfig = config.collections.find((col) => col.slug === this.collection);
|
|
2679
|
+
if (!colConfig && config.onSchemaFetch && siteId) {
|
|
2680
|
+
const requestConfig = mergeDynamicConfig(config, await config.onSchemaFetch(siteId));
|
|
2681
|
+
colConfig = requestConfig.collections.find((col) => col.slug === this.collection);
|
|
2682
|
+
}
|
|
2683
|
+
const uploadConfig = typeof colConfig?.upload === "object" ? colConfig.upload : void 0;
|
|
2684
|
+
const validationError = validateUpload(file, uploadConfig);
|
|
2685
|
+
if (validationError) {
|
|
2686
|
+
return c.json({ message: validationError.message }, validationError.status);
|
|
2687
|
+
}
|
|
2688
|
+
const buffer = new Uint8Array(await file.arrayBuffer());
|
|
2689
|
+
const workspaceId = c.get("workspaceId");
|
|
2690
|
+
const prefix = workspaceId ? `${workspaceId}/${siteId}` : siteId || "default";
|
|
2691
|
+
const uniqueFilename = generateUniqueUploadFilename(file.name);
|
|
2692
|
+
let imageMetadata = {};
|
|
2693
|
+
let imageSizes = {};
|
|
2694
|
+
if (imageService && file.type.startsWith("image/")) {
|
|
2695
|
+
try {
|
|
2696
|
+
const processed = await imageService.process({
|
|
2697
|
+
buffer,
|
|
2698
|
+
mimeType: file.type,
|
|
2699
|
+
config: colConfig?.upload,
|
|
2700
|
+
focalPoint: existingDoc.focalPoint
|
|
2701
|
+
});
|
|
2702
|
+
imageMetadata = processed.metadata || {};
|
|
2703
|
+
imageSizes = processed.sizes || {};
|
|
2704
|
+
} catch (err) {
|
|
2705
|
+
getRequestLogger(c, this.loggerComponent).error({
|
|
2706
|
+
err,
|
|
2707
|
+
msg: "Image processing failed during replace",
|
|
2708
|
+
collection: this.collection,
|
|
2709
|
+
filename: file.name
|
|
2710
|
+
});
|
|
2711
|
+
}
|
|
2712
|
+
}
|
|
2713
|
+
const fileData = await storage.upload({
|
|
2714
|
+
filename: uniqueFilename,
|
|
2715
|
+
buffer,
|
|
2716
|
+
mimeType: file.type,
|
|
2717
|
+
prefix
|
|
2718
|
+
});
|
|
2719
|
+
const width = fileData.width || imageMetadata.width;
|
|
2720
|
+
const height = fileData.height || imageMetadata.height;
|
|
2721
|
+
const aspectRatio = width && height ? width / height : void 0;
|
|
2722
|
+
const finalFileData = {
|
|
2723
|
+
...fileData,
|
|
2724
|
+
...imageMetadata,
|
|
2725
|
+
originalFilename: file.name,
|
|
2726
|
+
width,
|
|
2727
|
+
height,
|
|
2728
|
+
aspectRatio,
|
|
2729
|
+
sizes: {}
|
|
2730
|
+
};
|
|
2731
|
+
if (imageSizes) {
|
|
2732
|
+
for (const [sizeName, sizeData] of Object.entries(imageSizes)) {
|
|
2733
|
+
const lastDot = uniqueFilename.lastIndexOf(".");
|
|
2734
|
+
const ext = lastDot !== -1 ? uniqueFilename.substring(lastDot) : "";
|
|
2735
|
+
const baseName = lastDot !== -1 ? uniqueFilename.substring(0, lastDot) : uniqueFilename;
|
|
2736
|
+
const sizeFilename = `${baseName}-${sizeName}${ext}`;
|
|
2737
|
+
try {
|
|
2738
|
+
const sizeFileData = await storage.upload({
|
|
2739
|
+
filename: sizeFilename,
|
|
2740
|
+
buffer: sizeData.buffer,
|
|
2741
|
+
mimeType: file.type,
|
|
2742
|
+
prefix
|
|
2743
|
+
});
|
|
2744
|
+
finalFileData.sizes[sizeName] = {
|
|
2745
|
+
...sizeFileData,
|
|
2746
|
+
width: sizeData.width,
|
|
2747
|
+
height: sizeData.height
|
|
2748
|
+
};
|
|
2749
|
+
} catch (err) {
|
|
2750
|
+
getRequestLogger(c, this.loggerComponent).error({
|
|
2751
|
+
err,
|
|
2752
|
+
msg: "Failed to upload image size during replace",
|
|
2753
|
+
collection: this.collection,
|
|
2754
|
+
filename: file.name,
|
|
2755
|
+
sizeName
|
|
2756
|
+
});
|
|
2757
|
+
}
|
|
2758
|
+
}
|
|
2759
|
+
}
|
|
2760
|
+
if (existingDoc.filename && existingDoc.filename !== fileData.filename) {
|
|
2761
|
+
try {
|
|
2762
|
+
await storage.delete({ filename: existingDoc.filename });
|
|
2763
|
+
} catch {
|
|
2764
|
+
}
|
|
2765
|
+
}
|
|
2766
|
+
const updatedDoc = await db.update({
|
|
2767
|
+
collection: this.collection,
|
|
2768
|
+
id,
|
|
2769
|
+
data: finalFileData
|
|
2770
|
+
});
|
|
2771
|
+
return c.json(updatedDoc, 200);
|
|
2772
|
+
}
|
|
2611
2773
|
async find(c) {
|
|
2612
2774
|
const db = c.get("config").db;
|
|
2613
2775
|
if (!db) return c.json({ message: "Database not configured" }, 500);
|
|
2614
2776
|
const limit = Number(c.req.query("limit")) || 10;
|
|
2615
2777
|
const page = Number(c.req.query("page")) || 1;
|
|
2778
|
+
const sort = c.req.query("sort") || void 0;
|
|
2779
|
+
const folderId = c.req.query("folderId");
|
|
2780
|
+
const where = {};
|
|
2781
|
+
if (folderId !== void 0) {
|
|
2782
|
+
where.folderId = folderId === "root" || folderId === "null" ? null : folderId;
|
|
2783
|
+
}
|
|
2616
2784
|
const result = await db.find({
|
|
2617
2785
|
collection: this.collection,
|
|
2618
2786
|
limit,
|
|
2619
|
-
page
|
|
2787
|
+
page,
|
|
2788
|
+
sort,
|
|
2789
|
+
where: Object.keys(where).length > 0 ? where : void 0
|
|
2620
2790
|
});
|
|
2621
2791
|
return c.json(result);
|
|
2622
2792
|
}
|
|
@@ -2645,21 +2815,195 @@ var MediaController = class {
|
|
|
2645
2815
|
async serve(c) {
|
|
2646
2816
|
const config = c.get("config");
|
|
2647
2817
|
const storage = config.storage;
|
|
2648
|
-
if (!storage
|
|
2818
|
+
if (!storage) {
|
|
2649
2819
|
return c.json({ message: "Storage not configured for serving" }, 404);
|
|
2650
2820
|
}
|
|
2651
2821
|
const filename = c.req.param("filename");
|
|
2652
2822
|
if (!filename) return c.json({ message: "Missing filename" }, 400);
|
|
2823
|
+
const key = c.req.query("key");
|
|
2824
|
+
const width = c.req.query("width") ? Number(c.req.query("width")) : void 0;
|
|
2825
|
+
const height = c.req.query("height") ? Number(c.req.query("height")) : void 0;
|
|
2826
|
+
const aspectRatio = c.req.query("aspectRatio") || c.req.query("aspect_ratio");
|
|
2827
|
+
const crop = c.req.query("crop") || c.req.query("fit");
|
|
2828
|
+
const gravity = c.req.query("gravity");
|
|
2829
|
+
const format = c.req.query("format");
|
|
2830
|
+
const qualityParam = c.req.query("quality");
|
|
2831
|
+
const quality = qualityParam ? isNaN(Number(qualityParam)) ? qualityParam : Number(qualityParam) : void 0;
|
|
2832
|
+
const download = c.req.query("download") === "true" || c.req.query("download") === "1";
|
|
2833
|
+
let transform;
|
|
2834
|
+
if (key && config.media?.presets?.[key]) {
|
|
2835
|
+
transform = { ...config.media.presets[key] };
|
|
2836
|
+
} else if (width || height || aspectRatio || crop || gravity || format || quality) {
|
|
2837
|
+
transform = { width, height, aspectRatio, crop, gravity, format, quality };
|
|
2838
|
+
}
|
|
2839
|
+
if (transform && typeof storage.getURL === "function") {
|
|
2840
|
+
const targetUrl = storage.getURL({ filename, transform });
|
|
2841
|
+
if (targetUrl && targetUrl.startsWith("http")) {
|
|
2842
|
+
return c.redirect(targetUrl, 302);
|
|
2843
|
+
}
|
|
2844
|
+
}
|
|
2845
|
+
if (!storage.resolve) {
|
|
2846
|
+
const publicUrl = storage.getURL({ filename, transform });
|
|
2847
|
+
return c.redirect(publicUrl, 302);
|
|
2848
|
+
}
|
|
2653
2849
|
let res = await storage.resolve({ filename });
|
|
2654
2850
|
if (!res && !filename.includes("/")) {
|
|
2655
2851
|
res = await storage.resolve({ filename: `default/${filename}` });
|
|
2656
2852
|
}
|
|
2657
2853
|
if (!res) return c.json({ message: "Not Found" }, 404);
|
|
2658
2854
|
c.header("Content-Type", res.mimeType);
|
|
2855
|
+
c.header("Cache-Control", "public, max-age=31536000, immutable");
|
|
2856
|
+
if (download) {
|
|
2857
|
+
const downloadName = filename.split("/").pop() || "download";
|
|
2858
|
+
c.header("Content-Disposition", `attachment; filename="${downloadName}"`);
|
|
2859
|
+
}
|
|
2659
2860
|
return c.body(res.buffer);
|
|
2660
2861
|
}
|
|
2661
2862
|
};
|
|
2662
2863
|
|
|
2864
|
+
// src/controllers/media-folders.controller.ts
|
|
2865
|
+
var MediaFolderController = class {
|
|
2866
|
+
collection;
|
|
2867
|
+
constructor(collection = "media") {
|
|
2868
|
+
this.collection = collection;
|
|
2869
|
+
}
|
|
2870
|
+
async list(c) {
|
|
2871
|
+
const config = c.get("config");
|
|
2872
|
+
const db = config.db;
|
|
2873
|
+
if (!db) {
|
|
2874
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
2875
|
+
}
|
|
2876
|
+
try {
|
|
2877
|
+
const result = await db.find({
|
|
2878
|
+
collection: "_media_folders",
|
|
2879
|
+
where: {
|
|
2880
|
+
collection: { equals: this.collection }
|
|
2881
|
+
},
|
|
2882
|
+
limit: 1e3,
|
|
2883
|
+
sort: "name"
|
|
2884
|
+
});
|
|
2885
|
+
return c.json(result);
|
|
2886
|
+
} catch {
|
|
2887
|
+
return c.json({
|
|
2888
|
+
docs: [],
|
|
2889
|
+
total: 0,
|
|
2890
|
+
limit: 1e3,
|
|
2891
|
+
page: 1,
|
|
2892
|
+
totalPages: 1,
|
|
2893
|
+
hasNextPage: false,
|
|
2894
|
+
hasPrevPage: false
|
|
2895
|
+
});
|
|
2896
|
+
}
|
|
2897
|
+
}
|
|
2898
|
+
async create(c) {
|
|
2899
|
+
const config = c.get("config");
|
|
2900
|
+
const db = config.db;
|
|
2901
|
+
if (!db) {
|
|
2902
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
2903
|
+
}
|
|
2904
|
+
const body = await c.req.json().catch(() => ({}));
|
|
2905
|
+
const name = String(body.name || "").trim();
|
|
2906
|
+
const parentId = body.parentId ? String(body.parentId) : null;
|
|
2907
|
+
const color = body.color ? String(body.color) : null;
|
|
2908
|
+
if (!name) {
|
|
2909
|
+
return c.json({ message: "Folder name is required" }, 400);
|
|
2910
|
+
}
|
|
2911
|
+
const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
2912
|
+
let path = `/${slug}`;
|
|
2913
|
+
if (parentId) {
|
|
2914
|
+
try {
|
|
2915
|
+
const parent = await db.findOne({
|
|
2916
|
+
collection: "_media_folders",
|
|
2917
|
+
id: parentId
|
|
2918
|
+
});
|
|
2919
|
+
if (parent && parent.path) {
|
|
2920
|
+
path = `${parent.path}/${slug}`;
|
|
2921
|
+
}
|
|
2922
|
+
} catch {
|
|
2923
|
+
}
|
|
2924
|
+
}
|
|
2925
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2926
|
+
const folderData = {
|
|
2927
|
+
name,
|
|
2928
|
+
slug,
|
|
2929
|
+
collection: this.collection,
|
|
2930
|
+
parentId,
|
|
2931
|
+
path,
|
|
2932
|
+
color,
|
|
2933
|
+
createdAt: now,
|
|
2934
|
+
updatedAt: now
|
|
2935
|
+
};
|
|
2936
|
+
const doc = await db.create({
|
|
2937
|
+
collection: "_media_folders",
|
|
2938
|
+
data: folderData
|
|
2939
|
+
});
|
|
2940
|
+
return c.json(doc, 201);
|
|
2941
|
+
}
|
|
2942
|
+
async update(c) {
|
|
2943
|
+
const config = c.get("config");
|
|
2944
|
+
const db = config.db;
|
|
2945
|
+
const id = c.req.param("id");
|
|
2946
|
+
if (!db) {
|
|
2947
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
2948
|
+
}
|
|
2949
|
+
if (!id) {
|
|
2950
|
+
return c.json({ message: "Folder ID is required" }, 400);
|
|
2951
|
+
}
|
|
2952
|
+
const body = await c.req.json().catch(() => ({}));
|
|
2953
|
+
const existing = await db.findOne({
|
|
2954
|
+
collection: "_media_folders",
|
|
2955
|
+
id
|
|
2956
|
+
});
|
|
2957
|
+
if (!existing) {
|
|
2958
|
+
return c.json({ message: "Folder not found" }, 404);
|
|
2959
|
+
}
|
|
2960
|
+
const updateData = {
|
|
2961
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2962
|
+
};
|
|
2963
|
+
if (body.name !== void 0) {
|
|
2964
|
+
const name = String(body.name).trim();
|
|
2965
|
+
if (!name) return c.json({ message: "Folder name cannot be empty" }, 400);
|
|
2966
|
+
updateData.name = name;
|
|
2967
|
+
updateData.slug = name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
2968
|
+
}
|
|
2969
|
+
if (body.color !== void 0) {
|
|
2970
|
+
updateData.color = body.color || null;
|
|
2971
|
+
}
|
|
2972
|
+
if (body.parentId !== void 0) {
|
|
2973
|
+
updateData.parentId = body.parentId || null;
|
|
2974
|
+
}
|
|
2975
|
+
const updated = await db.update({
|
|
2976
|
+
collection: "_media_folders",
|
|
2977
|
+
id,
|
|
2978
|
+
data: updateData
|
|
2979
|
+
});
|
|
2980
|
+
return c.json(updated);
|
|
2981
|
+
}
|
|
2982
|
+
async delete(c) {
|
|
2983
|
+
const config = c.get("config");
|
|
2984
|
+
const db = config.db;
|
|
2985
|
+
const id = c.req.param("id");
|
|
2986
|
+
if (!db) {
|
|
2987
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
2988
|
+
}
|
|
2989
|
+
if (!id) {
|
|
2990
|
+
return c.json({ message: "Folder ID is required" }, 400);
|
|
2991
|
+
}
|
|
2992
|
+
const existing = await db.findOne({
|
|
2993
|
+
collection: "_media_folders",
|
|
2994
|
+
id
|
|
2995
|
+
});
|
|
2996
|
+
if (!existing) {
|
|
2997
|
+
return c.json({ message: "Folder not found" }, 404);
|
|
2998
|
+
}
|
|
2999
|
+
await db.delete({
|
|
3000
|
+
collection: "_media_folders",
|
|
3001
|
+
id
|
|
3002
|
+
});
|
|
3003
|
+
return c.json({ success: true, id });
|
|
3004
|
+
}
|
|
3005
|
+
};
|
|
3006
|
+
|
|
2663
3007
|
// src/controllers/auth.controller.ts
|
|
2664
3008
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
2665
3009
|
|
|
@@ -4465,6 +4809,9 @@ function accessGate(config, target, action) {
|
|
|
4465
4809
|
function serializeFieldForApi(f) {
|
|
4466
4810
|
if (!f) return f;
|
|
4467
4811
|
const serialized = { ...f };
|
|
4812
|
+
if (typeof serialized.defaultValue === "function") {
|
|
4813
|
+
serialized.defaultValue = `${SERIALIZED_ADMIN_HOOK_PREFIX}${serialized.defaultValue.toString()}`;
|
|
4814
|
+
}
|
|
4468
4815
|
if (serialized.admin?.hooks) {
|
|
4469
4816
|
const hooks = { ...serialized.admin.hooks };
|
|
4470
4817
|
if (typeof hooks.onChange === "function") {
|
|
@@ -4990,6 +5337,22 @@ function registerRoutes(app, config) {
|
|
|
4990
5337
|
});
|
|
4991
5338
|
return c.json({ success: true });
|
|
4992
5339
|
});
|
|
5340
|
+
app.get("/api/media/folders", async (c) => {
|
|
5341
|
+
const folderController = new MediaFolderController("media");
|
|
5342
|
+
return folderController.list(c);
|
|
5343
|
+
});
|
|
5344
|
+
app.post("/api/media/folders", async (c) => {
|
|
5345
|
+
const folderController = new MediaFolderController("media");
|
|
5346
|
+
return folderController.create(c);
|
|
5347
|
+
});
|
|
5348
|
+
app.patch("/api/media/folders/:id", async (c) => {
|
|
5349
|
+
const folderController = new MediaFolderController("media");
|
|
5350
|
+
return folderController.update(c);
|
|
5351
|
+
});
|
|
5352
|
+
app.delete("/api/media/folders/:id", async (c) => {
|
|
5353
|
+
const folderController = new MediaFolderController("media");
|
|
5354
|
+
return folderController.delete(c);
|
|
5355
|
+
});
|
|
4993
5356
|
app.get("/api/media/:filename{.+$}", async (c) => {
|
|
4994
5357
|
const mediaController = new MediaController("media");
|
|
4995
5358
|
return mediaController.serve(c);
|
|
@@ -5002,10 +5365,17 @@ function registerRoutes(app, config) {
|
|
|
5002
5365
|
const uploadCollections = config.collections.filter((c) => c.upload);
|
|
5003
5366
|
for (const col of uploadCollections) {
|
|
5004
5367
|
const mediaController = new MediaController(col.slug);
|
|
5368
|
+
const folderController = new MediaFolderController(col.slug);
|
|
5005
5369
|
const prefix = `/api/collections/${col.slug}`;
|
|
5370
|
+
app.get(`${prefix}/folders`, accessGate(config, col, "read"), (c) => folderController.list(c));
|
|
5371
|
+
app.post(`${prefix}/folders`, accessGate(config, col, "create"), (c) => folderController.create(c));
|
|
5372
|
+
app.patch(`${prefix}/folders/:id`, accessGate(config, col, "update"), (c) => folderController.update(c));
|
|
5373
|
+
app.delete(`${prefix}/folders/:id`, accessGate(config, col, "delete"), (c) => folderController.delete(c));
|
|
5006
5374
|
app.get(`${prefix}/media`, accessGate(config, col, "read"), (c) => mediaController.find(c));
|
|
5007
5375
|
app.get(`${prefix}/media/:filename{.+$}`, (c) => mediaController.serve(c));
|
|
5008
5376
|
app.post(`${prefix}/media`, accessGate(config, col, "create"), (c) => mediaController.upload(c));
|
|
5377
|
+
app.post(`${prefix}/media/:id/file`, accessGate(config, col, "update"), (c) => mediaController.replace(c));
|
|
5378
|
+
app.put(`${prefix}/media/:id/file`, accessGate(config, col, "update"), (c) => mediaController.replace(c));
|
|
5009
5379
|
app.delete(`${prefix}/media/:id`, accessGate(config, col, "delete"), (c) => mediaController.delete(c));
|
|
5010
5380
|
}
|
|
5011
5381
|
}
|
|
@@ -10,6 +10,25 @@ var NEVER_FILTERABLE_TYPES = [
|
|
|
10
10
|
"join",
|
|
11
11
|
"collapsible"
|
|
12
12
|
];
|
|
13
|
+
var SYSTEM_FILTERABLE_FIELDS = /* @__PURE__ */ new Set([
|
|
14
|
+
"id",
|
|
15
|
+
"createdAt",
|
|
16
|
+
"updatedAt",
|
|
17
|
+
"createdBy",
|
|
18
|
+
"updatedBy",
|
|
19
|
+
"folderId",
|
|
20
|
+
"filename",
|
|
21
|
+
"originalFilename",
|
|
22
|
+
"mimeType",
|
|
23
|
+
"filesize",
|
|
24
|
+
"url",
|
|
25
|
+
"width",
|
|
26
|
+
"height",
|
|
27
|
+
"aspectRatio",
|
|
28
|
+
"blurhash",
|
|
29
|
+
"alt",
|
|
30
|
+
"caption"
|
|
31
|
+
]);
|
|
13
32
|
function sanitizeWhereClause(where, fields) {
|
|
14
33
|
const fieldMap = /* @__PURE__ */ new Map();
|
|
15
34
|
function addFieldsToMap(fList, prefix = "") {
|
|
@@ -38,7 +57,7 @@ function sanitizeWhereClause(where, fields) {
|
|
|
38
57
|
}
|
|
39
58
|
continue;
|
|
40
59
|
}
|
|
41
|
-
if (key
|
|
60
|
+
if (SYSTEM_FILTERABLE_FIELDS.has(key)) {
|
|
42
61
|
result[key] = value;
|
|
43
62
|
continue;
|
|
44
63
|
}
|