@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.cjs
CHANGED
|
@@ -2328,7 +2328,7 @@ function sanitizeWhereClause(where, fields) {
|
|
|
2328
2328
|
}
|
|
2329
2329
|
continue;
|
|
2330
2330
|
}
|
|
2331
|
-
if (key
|
|
2331
|
+
if (SYSTEM_FILTERABLE_FIELDS.has(key)) {
|
|
2332
2332
|
result[key] = value;
|
|
2333
2333
|
continue;
|
|
2334
2334
|
}
|
|
@@ -2342,7 +2342,7 @@ function sanitizeWhereClause(where, fields) {
|
|
|
2342
2342
|
}
|
|
2343
2343
|
return walk(where);
|
|
2344
2344
|
}
|
|
2345
|
-
var NEVER_FILTERABLE_TYPES;
|
|
2345
|
+
var NEVER_FILTERABLE_TYPES, SYSTEM_FILTERABLE_FIELDS;
|
|
2346
2346
|
var init_where_sanitizer = __esm({
|
|
2347
2347
|
"src/utils/where-sanitizer.ts"() {
|
|
2348
2348
|
"use strict";
|
|
@@ -2355,6 +2355,25 @@ var init_where_sanitizer = __esm({
|
|
|
2355
2355
|
"join",
|
|
2356
2356
|
"collapsible"
|
|
2357
2357
|
];
|
|
2358
|
+
SYSTEM_FILTERABLE_FIELDS = /* @__PURE__ */ new Set([
|
|
2359
|
+
"id",
|
|
2360
|
+
"createdAt",
|
|
2361
|
+
"updatedAt",
|
|
2362
|
+
"createdBy",
|
|
2363
|
+
"updatedBy",
|
|
2364
|
+
"folderId",
|
|
2365
|
+
"filename",
|
|
2366
|
+
"originalFilename",
|
|
2367
|
+
"mimeType",
|
|
2368
|
+
"filesize",
|
|
2369
|
+
"url",
|
|
2370
|
+
"width",
|
|
2371
|
+
"height",
|
|
2372
|
+
"aspectRatio",
|
|
2373
|
+
"blurhash",
|
|
2374
|
+
"alt",
|
|
2375
|
+
"caption"
|
|
2376
|
+
]);
|
|
2358
2377
|
}
|
|
2359
2378
|
});
|
|
2360
2379
|
|
|
@@ -4511,7 +4530,7 @@ function mergeWhereConstraint(where, constraint) {
|
|
|
4511
4530
|
|
|
4512
4531
|
// src/utils/action-mutation.ts
|
|
4513
4532
|
function resolveActionMutation(mutation, ctx) {
|
|
4514
|
-
if (!mutation) return {};
|
|
4533
|
+
if (!mutation) return { ...ctx.input ?? {} };
|
|
4515
4534
|
return resolveValue(mutation, ctx);
|
|
4516
4535
|
}
|
|
4517
4536
|
function resolveValue(value, ctx) {
|
|
@@ -6141,9 +6160,31 @@ var CollectionController = class {
|
|
|
6141
6160
|
const body = await c.req.json().catch(() => null);
|
|
6142
6161
|
if (body?.ids && Array.isArray(body.ids)) {
|
|
6143
6162
|
ids = body.ids;
|
|
6163
|
+
} else if (Array.isArray(body)) {
|
|
6164
|
+
ids = body;
|
|
6144
6165
|
}
|
|
6145
6166
|
} catch {
|
|
6146
6167
|
}
|
|
6168
|
+
if (!ids.length) {
|
|
6169
|
+
try {
|
|
6170
|
+
const parsed = await c.req.parseBody().catch(() => null);
|
|
6171
|
+
if (parsed) {
|
|
6172
|
+
if (Array.isArray(parsed.ids)) {
|
|
6173
|
+
ids = parsed.ids.filter((x) => typeof x === "string");
|
|
6174
|
+
} else if (Array.isArray(parsed["ids[]"])) {
|
|
6175
|
+
ids = parsed["ids[]"].filter((x) => typeof x === "string");
|
|
6176
|
+
} else if (typeof parsed.ids === "string") {
|
|
6177
|
+
try {
|
|
6178
|
+
const json = JSON.parse(parsed.ids);
|
|
6179
|
+
if (Array.isArray(json)) ids = json;
|
|
6180
|
+
} catch {
|
|
6181
|
+
ids = [parsed.ids];
|
|
6182
|
+
}
|
|
6183
|
+
}
|
|
6184
|
+
}
|
|
6185
|
+
} catch {
|
|
6186
|
+
}
|
|
6187
|
+
}
|
|
6147
6188
|
if (!ids.length) {
|
|
6148
6189
|
const raw = c.req.queries("ids") ?? c.req.queries("ids[]") ?? [];
|
|
6149
6190
|
ids = raw.filter(Boolean);
|
|
@@ -6675,6 +6716,7 @@ var MediaController = class {
|
|
|
6675
6716
|
}
|
|
6676
6717
|
const body = await c.req.parseBody();
|
|
6677
6718
|
const file = body["file"];
|
|
6719
|
+
const folderId = body["folderId"] || null;
|
|
6678
6720
|
const focalPointStr = body["focalPoint"];
|
|
6679
6721
|
const focalPoint = focalPointStr ? JSON.parse(focalPointStr) : void 0;
|
|
6680
6722
|
if (!file) {
|
|
@@ -6715,8 +6757,8 @@ var MediaController = class {
|
|
|
6715
6757
|
config: colConfig?.upload,
|
|
6716
6758
|
focalPoint
|
|
6717
6759
|
});
|
|
6718
|
-
imageMetadata = processed.metadata;
|
|
6719
|
-
imageSizes = processed.sizes;
|
|
6760
|
+
imageMetadata = processed.metadata || {};
|
|
6761
|
+
imageSizes = processed.sizes || {};
|
|
6720
6762
|
} catch (err) {
|
|
6721
6763
|
getRequestLogger(c, this.loggerComponent).error({
|
|
6722
6764
|
err,
|
|
@@ -6732,9 +6774,28 @@ var MediaController = class {
|
|
|
6732
6774
|
mimeType: file.type,
|
|
6733
6775
|
prefix
|
|
6734
6776
|
});
|
|
6777
|
+
const width = fileData.width || imageMetadata.width;
|
|
6778
|
+
const height = fileData.height || imageMetadata.height;
|
|
6779
|
+
const aspectRatio = width && height ? width / height : void 0;
|
|
6780
|
+
const customFields = {};
|
|
6781
|
+
for (const [key, val] of Object.entries(body)) {
|
|
6782
|
+
if (!["file", "focalPoint", "folderId"].includes(key) && typeof val === "string") {
|
|
6783
|
+
try {
|
|
6784
|
+
customFields[key] = JSON.parse(val);
|
|
6785
|
+
} catch {
|
|
6786
|
+
customFields[key] = val;
|
|
6787
|
+
}
|
|
6788
|
+
}
|
|
6789
|
+
}
|
|
6735
6790
|
const finalFileData = {
|
|
6791
|
+
...customFields,
|
|
6736
6792
|
...fileData,
|
|
6737
6793
|
...imageMetadata,
|
|
6794
|
+
originalFilename: file.name,
|
|
6795
|
+
folderId,
|
|
6796
|
+
width,
|
|
6797
|
+
height,
|
|
6798
|
+
aspectRatio,
|
|
6738
6799
|
focalPoint,
|
|
6739
6800
|
sizes: {}
|
|
6740
6801
|
};
|
|
@@ -6775,15 +6836,143 @@ var MediaController = class {
|
|
|
6775
6836
|
});
|
|
6776
6837
|
return c.json(doc, 201);
|
|
6777
6838
|
}
|
|
6839
|
+
async replace(c) {
|
|
6840
|
+
const id = c.req.param("id");
|
|
6841
|
+
const config = c.get("config");
|
|
6842
|
+
const storage = config.storage;
|
|
6843
|
+
const imageService = config.image;
|
|
6844
|
+
const db = config.db;
|
|
6845
|
+
if (!storage) {
|
|
6846
|
+
return c.json({ message: "Storage not configured" }, 500);
|
|
6847
|
+
}
|
|
6848
|
+
if (!db) {
|
|
6849
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
6850
|
+
}
|
|
6851
|
+
if (!id) {
|
|
6852
|
+
return c.json({ message: "Missing ID" }, 400);
|
|
6853
|
+
}
|
|
6854
|
+
const existingDoc = await db.findOne({ collection: this.collection, id });
|
|
6855
|
+
if (!existingDoc) {
|
|
6856
|
+
return c.json({ message: "Asset not found" }, 404);
|
|
6857
|
+
}
|
|
6858
|
+
const body = await c.req.parseBody();
|
|
6859
|
+
const file = body["file"];
|
|
6860
|
+
if (!file) {
|
|
6861
|
+
return c.json({ message: "No file uploaded" }, 400);
|
|
6862
|
+
}
|
|
6863
|
+
const siteId = c.get("siteId");
|
|
6864
|
+
let colConfig = config.collections.find((col) => col.slug === this.collection);
|
|
6865
|
+
if (!colConfig && config.onSchemaFetch && siteId) {
|
|
6866
|
+
const requestConfig = mergeDynamicConfig(config, await config.onSchemaFetch(siteId));
|
|
6867
|
+
colConfig = requestConfig.collections.find((col) => col.slug === this.collection);
|
|
6868
|
+
}
|
|
6869
|
+
const uploadConfig = typeof colConfig?.upload === "object" ? colConfig.upload : void 0;
|
|
6870
|
+
const validationError = validateUpload(file, uploadConfig);
|
|
6871
|
+
if (validationError) {
|
|
6872
|
+
return c.json({ message: validationError.message }, validationError.status);
|
|
6873
|
+
}
|
|
6874
|
+
const buffer = new Uint8Array(await file.arrayBuffer());
|
|
6875
|
+
const workspaceId = c.get("workspaceId");
|
|
6876
|
+
const prefix = workspaceId ? `${workspaceId}/${siteId}` : siteId || "default";
|
|
6877
|
+
const uniqueFilename = generateUniqueUploadFilename(file.name);
|
|
6878
|
+
let imageMetadata = {};
|
|
6879
|
+
let imageSizes = {};
|
|
6880
|
+
if (imageService && file.type.startsWith("image/")) {
|
|
6881
|
+
try {
|
|
6882
|
+
const processed = await imageService.process({
|
|
6883
|
+
buffer,
|
|
6884
|
+
mimeType: file.type,
|
|
6885
|
+
config: colConfig?.upload,
|
|
6886
|
+
focalPoint: existingDoc.focalPoint
|
|
6887
|
+
});
|
|
6888
|
+
imageMetadata = processed.metadata || {};
|
|
6889
|
+
imageSizes = processed.sizes || {};
|
|
6890
|
+
} catch (err) {
|
|
6891
|
+
getRequestLogger(c, this.loggerComponent).error({
|
|
6892
|
+
err,
|
|
6893
|
+
msg: "Image processing failed during replace",
|
|
6894
|
+
collection: this.collection,
|
|
6895
|
+
filename: file.name
|
|
6896
|
+
});
|
|
6897
|
+
}
|
|
6898
|
+
}
|
|
6899
|
+
const fileData = await storage.upload({
|
|
6900
|
+
filename: uniqueFilename,
|
|
6901
|
+
buffer,
|
|
6902
|
+
mimeType: file.type,
|
|
6903
|
+
prefix
|
|
6904
|
+
});
|
|
6905
|
+
const width = fileData.width || imageMetadata.width;
|
|
6906
|
+
const height = fileData.height || imageMetadata.height;
|
|
6907
|
+
const aspectRatio = width && height ? width / height : void 0;
|
|
6908
|
+
const finalFileData = {
|
|
6909
|
+
...fileData,
|
|
6910
|
+
...imageMetadata,
|
|
6911
|
+
originalFilename: file.name,
|
|
6912
|
+
width,
|
|
6913
|
+
height,
|
|
6914
|
+
aspectRatio,
|
|
6915
|
+
sizes: {}
|
|
6916
|
+
};
|
|
6917
|
+
if (imageSizes) {
|
|
6918
|
+
for (const [sizeName, sizeData] of Object.entries(imageSizes)) {
|
|
6919
|
+
const lastDot = uniqueFilename.lastIndexOf(".");
|
|
6920
|
+
const ext = lastDot !== -1 ? uniqueFilename.substring(lastDot) : "";
|
|
6921
|
+
const baseName = lastDot !== -1 ? uniqueFilename.substring(0, lastDot) : uniqueFilename;
|
|
6922
|
+
const sizeFilename = `${baseName}-${sizeName}${ext}`;
|
|
6923
|
+
try {
|
|
6924
|
+
const sizeFileData = await storage.upload({
|
|
6925
|
+
filename: sizeFilename,
|
|
6926
|
+
buffer: sizeData.buffer,
|
|
6927
|
+
mimeType: file.type,
|
|
6928
|
+
prefix
|
|
6929
|
+
});
|
|
6930
|
+
finalFileData.sizes[sizeName] = {
|
|
6931
|
+
...sizeFileData,
|
|
6932
|
+
width: sizeData.width,
|
|
6933
|
+
height: sizeData.height
|
|
6934
|
+
};
|
|
6935
|
+
} catch (err) {
|
|
6936
|
+
getRequestLogger(c, this.loggerComponent).error({
|
|
6937
|
+
err,
|
|
6938
|
+
msg: "Failed to upload image size during replace",
|
|
6939
|
+
collection: this.collection,
|
|
6940
|
+
filename: file.name,
|
|
6941
|
+
sizeName
|
|
6942
|
+
});
|
|
6943
|
+
}
|
|
6944
|
+
}
|
|
6945
|
+
}
|
|
6946
|
+
if (existingDoc.filename && existingDoc.filename !== fileData.filename) {
|
|
6947
|
+
try {
|
|
6948
|
+
await storage.delete({ filename: existingDoc.filename });
|
|
6949
|
+
} catch {
|
|
6950
|
+
}
|
|
6951
|
+
}
|
|
6952
|
+
const updatedDoc = await db.update({
|
|
6953
|
+
collection: this.collection,
|
|
6954
|
+
id,
|
|
6955
|
+
data: finalFileData
|
|
6956
|
+
});
|
|
6957
|
+
return c.json(updatedDoc, 200);
|
|
6958
|
+
}
|
|
6778
6959
|
async find(c) {
|
|
6779
6960
|
const db = c.get("config").db;
|
|
6780
6961
|
if (!db) return c.json({ message: "Database not configured" }, 500);
|
|
6781
6962
|
const limit = Number(c.req.query("limit")) || 10;
|
|
6782
6963
|
const page = Number(c.req.query("page")) || 1;
|
|
6964
|
+
const sort = c.req.query("sort") || void 0;
|
|
6965
|
+
const folderId = c.req.query("folderId");
|
|
6966
|
+
const where = {};
|
|
6967
|
+
if (folderId !== void 0) {
|
|
6968
|
+
where.folderId = folderId === "root" || folderId === "null" ? null : folderId;
|
|
6969
|
+
}
|
|
6783
6970
|
const result = await db.find({
|
|
6784
6971
|
collection: this.collection,
|
|
6785
6972
|
limit,
|
|
6786
|
-
page
|
|
6973
|
+
page,
|
|
6974
|
+
sort,
|
|
6975
|
+
where: Object.keys(where).length > 0 ? where : void 0
|
|
6787
6976
|
});
|
|
6788
6977
|
return c.json(result);
|
|
6789
6978
|
}
|
|
@@ -6812,21 +7001,195 @@ var MediaController = class {
|
|
|
6812
7001
|
async serve(c) {
|
|
6813
7002
|
const config = c.get("config");
|
|
6814
7003
|
const storage = config.storage;
|
|
6815
|
-
if (!storage
|
|
7004
|
+
if (!storage) {
|
|
6816
7005
|
return c.json({ message: "Storage not configured for serving" }, 404);
|
|
6817
7006
|
}
|
|
6818
7007
|
const filename = c.req.param("filename");
|
|
6819
7008
|
if (!filename) return c.json({ message: "Missing filename" }, 400);
|
|
7009
|
+
const key = c.req.query("key");
|
|
7010
|
+
const width = c.req.query("width") ? Number(c.req.query("width")) : void 0;
|
|
7011
|
+
const height = c.req.query("height") ? Number(c.req.query("height")) : void 0;
|
|
7012
|
+
const aspectRatio = c.req.query("aspectRatio") || c.req.query("aspect_ratio");
|
|
7013
|
+
const crop = c.req.query("crop") || c.req.query("fit");
|
|
7014
|
+
const gravity = c.req.query("gravity");
|
|
7015
|
+
const format = c.req.query("format");
|
|
7016
|
+
const qualityParam = c.req.query("quality");
|
|
7017
|
+
const quality = qualityParam ? isNaN(Number(qualityParam)) ? qualityParam : Number(qualityParam) : void 0;
|
|
7018
|
+
const download = c.req.query("download") === "true" || c.req.query("download") === "1";
|
|
7019
|
+
let transform;
|
|
7020
|
+
if (key && config.media?.presets?.[key]) {
|
|
7021
|
+
transform = { ...config.media.presets[key] };
|
|
7022
|
+
} else if (width || height || aspectRatio || crop || gravity || format || quality) {
|
|
7023
|
+
transform = { width, height, aspectRatio, crop, gravity, format, quality };
|
|
7024
|
+
}
|
|
7025
|
+
if (transform && typeof storage.getURL === "function") {
|
|
7026
|
+
const targetUrl = storage.getURL({ filename, transform });
|
|
7027
|
+
if (targetUrl && targetUrl.startsWith("http")) {
|
|
7028
|
+
return c.redirect(targetUrl, 302);
|
|
7029
|
+
}
|
|
7030
|
+
}
|
|
7031
|
+
if (!storage.resolve) {
|
|
7032
|
+
const publicUrl = storage.getURL({ filename, transform });
|
|
7033
|
+
return c.redirect(publicUrl, 302);
|
|
7034
|
+
}
|
|
6820
7035
|
let res = await storage.resolve({ filename });
|
|
6821
7036
|
if (!res && !filename.includes("/")) {
|
|
6822
7037
|
res = await storage.resolve({ filename: `default/${filename}` });
|
|
6823
7038
|
}
|
|
6824
7039
|
if (!res) return c.json({ message: "Not Found" }, 404);
|
|
6825
7040
|
c.header("Content-Type", res.mimeType);
|
|
7041
|
+
c.header("Cache-Control", "public, max-age=31536000, immutable");
|
|
7042
|
+
if (download) {
|
|
7043
|
+
const downloadName = filename.split("/").pop() || "download";
|
|
7044
|
+
c.header("Content-Disposition", `attachment; filename="${downloadName}"`);
|
|
7045
|
+
}
|
|
6826
7046
|
return c.body(res.buffer);
|
|
6827
7047
|
}
|
|
6828
7048
|
};
|
|
6829
7049
|
|
|
7050
|
+
// src/controllers/media-folders.controller.ts
|
|
7051
|
+
var MediaFolderController = class {
|
|
7052
|
+
collection;
|
|
7053
|
+
constructor(collection = "media") {
|
|
7054
|
+
this.collection = collection;
|
|
7055
|
+
}
|
|
7056
|
+
async list(c) {
|
|
7057
|
+
const config = c.get("config");
|
|
7058
|
+
const db = config.db;
|
|
7059
|
+
if (!db) {
|
|
7060
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
7061
|
+
}
|
|
7062
|
+
try {
|
|
7063
|
+
const result = await db.find({
|
|
7064
|
+
collection: "_media_folders",
|
|
7065
|
+
where: {
|
|
7066
|
+
collection: { equals: this.collection }
|
|
7067
|
+
},
|
|
7068
|
+
limit: 1e3,
|
|
7069
|
+
sort: "name"
|
|
7070
|
+
});
|
|
7071
|
+
return c.json(result);
|
|
7072
|
+
} catch {
|
|
7073
|
+
return c.json({
|
|
7074
|
+
docs: [],
|
|
7075
|
+
total: 0,
|
|
7076
|
+
limit: 1e3,
|
|
7077
|
+
page: 1,
|
|
7078
|
+
totalPages: 1,
|
|
7079
|
+
hasNextPage: false,
|
|
7080
|
+
hasPrevPage: false
|
|
7081
|
+
});
|
|
7082
|
+
}
|
|
7083
|
+
}
|
|
7084
|
+
async create(c) {
|
|
7085
|
+
const config = c.get("config");
|
|
7086
|
+
const db = config.db;
|
|
7087
|
+
if (!db) {
|
|
7088
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
7089
|
+
}
|
|
7090
|
+
const body = await c.req.json().catch(() => ({}));
|
|
7091
|
+
const name = String(body.name || "").trim();
|
|
7092
|
+
const parentId = body.parentId ? String(body.parentId) : null;
|
|
7093
|
+
const color = body.color ? String(body.color) : null;
|
|
7094
|
+
if (!name) {
|
|
7095
|
+
return c.json({ message: "Folder name is required" }, 400);
|
|
7096
|
+
}
|
|
7097
|
+
const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
7098
|
+
let path = `/${slug}`;
|
|
7099
|
+
if (parentId) {
|
|
7100
|
+
try {
|
|
7101
|
+
const parent = await db.findOne({
|
|
7102
|
+
collection: "_media_folders",
|
|
7103
|
+
id: parentId
|
|
7104
|
+
});
|
|
7105
|
+
if (parent && parent.path) {
|
|
7106
|
+
path = `${parent.path}/${slug}`;
|
|
7107
|
+
}
|
|
7108
|
+
} catch {
|
|
7109
|
+
}
|
|
7110
|
+
}
|
|
7111
|
+
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
7112
|
+
const folderData = {
|
|
7113
|
+
name,
|
|
7114
|
+
slug,
|
|
7115
|
+
collection: this.collection,
|
|
7116
|
+
parentId,
|
|
7117
|
+
path,
|
|
7118
|
+
color,
|
|
7119
|
+
createdAt: now2,
|
|
7120
|
+
updatedAt: now2
|
|
7121
|
+
};
|
|
7122
|
+
const doc = await db.create({
|
|
7123
|
+
collection: "_media_folders",
|
|
7124
|
+
data: folderData
|
|
7125
|
+
});
|
|
7126
|
+
return c.json(doc, 201);
|
|
7127
|
+
}
|
|
7128
|
+
async update(c) {
|
|
7129
|
+
const config = c.get("config");
|
|
7130
|
+
const db = config.db;
|
|
7131
|
+
const id = c.req.param("id");
|
|
7132
|
+
if (!db) {
|
|
7133
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
7134
|
+
}
|
|
7135
|
+
if (!id) {
|
|
7136
|
+
return c.json({ message: "Folder ID is required" }, 400);
|
|
7137
|
+
}
|
|
7138
|
+
const body = await c.req.json().catch(() => ({}));
|
|
7139
|
+
const existing = await db.findOne({
|
|
7140
|
+
collection: "_media_folders",
|
|
7141
|
+
id
|
|
7142
|
+
});
|
|
7143
|
+
if (!existing) {
|
|
7144
|
+
return c.json({ message: "Folder not found" }, 404);
|
|
7145
|
+
}
|
|
7146
|
+
const updateData = {
|
|
7147
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7148
|
+
};
|
|
7149
|
+
if (body.name !== void 0) {
|
|
7150
|
+
const name = String(body.name).trim();
|
|
7151
|
+
if (!name) return c.json({ message: "Folder name cannot be empty" }, 400);
|
|
7152
|
+
updateData.name = name;
|
|
7153
|
+
updateData.slug = name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
7154
|
+
}
|
|
7155
|
+
if (body.color !== void 0) {
|
|
7156
|
+
updateData.color = body.color || null;
|
|
7157
|
+
}
|
|
7158
|
+
if (body.parentId !== void 0) {
|
|
7159
|
+
updateData.parentId = body.parentId || null;
|
|
7160
|
+
}
|
|
7161
|
+
const updated = await db.update({
|
|
7162
|
+
collection: "_media_folders",
|
|
7163
|
+
id,
|
|
7164
|
+
data: updateData
|
|
7165
|
+
});
|
|
7166
|
+
return c.json(updated);
|
|
7167
|
+
}
|
|
7168
|
+
async delete(c) {
|
|
7169
|
+
const config = c.get("config");
|
|
7170
|
+
const db = config.db;
|
|
7171
|
+
const id = c.req.param("id");
|
|
7172
|
+
if (!db) {
|
|
7173
|
+
return c.json({ message: "Database not configured" }, 500);
|
|
7174
|
+
}
|
|
7175
|
+
if (!id) {
|
|
7176
|
+
return c.json({ message: "Folder ID is required" }, 400);
|
|
7177
|
+
}
|
|
7178
|
+
const existing = await db.findOne({
|
|
7179
|
+
collection: "_media_folders",
|
|
7180
|
+
id
|
|
7181
|
+
});
|
|
7182
|
+
if (!existing) {
|
|
7183
|
+
return c.json({ message: "Folder not found" }, 404);
|
|
7184
|
+
}
|
|
7185
|
+
await db.delete({
|
|
7186
|
+
collection: "_media_folders",
|
|
7187
|
+
id
|
|
7188
|
+
});
|
|
7189
|
+
return c.json({ success: true, id });
|
|
7190
|
+
}
|
|
7191
|
+
};
|
|
7192
|
+
|
|
6830
7193
|
// src/controllers/auth.controller.ts
|
|
6831
7194
|
var import_node_crypto2 = require("crypto");
|
|
6832
7195
|
|
|
@@ -9740,6 +10103,9 @@ function accessGate(config, target, action) {
|
|
|
9740
10103
|
function serializeFieldForApi(f) {
|
|
9741
10104
|
if (!f) return f;
|
|
9742
10105
|
const serialized = { ...f };
|
|
10106
|
+
if (typeof serialized.defaultValue === "function") {
|
|
10107
|
+
serialized.defaultValue = `${SERIALIZED_ADMIN_HOOK_PREFIX}${serialized.defaultValue.toString()}`;
|
|
10108
|
+
}
|
|
9743
10109
|
if (serialized.admin?.hooks) {
|
|
9744
10110
|
const hooks = { ...serialized.admin.hooks };
|
|
9745
10111
|
if (typeof hooks.onChange === "function") {
|
|
@@ -10265,6 +10631,22 @@ function registerRoutes(app, config) {
|
|
|
10265
10631
|
});
|
|
10266
10632
|
return c.json({ success: true });
|
|
10267
10633
|
});
|
|
10634
|
+
app.get("/api/media/folders", async (c) => {
|
|
10635
|
+
const folderController = new MediaFolderController("media");
|
|
10636
|
+
return folderController.list(c);
|
|
10637
|
+
});
|
|
10638
|
+
app.post("/api/media/folders", async (c) => {
|
|
10639
|
+
const folderController = new MediaFolderController("media");
|
|
10640
|
+
return folderController.create(c);
|
|
10641
|
+
});
|
|
10642
|
+
app.patch("/api/media/folders/:id", async (c) => {
|
|
10643
|
+
const folderController = new MediaFolderController("media");
|
|
10644
|
+
return folderController.update(c);
|
|
10645
|
+
});
|
|
10646
|
+
app.delete("/api/media/folders/:id", async (c) => {
|
|
10647
|
+
const folderController = new MediaFolderController("media");
|
|
10648
|
+
return folderController.delete(c);
|
|
10649
|
+
});
|
|
10268
10650
|
app.get("/api/media/:filename{.+$}", async (c) => {
|
|
10269
10651
|
const mediaController = new MediaController("media");
|
|
10270
10652
|
return mediaController.serve(c);
|
|
@@ -10277,10 +10659,17 @@ function registerRoutes(app, config) {
|
|
|
10277
10659
|
const uploadCollections = config.collections.filter((c) => c.upload);
|
|
10278
10660
|
for (const col of uploadCollections) {
|
|
10279
10661
|
const mediaController = new MediaController(col.slug);
|
|
10662
|
+
const folderController = new MediaFolderController(col.slug);
|
|
10280
10663
|
const prefix = `/api/collections/${col.slug}`;
|
|
10664
|
+
app.get(`${prefix}/folders`, accessGate(config, col, "read"), (c) => folderController.list(c));
|
|
10665
|
+
app.post(`${prefix}/folders`, accessGate(config, col, "create"), (c) => folderController.create(c));
|
|
10666
|
+
app.patch(`${prefix}/folders/:id`, accessGate(config, col, "update"), (c) => folderController.update(c));
|
|
10667
|
+
app.delete(`${prefix}/folders/:id`, accessGate(config, col, "delete"), (c) => folderController.delete(c));
|
|
10281
10668
|
app.get(`${prefix}/media`, accessGate(config, col, "read"), (c) => mediaController.find(c));
|
|
10282
10669
|
app.get(`${prefix}/media/:filename{.+$}`, (c) => mediaController.serve(c));
|
|
10283
10670
|
app.post(`${prefix}/media`, accessGate(config, col, "create"), (c) => mediaController.upload(c));
|
|
10671
|
+
app.post(`${prefix}/media/:id/file`, accessGate(config, col, "update"), (c) => mediaController.replace(c));
|
|
10672
|
+
app.put(`${prefix}/media/:id/file`, accessGate(config, col, "update"), (c) => mediaController.replace(c));
|
|
10284
10673
|
app.delete(`${prefix}/media/:id`, accessGate(config, col, "delete"), (c) => mediaController.delete(c));
|
|
10285
10674
|
}
|
|
10286
10675
|
}
|
|
@@ -10498,6 +10887,21 @@ var SYSTEM_FIELDS = [
|
|
|
10498
10887
|
admin: { readOnly: true, hidden: true }
|
|
10499
10888
|
}
|
|
10500
10889
|
];
|
|
10890
|
+
var MEDIA_SYSTEM_FIELDS = [
|
|
10891
|
+
{ name: "folderId", type: "text", label: "Folder", admin: { hidden: true } },
|
|
10892
|
+
{ name: "filename", type: "text", label: "Filename", required: true },
|
|
10893
|
+
{ name: "originalFilename", type: "text", label: "Original Filename", admin: { hidden: true } },
|
|
10894
|
+
{ name: "mimeType", type: "text", label: "MIME Type", admin: { hidden: true } },
|
|
10895
|
+
{ name: "filesize", type: "number", label: "File Size", admin: { hidden: true } },
|
|
10896
|
+
{ name: "url", type: "text", label: "URL", admin: { hidden: true } },
|
|
10897
|
+
{ name: "width", type: "number", label: "Width", admin: { hidden: true } },
|
|
10898
|
+
{ name: "height", type: "number", label: "Height", admin: { hidden: true } },
|
|
10899
|
+
{ name: "aspectRatio", type: "number", label: "Aspect Ratio", admin: { hidden: true } },
|
|
10900
|
+
{ name: "blurhash", type: "text", label: "Blurhash", admin: { hidden: true } },
|
|
10901
|
+
{ name: "focalPoint", type: "json", label: "Focal Point", admin: { hidden: true } },
|
|
10902
|
+
{ name: "alt", type: "text", label: "Alt Text" },
|
|
10903
|
+
{ name: "caption", type: "text", label: "Caption" }
|
|
10904
|
+
];
|
|
10501
10905
|
var AUDIT_COLLECTION = {
|
|
10502
10906
|
slug: AUDIT_COLLECTION_SLUG,
|
|
10503
10907
|
labels: { singular: "Audit Log", plural: "Audit Logs" },
|
|
@@ -10769,6 +11173,11 @@ function normalizeConfig(config) {
|
|
|
10769
11173
|
}
|
|
10770
11174
|
}
|
|
10771
11175
|
}
|
|
11176
|
+
if (col.upload || col.slug === "media") {
|
|
11177
|
+
const currentNames = new Set(fields.map((f) => f.name));
|
|
11178
|
+
const mediaFieldsToInject = MEDIA_SYSTEM_FIELDS.filter((f) => !currentNames.has(f.name));
|
|
11179
|
+
fields = [...fields, ...mediaFieldsToInject];
|
|
11180
|
+
}
|
|
10772
11181
|
const updatedFieldNames = new Set(fields.map((f) => f.name));
|
|
10773
11182
|
const fieldsToInject = SYSTEM_FIELDS.filter((f) => !updatedFieldNames.has(f.name));
|
|
10774
11183
|
const workflow = col.workflow || (col.drafts ? simplePublishingWorkflow() : void 0);
|
package/dist/server.d.cts
CHANGED
|
@@ -2,7 +2,7 @@ import * as hono_types from 'hono/types';
|
|
|
2
2
|
import * as hono from 'hono';
|
|
3
3
|
import { Context, Hono } from 'hono';
|
|
4
4
|
import { Logger } from 'pino';
|
|
5
|
-
import { D as DyrectedConfig, b5 as DyrectedObservabilityConfig, C as CollectionConfig, G as GlobalConfig } from './app-config-
|
|
5
|
+
import { D as DyrectedConfig, b5 as DyrectedObservabilityConfig, C as CollectionConfig, G as GlobalConfig } from './app-config-C6hYM8WO.cjs';
|
|
6
6
|
import { JWTPayload } from 'jose';
|
|
7
7
|
import { Tracer, Counter, Attributes, Histogram, Span } from '@opentelemetry/api';
|
|
8
8
|
import { PrometheusExporter } from '@opentelemetry/exporter-prometheus';
|
|
@@ -540,6 +540,18 @@ declare class MediaController {
|
|
|
540
540
|
[x: string]: any;
|
|
541
541
|
id: string;
|
|
542
542
|
}, 201, "json">)>;
|
|
543
|
+
replace(c: Context<DyrectedContext>): Promise<(Response & hono.TypedResponse<{
|
|
544
|
+
message: string;
|
|
545
|
+
}, 500, "json">) | (Response & hono.TypedResponse<{
|
|
546
|
+
message: string;
|
|
547
|
+
}, 400, "json">) | (Response & hono.TypedResponse<{
|
|
548
|
+
message: string;
|
|
549
|
+
}, 404, "json">) | (Response & hono.TypedResponse<{
|
|
550
|
+
message: string;
|
|
551
|
+
}, 413 | 415, "json">) | (Response & hono.TypedResponse<{
|
|
552
|
+
[x: string]: any;
|
|
553
|
+
id: string;
|
|
554
|
+
}, 200, "json">)>;
|
|
543
555
|
find(c: Context<DyrectedContext>): Promise<(Response & hono.TypedResponse<{
|
|
544
556
|
docs: {
|
|
545
557
|
[x: string]: any;
|
|
@@ -560,7 +572,7 @@ declare class MediaController {
|
|
|
560
572
|
message: string;
|
|
561
573
|
}, 404, "json">) | (Response & hono.TypedResponse<{
|
|
562
574
|
message: string;
|
|
563
|
-
}, 400, "json">) | (Response & hono.TypedResponse<any, hono_utils_http_status.ContentfulStatusCode, "body">)>;
|
|
575
|
+
}, 400, "json">) | (Response & hono.TypedResponse<undefined, 302, "redirect">) | (Response & hono.TypedResponse<any, hono_utils_http_status.ContentfulStatusCode, "body">)>;
|
|
564
576
|
}
|
|
565
577
|
|
|
566
578
|
declare class PreviewController {
|
package/dist/server.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import * as hono_types from 'hono/types';
|
|
|
2
2
|
import * as hono from 'hono';
|
|
3
3
|
import { Context, Hono } from 'hono';
|
|
4
4
|
import { Logger } from 'pino';
|
|
5
|
-
import { D as DyrectedConfig, b5 as DyrectedObservabilityConfig, C as CollectionConfig, G as GlobalConfig } from './app-config-
|
|
5
|
+
import { D as DyrectedConfig, b5 as DyrectedObservabilityConfig, C as CollectionConfig, G as GlobalConfig } from './app-config-C6hYM8WO.js';
|
|
6
6
|
import { JWTPayload } from 'jose';
|
|
7
7
|
import { Tracer, Counter, Attributes, Histogram, Span } from '@opentelemetry/api';
|
|
8
8
|
import { PrometheusExporter } from '@opentelemetry/exporter-prometheus';
|
|
@@ -540,6 +540,18 @@ declare class MediaController {
|
|
|
540
540
|
[x: string]: any;
|
|
541
541
|
id: string;
|
|
542
542
|
}, 201, "json">)>;
|
|
543
|
+
replace(c: Context<DyrectedContext>): Promise<(Response & hono.TypedResponse<{
|
|
544
|
+
message: string;
|
|
545
|
+
}, 500, "json">) | (Response & hono.TypedResponse<{
|
|
546
|
+
message: string;
|
|
547
|
+
}, 400, "json">) | (Response & hono.TypedResponse<{
|
|
548
|
+
message: string;
|
|
549
|
+
}, 404, "json">) | (Response & hono.TypedResponse<{
|
|
550
|
+
message: string;
|
|
551
|
+
}, 413 | 415, "json">) | (Response & hono.TypedResponse<{
|
|
552
|
+
[x: string]: any;
|
|
553
|
+
id: string;
|
|
554
|
+
}, 200, "json">)>;
|
|
543
555
|
find(c: Context<DyrectedContext>): Promise<(Response & hono.TypedResponse<{
|
|
544
556
|
docs: {
|
|
545
557
|
[x: string]: any;
|
|
@@ -560,7 +572,7 @@ declare class MediaController {
|
|
|
560
572
|
message: string;
|
|
561
573
|
}, 404, "json">) | (Response & hono.TypedResponse<{
|
|
562
574
|
message: string;
|
|
563
|
-
}, 400, "json">) | (Response & hono.TypedResponse<any, hono_utils_http_status.ContentfulStatusCode, "body">)>;
|
|
575
|
+
}, 400, "json">) | (Response & hono.TypedResponse<undefined, 302, "redirect">) | (Response & hono.TypedResponse<any, hono_utils_http_status.ContentfulStatusCode, "body">)>;
|
|
564
576
|
}
|
|
565
577
|
|
|
566
578
|
declare class PreviewController {
|