@momentumcms/server-express 0.5.2 → 0.5.4
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/CHANGELOG.md +12 -0
- package/index.cjs +113 -9
- package/index.js +113 -9
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
## 0.5.4 (2026-03-07)
|
|
2
|
+
|
|
3
|
+
### 🚀 Features
|
|
4
|
+
|
|
5
|
+
- Versioning & drafts with draft/publish workflow ([#50](https://github.com/DonaldMurillo/momentum-cms/pull/50))
|
|
6
|
+
|
|
7
|
+
### ❤️ Thank You
|
|
8
|
+
|
|
9
|
+
- Claude Haiku 4.5
|
|
10
|
+
- Claude Opus 4.6
|
|
11
|
+
- Donald Murillo @DonaldMurillo
|
|
12
|
+
|
|
1
13
|
## 0.5.0 (2026-02-23)
|
|
2
14
|
|
|
3
15
|
This was a version bump only for server-express to align it with other projects, there were no code changes.
|
package/index.cjs
CHANGED
|
@@ -2335,6 +2335,14 @@ function createSeedHelpers() {
|
|
|
2335
2335
|
};
|
|
2336
2336
|
}
|
|
2337
2337
|
|
|
2338
|
+
// libs/core/src/lib/versions/version.types.ts
|
|
2339
|
+
function hasVersionDrafts(collection) {
|
|
2340
|
+
const v = collection.versions;
|
|
2341
|
+
if (!v || typeof v === "boolean")
|
|
2342
|
+
return false;
|
|
2343
|
+
return !!v.drafts;
|
|
2344
|
+
}
|
|
2345
|
+
|
|
2338
2346
|
// libs/server-core/src/lib/field-access.ts
|
|
2339
2347
|
function hasFieldAccessControl(fields) {
|
|
2340
2348
|
for (const field of fields) {
|
|
@@ -2921,13 +2929,18 @@ var VersionOperationsImpl = class {
|
|
|
2921
2929
|
}
|
|
2922
2930
|
return "draft";
|
|
2923
2931
|
}
|
|
2924
|
-
async compare(versionId1, versionId2) {
|
|
2932
|
+
async compare(versionId1, versionId2, parentId) {
|
|
2925
2933
|
await this.checkAccess("readVersions");
|
|
2926
2934
|
const version1 = await this.findVersionById(versionId1);
|
|
2927
2935
|
const version2 = await this.findVersionById(versionId2);
|
|
2928
2936
|
if (!version1 || !version2) {
|
|
2929
2937
|
throw new Error("One or both versions not found");
|
|
2930
2938
|
}
|
|
2939
|
+
if (parentId) {
|
|
2940
|
+
if (version1.parent !== parentId || version2.parent !== parentId) {
|
|
2941
|
+
throw new Error("Version does not belong to the specified document");
|
|
2942
|
+
}
|
|
2943
|
+
}
|
|
2931
2944
|
const data1 = version1.version;
|
|
2932
2945
|
const data2 = version2.version;
|
|
2933
2946
|
if (!isRecord(data1) || !isRecord(data2)) {
|
|
@@ -2982,6 +2995,9 @@ var VersionOperationsImpl = class {
|
|
|
2982
2995
|
// Private Helpers
|
|
2983
2996
|
// ============================================
|
|
2984
2997
|
async checkAccess(operation) {
|
|
2998
|
+
if (this.context.overrideAccess) {
|
|
2999
|
+
return;
|
|
3000
|
+
}
|
|
2985
3001
|
const accessFn = this.collectionConfig.access?.[operation];
|
|
2986
3002
|
if (!accessFn) {
|
|
2987
3003
|
return;
|
|
@@ -3138,6 +3154,12 @@ var CollectionOperationsImpl = class _CollectionOperationsImpl {
|
|
|
3138
3154
|
Object.assign(whereParams, constraints);
|
|
3139
3155
|
}
|
|
3140
3156
|
}
|
|
3157
|
+
if (hasVersionDrafts(this.collectionConfig) && !this.context.overrideAccess) {
|
|
3158
|
+
const canSeeDrafts = await this.canReadDrafts();
|
|
3159
|
+
if (!canSeeDrafts) {
|
|
3160
|
+
whereParams["_status"] = "published";
|
|
3161
|
+
}
|
|
3162
|
+
}
|
|
3141
3163
|
const query = {
|
|
3142
3164
|
...queryOptions,
|
|
3143
3165
|
...whereParams,
|
|
@@ -3200,6 +3222,15 @@ var CollectionOperationsImpl = class _CollectionOperationsImpl {
|
|
|
3200
3222
|
doc[softDeleteField]) {
|
|
3201
3223
|
return null;
|
|
3202
3224
|
}
|
|
3225
|
+
if (hasVersionDrafts(this.collectionConfig) && !this.context.overrideAccess) {
|
|
3226
|
+
const canSeeDrafts = await this.canReadDrafts();
|
|
3227
|
+
if (!canSeeDrafts) {
|
|
3228
|
+
const record = doc;
|
|
3229
|
+
if (record["_status"] !== "published") {
|
|
3230
|
+
return null;
|
|
3231
|
+
}
|
|
3232
|
+
}
|
|
3233
|
+
}
|
|
3203
3234
|
if (!this.matchesDefaultWhereConstraints(doc)) {
|
|
3204
3235
|
return null;
|
|
3205
3236
|
}
|
|
@@ -3285,6 +3316,15 @@ var CollectionOperationsImpl = class _CollectionOperationsImpl {
|
|
|
3285
3316
|
if (!this.matchesDefaultWhereConstraints(originalDoc)) {
|
|
3286
3317
|
throw new DocumentNotFoundError(this.slug, id);
|
|
3287
3318
|
}
|
|
3319
|
+
if (hasVersionDrafts(this.collectionConfig) && this.adapter.createVersion) {
|
|
3320
|
+
const status = originalDoc["_status"] ?? "draft";
|
|
3321
|
+
await this.adapter.createVersion(this.slug, id, originalDoc, { status });
|
|
3322
|
+
const versionsConfig = this.collectionConfig.versions;
|
|
3323
|
+
const maxPerDoc = typeof versionsConfig === "object" && versionsConfig !== null ? versionsConfig.maxPerDoc : void 0;
|
|
3324
|
+
if (maxPerDoc && this.adapter.deleteVersions) {
|
|
3325
|
+
await this.adapter.deleteVersions(this.slug, id, maxPerDoc);
|
|
3326
|
+
}
|
|
3327
|
+
}
|
|
3288
3328
|
let processedData = data;
|
|
3289
3329
|
const softDeleteField = getSoftDeleteField(this.collectionConfig);
|
|
3290
3330
|
if (softDeleteField && softDeleteField in processedData) {
|
|
@@ -3616,6 +3656,30 @@ var CollectionOperationsImpl = class _CollectionOperationsImpl {
|
|
|
3616
3656
|
throw new AccessDeniedError(operation, this.slug);
|
|
3617
3657
|
}
|
|
3618
3658
|
}
|
|
3659
|
+
/**
|
|
3660
|
+
* Check if the current user can see draft documents (non-throwing).
|
|
3661
|
+
* Uses `access.readDrafts` if configured, otherwise falls back to `access.update`.
|
|
3662
|
+
*/
|
|
3663
|
+
async canReadDrafts() {
|
|
3664
|
+
if (!this.context.user)
|
|
3665
|
+
return false;
|
|
3666
|
+
const readDraftsFn = this.collectionConfig.access?.readDrafts;
|
|
3667
|
+
if (readDraftsFn) {
|
|
3668
|
+
try {
|
|
3669
|
+
return !!await Promise.resolve(readDraftsFn({ req: this.buildRequestContext() }));
|
|
3670
|
+
} catch {
|
|
3671
|
+
return false;
|
|
3672
|
+
}
|
|
3673
|
+
}
|
|
3674
|
+
const updateFn = this.collectionConfig.access?.update;
|
|
3675
|
+
if (!updateFn)
|
|
3676
|
+
return true;
|
|
3677
|
+
try {
|
|
3678
|
+
return !!await Promise.resolve(updateFn({ req: this.buildRequestContext() }));
|
|
3679
|
+
} catch {
|
|
3680
|
+
return false;
|
|
3681
|
+
}
|
|
3682
|
+
}
|
|
3619
3683
|
buildRequestContext() {
|
|
3620
3684
|
return {
|
|
3621
3685
|
user: this.context.user
|
|
@@ -4752,7 +4816,7 @@ function startPublishScheduler(adapter, collections, options) {
|
|
|
4752
4816
|
const scheduled = await adapter.findScheduledDocuments(collection.slug, now);
|
|
4753
4817
|
for (const doc of scheduled) {
|
|
4754
4818
|
try {
|
|
4755
|
-
const api = getMomentumAPI();
|
|
4819
|
+
const api = getMomentumAPI().setContext({ overrideAccess: true });
|
|
4756
4820
|
const versionOps = api.collection(collection.slug).versions();
|
|
4757
4821
|
if (!versionOps)
|
|
4758
4822
|
continue;
|
|
@@ -4859,8 +4923,9 @@ function buildGraphQLSchema(collections) {
|
|
|
4859
4923
|
}
|
|
4860
4924
|
function getOrCreateEnum(field, parentName) {
|
|
4861
4925
|
const key = `${parentName}_${field.name}`;
|
|
4862
|
-
|
|
4863
|
-
|
|
4926
|
+
const cached = enumCache.get(key);
|
|
4927
|
+
if (cached) {
|
|
4928
|
+
return cached;
|
|
4864
4929
|
}
|
|
4865
4930
|
const values = {};
|
|
4866
4931
|
for (const opt of field.options) {
|
|
@@ -4971,8 +5036,9 @@ function buildGraphQLSchema(collections) {
|
|
|
4971
5036
|
}
|
|
4972
5037
|
}
|
|
4973
5038
|
function getOrCreateObjectType(fields, typeName) {
|
|
4974
|
-
|
|
4975
|
-
|
|
5039
|
+
const cachedType = typeCache.get(typeName);
|
|
5040
|
+
if (cachedType) {
|
|
5041
|
+
return cachedType;
|
|
4976
5042
|
}
|
|
4977
5043
|
const objType = new import_graphql2.GraphQLObjectType({
|
|
4978
5044
|
name: typeName,
|
|
@@ -4993,8 +5059,9 @@ function buildGraphQLSchema(collections) {
|
|
|
4993
5059
|
}
|
|
4994
5060
|
function getOrCreateInputType(fields, typeName) {
|
|
4995
5061
|
const inputName = `${typeName}Input`;
|
|
4996
|
-
|
|
4997
|
-
|
|
5062
|
+
const cachedInput = inputCache.get(inputName);
|
|
5063
|
+
if (cachedInput) {
|
|
5064
|
+
return cachedInput;
|
|
4998
5065
|
}
|
|
4999
5066
|
const inputType = new import_graphql2.GraphQLInputObjectType({
|
|
5000
5067
|
name: inputName,
|
|
@@ -6823,6 +6890,10 @@ function momentumApiMiddleware(config) {
|
|
|
6823
6890
|
res.json(result);
|
|
6824
6891
|
} catch (error) {
|
|
6825
6892
|
const message = sanitizeErrorMessage(error, "Unknown error");
|
|
6893
|
+
if (error instanceof Error && error.name === "AccessDeniedError") {
|
|
6894
|
+
res.status(403).json({ error: "Access denied" });
|
|
6895
|
+
return;
|
|
6896
|
+
}
|
|
6826
6897
|
res.status(500).json({ error: "Failed to fetch versions", message });
|
|
6827
6898
|
}
|
|
6828
6899
|
});
|
|
@@ -6851,6 +6922,10 @@ function momentumApiMiddleware(config) {
|
|
|
6851
6922
|
res.json(version);
|
|
6852
6923
|
} catch (error) {
|
|
6853
6924
|
const message = sanitizeErrorMessage(error, "Unknown error");
|
|
6925
|
+
if (error instanceof Error && error.name === "AccessDeniedError") {
|
|
6926
|
+
res.status(403).json({ error: "Access denied" });
|
|
6927
|
+
return;
|
|
6928
|
+
}
|
|
6854
6929
|
res.status(500).json({ error: "Failed to fetch version", message });
|
|
6855
6930
|
}
|
|
6856
6931
|
});
|
|
@@ -6889,6 +6964,10 @@ function momentumApiMiddleware(config) {
|
|
|
6889
6964
|
res.status(400).json({ error: "Version parent mismatch", message });
|
|
6890
6965
|
return;
|
|
6891
6966
|
}
|
|
6967
|
+
if (error instanceof Error && error.name === "AccessDeniedError") {
|
|
6968
|
+
res.status(403).json({ error: "Access denied" });
|
|
6969
|
+
return;
|
|
6970
|
+
}
|
|
6892
6971
|
res.status(500).json({ error: "Failed to restore version", message });
|
|
6893
6972
|
}
|
|
6894
6973
|
});
|
|
@@ -6910,6 +6989,10 @@ function momentumApiMiddleware(config) {
|
|
|
6910
6989
|
res.json({ doc: published, message: "Document published successfully" });
|
|
6911
6990
|
} catch (error) {
|
|
6912
6991
|
const message = sanitizeErrorMessage(error, "Unknown error");
|
|
6992
|
+
if (error instanceof Error && error.name === "AccessDeniedError") {
|
|
6993
|
+
res.status(403).json({ error: "Access denied" });
|
|
6994
|
+
return;
|
|
6995
|
+
}
|
|
6913
6996
|
res.status(500).json({ error: "Failed to publish document", message });
|
|
6914
6997
|
}
|
|
6915
6998
|
});
|
|
@@ -6939,6 +7022,10 @@ function momentumApiMiddleware(config) {
|
|
|
6939
7022
|
res.json(result);
|
|
6940
7023
|
} catch (error) {
|
|
6941
7024
|
const message = sanitizeErrorMessage(error, "Unknown error");
|
|
7025
|
+
if (error instanceof Error && error.name === "AccessDeniedError") {
|
|
7026
|
+
res.status(403).json({ error: "Access denied" });
|
|
7027
|
+
return;
|
|
7028
|
+
}
|
|
6942
7029
|
res.status(500).json({ error: "Failed to schedule publish", message });
|
|
6943
7030
|
}
|
|
6944
7031
|
});
|
|
@@ -6960,6 +7047,10 @@ function momentumApiMiddleware(config) {
|
|
|
6960
7047
|
res.json({ message: "Scheduled publish cancelled" });
|
|
6961
7048
|
} catch (error) {
|
|
6962
7049
|
const message = sanitizeErrorMessage(error, "Unknown error");
|
|
7050
|
+
if (error instanceof Error && error.name === "AccessDeniedError") {
|
|
7051
|
+
res.status(403).json({ error: "Access denied" });
|
|
7052
|
+
return;
|
|
7053
|
+
}
|
|
6963
7054
|
res.status(500).json({ error: "Failed to cancel scheduled publish", message });
|
|
6964
7055
|
}
|
|
6965
7056
|
});
|
|
@@ -6981,6 +7072,10 @@ function momentumApiMiddleware(config) {
|
|
|
6981
7072
|
res.json({ doc: unpublished, message: "Document unpublished successfully" });
|
|
6982
7073
|
} catch (error) {
|
|
6983
7074
|
const message = sanitizeErrorMessage(error, "Unknown error");
|
|
7075
|
+
if (error instanceof Error && error.name === "AccessDeniedError") {
|
|
7076
|
+
res.status(403).json({ error: "Access denied" });
|
|
7077
|
+
return;
|
|
7078
|
+
}
|
|
6984
7079
|
res.status(500).json({ error: "Failed to unpublish document", message });
|
|
6985
7080
|
}
|
|
6986
7081
|
});
|
|
@@ -7003,6 +7098,10 @@ function momentumApiMiddleware(config) {
|
|
|
7003
7098
|
res.json({ version: draft, message: "Draft saved successfully" });
|
|
7004
7099
|
} catch (error) {
|
|
7005
7100
|
const message = sanitizeErrorMessage(error, "Unknown error");
|
|
7101
|
+
if (error instanceof Error && error.name === "AccessDeniedError") {
|
|
7102
|
+
res.status(403).json({ error: "Access denied" });
|
|
7103
|
+
return;
|
|
7104
|
+
}
|
|
7006
7105
|
res.status(500).json({ error: "Failed to save draft", message });
|
|
7007
7106
|
}
|
|
7008
7107
|
});
|
|
@@ -7028,10 +7127,15 @@ function momentumApiMiddleware(config) {
|
|
|
7028
7127
|
});
|
|
7029
7128
|
return;
|
|
7030
7129
|
}
|
|
7031
|
-
const
|
|
7130
|
+
const parentId = req.params["id"];
|
|
7131
|
+
const differences = await versionOps.compare(versionId1, versionId2, parentId);
|
|
7032
7132
|
res.json({ differences });
|
|
7033
7133
|
} catch (error) {
|
|
7034
7134
|
const message = sanitizeErrorMessage(error, "Unknown error");
|
|
7135
|
+
if (error instanceof Error && error.name === "AccessDeniedError") {
|
|
7136
|
+
res.status(403).json({ error: "Access denied" });
|
|
7137
|
+
return;
|
|
7138
|
+
}
|
|
7035
7139
|
res.status(500).json({ error: "Failed to compare versions", message });
|
|
7036
7140
|
}
|
|
7037
7141
|
});
|
package/index.js
CHANGED
|
@@ -2291,6 +2291,14 @@ function createSeedHelpers() {
|
|
|
2291
2291
|
};
|
|
2292
2292
|
}
|
|
2293
2293
|
|
|
2294
|
+
// libs/core/src/lib/versions/version.types.ts
|
|
2295
|
+
function hasVersionDrafts(collection) {
|
|
2296
|
+
const v = collection.versions;
|
|
2297
|
+
if (!v || typeof v === "boolean")
|
|
2298
|
+
return false;
|
|
2299
|
+
return !!v.drafts;
|
|
2300
|
+
}
|
|
2301
|
+
|
|
2294
2302
|
// libs/server-core/src/lib/field-access.ts
|
|
2295
2303
|
function hasFieldAccessControl(fields) {
|
|
2296
2304
|
for (const field of fields) {
|
|
@@ -2877,13 +2885,18 @@ var VersionOperationsImpl = class {
|
|
|
2877
2885
|
}
|
|
2878
2886
|
return "draft";
|
|
2879
2887
|
}
|
|
2880
|
-
async compare(versionId1, versionId2) {
|
|
2888
|
+
async compare(versionId1, versionId2, parentId) {
|
|
2881
2889
|
await this.checkAccess("readVersions");
|
|
2882
2890
|
const version1 = await this.findVersionById(versionId1);
|
|
2883
2891
|
const version2 = await this.findVersionById(versionId2);
|
|
2884
2892
|
if (!version1 || !version2) {
|
|
2885
2893
|
throw new Error("One or both versions not found");
|
|
2886
2894
|
}
|
|
2895
|
+
if (parentId) {
|
|
2896
|
+
if (version1.parent !== parentId || version2.parent !== parentId) {
|
|
2897
|
+
throw new Error("Version does not belong to the specified document");
|
|
2898
|
+
}
|
|
2899
|
+
}
|
|
2887
2900
|
const data1 = version1.version;
|
|
2888
2901
|
const data2 = version2.version;
|
|
2889
2902
|
if (!isRecord(data1) || !isRecord(data2)) {
|
|
@@ -2938,6 +2951,9 @@ var VersionOperationsImpl = class {
|
|
|
2938
2951
|
// Private Helpers
|
|
2939
2952
|
// ============================================
|
|
2940
2953
|
async checkAccess(operation) {
|
|
2954
|
+
if (this.context.overrideAccess) {
|
|
2955
|
+
return;
|
|
2956
|
+
}
|
|
2941
2957
|
const accessFn = this.collectionConfig.access?.[operation];
|
|
2942
2958
|
if (!accessFn) {
|
|
2943
2959
|
return;
|
|
@@ -3094,6 +3110,12 @@ var CollectionOperationsImpl = class _CollectionOperationsImpl {
|
|
|
3094
3110
|
Object.assign(whereParams, constraints);
|
|
3095
3111
|
}
|
|
3096
3112
|
}
|
|
3113
|
+
if (hasVersionDrafts(this.collectionConfig) && !this.context.overrideAccess) {
|
|
3114
|
+
const canSeeDrafts = await this.canReadDrafts();
|
|
3115
|
+
if (!canSeeDrafts) {
|
|
3116
|
+
whereParams["_status"] = "published";
|
|
3117
|
+
}
|
|
3118
|
+
}
|
|
3097
3119
|
const query = {
|
|
3098
3120
|
...queryOptions,
|
|
3099
3121
|
...whereParams,
|
|
@@ -3156,6 +3178,15 @@ var CollectionOperationsImpl = class _CollectionOperationsImpl {
|
|
|
3156
3178
|
doc[softDeleteField]) {
|
|
3157
3179
|
return null;
|
|
3158
3180
|
}
|
|
3181
|
+
if (hasVersionDrafts(this.collectionConfig) && !this.context.overrideAccess) {
|
|
3182
|
+
const canSeeDrafts = await this.canReadDrafts();
|
|
3183
|
+
if (!canSeeDrafts) {
|
|
3184
|
+
const record = doc;
|
|
3185
|
+
if (record["_status"] !== "published") {
|
|
3186
|
+
return null;
|
|
3187
|
+
}
|
|
3188
|
+
}
|
|
3189
|
+
}
|
|
3159
3190
|
if (!this.matchesDefaultWhereConstraints(doc)) {
|
|
3160
3191
|
return null;
|
|
3161
3192
|
}
|
|
@@ -3241,6 +3272,15 @@ var CollectionOperationsImpl = class _CollectionOperationsImpl {
|
|
|
3241
3272
|
if (!this.matchesDefaultWhereConstraints(originalDoc)) {
|
|
3242
3273
|
throw new DocumentNotFoundError(this.slug, id);
|
|
3243
3274
|
}
|
|
3275
|
+
if (hasVersionDrafts(this.collectionConfig) && this.adapter.createVersion) {
|
|
3276
|
+
const status = originalDoc["_status"] ?? "draft";
|
|
3277
|
+
await this.adapter.createVersion(this.slug, id, originalDoc, { status });
|
|
3278
|
+
const versionsConfig = this.collectionConfig.versions;
|
|
3279
|
+
const maxPerDoc = typeof versionsConfig === "object" && versionsConfig !== null ? versionsConfig.maxPerDoc : void 0;
|
|
3280
|
+
if (maxPerDoc && this.adapter.deleteVersions) {
|
|
3281
|
+
await this.adapter.deleteVersions(this.slug, id, maxPerDoc);
|
|
3282
|
+
}
|
|
3283
|
+
}
|
|
3244
3284
|
let processedData = data;
|
|
3245
3285
|
const softDeleteField = getSoftDeleteField(this.collectionConfig);
|
|
3246
3286
|
if (softDeleteField && softDeleteField in processedData) {
|
|
@@ -3572,6 +3612,30 @@ var CollectionOperationsImpl = class _CollectionOperationsImpl {
|
|
|
3572
3612
|
throw new AccessDeniedError(operation, this.slug);
|
|
3573
3613
|
}
|
|
3574
3614
|
}
|
|
3615
|
+
/**
|
|
3616
|
+
* Check if the current user can see draft documents (non-throwing).
|
|
3617
|
+
* Uses `access.readDrafts` if configured, otherwise falls back to `access.update`.
|
|
3618
|
+
*/
|
|
3619
|
+
async canReadDrafts() {
|
|
3620
|
+
if (!this.context.user)
|
|
3621
|
+
return false;
|
|
3622
|
+
const readDraftsFn = this.collectionConfig.access?.readDrafts;
|
|
3623
|
+
if (readDraftsFn) {
|
|
3624
|
+
try {
|
|
3625
|
+
return !!await Promise.resolve(readDraftsFn({ req: this.buildRequestContext() }));
|
|
3626
|
+
} catch {
|
|
3627
|
+
return false;
|
|
3628
|
+
}
|
|
3629
|
+
}
|
|
3630
|
+
const updateFn = this.collectionConfig.access?.update;
|
|
3631
|
+
if (!updateFn)
|
|
3632
|
+
return true;
|
|
3633
|
+
try {
|
|
3634
|
+
return !!await Promise.resolve(updateFn({ req: this.buildRequestContext() }));
|
|
3635
|
+
} catch {
|
|
3636
|
+
return false;
|
|
3637
|
+
}
|
|
3638
|
+
}
|
|
3575
3639
|
buildRequestContext() {
|
|
3576
3640
|
return {
|
|
3577
3641
|
user: this.context.user
|
|
@@ -4708,7 +4772,7 @@ function startPublishScheduler(adapter, collections, options) {
|
|
|
4708
4772
|
const scheduled = await adapter.findScheduledDocuments(collection.slug, now);
|
|
4709
4773
|
for (const doc of scheduled) {
|
|
4710
4774
|
try {
|
|
4711
|
-
const api = getMomentumAPI();
|
|
4775
|
+
const api = getMomentumAPI().setContext({ overrideAccess: true });
|
|
4712
4776
|
const versionOps = api.collection(collection.slug).versions();
|
|
4713
4777
|
if (!versionOps)
|
|
4714
4778
|
continue;
|
|
@@ -4828,8 +4892,9 @@ function buildGraphQLSchema(collections) {
|
|
|
4828
4892
|
}
|
|
4829
4893
|
function getOrCreateEnum(field, parentName) {
|
|
4830
4894
|
const key = `${parentName}_${field.name}`;
|
|
4831
|
-
|
|
4832
|
-
|
|
4895
|
+
const cached = enumCache.get(key);
|
|
4896
|
+
if (cached) {
|
|
4897
|
+
return cached;
|
|
4833
4898
|
}
|
|
4834
4899
|
const values = {};
|
|
4835
4900
|
for (const opt of field.options) {
|
|
@@ -4940,8 +5005,9 @@ function buildGraphQLSchema(collections) {
|
|
|
4940
5005
|
}
|
|
4941
5006
|
}
|
|
4942
5007
|
function getOrCreateObjectType(fields, typeName) {
|
|
4943
|
-
|
|
4944
|
-
|
|
5008
|
+
const cachedType = typeCache.get(typeName);
|
|
5009
|
+
if (cachedType) {
|
|
5010
|
+
return cachedType;
|
|
4945
5011
|
}
|
|
4946
5012
|
const objType = new GraphQLObjectType({
|
|
4947
5013
|
name: typeName,
|
|
@@ -4962,8 +5028,9 @@ function buildGraphQLSchema(collections) {
|
|
|
4962
5028
|
}
|
|
4963
5029
|
function getOrCreateInputType(fields, typeName) {
|
|
4964
5030
|
const inputName = `${typeName}Input`;
|
|
4965
|
-
|
|
4966
|
-
|
|
5031
|
+
const cachedInput = inputCache.get(inputName);
|
|
5032
|
+
if (cachedInput) {
|
|
5033
|
+
return cachedInput;
|
|
4967
5034
|
}
|
|
4968
5035
|
const inputType = new GraphQLInputObjectType({
|
|
4969
5036
|
name: inputName,
|
|
@@ -6792,6 +6859,10 @@ function momentumApiMiddleware(config) {
|
|
|
6792
6859
|
res.json(result);
|
|
6793
6860
|
} catch (error) {
|
|
6794
6861
|
const message = sanitizeErrorMessage(error, "Unknown error");
|
|
6862
|
+
if (error instanceof Error && error.name === "AccessDeniedError") {
|
|
6863
|
+
res.status(403).json({ error: "Access denied" });
|
|
6864
|
+
return;
|
|
6865
|
+
}
|
|
6795
6866
|
res.status(500).json({ error: "Failed to fetch versions", message });
|
|
6796
6867
|
}
|
|
6797
6868
|
});
|
|
@@ -6820,6 +6891,10 @@ function momentumApiMiddleware(config) {
|
|
|
6820
6891
|
res.json(version);
|
|
6821
6892
|
} catch (error) {
|
|
6822
6893
|
const message = sanitizeErrorMessage(error, "Unknown error");
|
|
6894
|
+
if (error instanceof Error && error.name === "AccessDeniedError") {
|
|
6895
|
+
res.status(403).json({ error: "Access denied" });
|
|
6896
|
+
return;
|
|
6897
|
+
}
|
|
6823
6898
|
res.status(500).json({ error: "Failed to fetch version", message });
|
|
6824
6899
|
}
|
|
6825
6900
|
});
|
|
@@ -6858,6 +6933,10 @@ function momentumApiMiddleware(config) {
|
|
|
6858
6933
|
res.status(400).json({ error: "Version parent mismatch", message });
|
|
6859
6934
|
return;
|
|
6860
6935
|
}
|
|
6936
|
+
if (error instanceof Error && error.name === "AccessDeniedError") {
|
|
6937
|
+
res.status(403).json({ error: "Access denied" });
|
|
6938
|
+
return;
|
|
6939
|
+
}
|
|
6861
6940
|
res.status(500).json({ error: "Failed to restore version", message });
|
|
6862
6941
|
}
|
|
6863
6942
|
});
|
|
@@ -6879,6 +6958,10 @@ function momentumApiMiddleware(config) {
|
|
|
6879
6958
|
res.json({ doc: published, message: "Document published successfully" });
|
|
6880
6959
|
} catch (error) {
|
|
6881
6960
|
const message = sanitizeErrorMessage(error, "Unknown error");
|
|
6961
|
+
if (error instanceof Error && error.name === "AccessDeniedError") {
|
|
6962
|
+
res.status(403).json({ error: "Access denied" });
|
|
6963
|
+
return;
|
|
6964
|
+
}
|
|
6882
6965
|
res.status(500).json({ error: "Failed to publish document", message });
|
|
6883
6966
|
}
|
|
6884
6967
|
});
|
|
@@ -6908,6 +6991,10 @@ function momentumApiMiddleware(config) {
|
|
|
6908
6991
|
res.json(result);
|
|
6909
6992
|
} catch (error) {
|
|
6910
6993
|
const message = sanitizeErrorMessage(error, "Unknown error");
|
|
6994
|
+
if (error instanceof Error && error.name === "AccessDeniedError") {
|
|
6995
|
+
res.status(403).json({ error: "Access denied" });
|
|
6996
|
+
return;
|
|
6997
|
+
}
|
|
6911
6998
|
res.status(500).json({ error: "Failed to schedule publish", message });
|
|
6912
6999
|
}
|
|
6913
7000
|
});
|
|
@@ -6929,6 +7016,10 @@ function momentumApiMiddleware(config) {
|
|
|
6929
7016
|
res.json({ message: "Scheduled publish cancelled" });
|
|
6930
7017
|
} catch (error) {
|
|
6931
7018
|
const message = sanitizeErrorMessage(error, "Unknown error");
|
|
7019
|
+
if (error instanceof Error && error.name === "AccessDeniedError") {
|
|
7020
|
+
res.status(403).json({ error: "Access denied" });
|
|
7021
|
+
return;
|
|
7022
|
+
}
|
|
6932
7023
|
res.status(500).json({ error: "Failed to cancel scheduled publish", message });
|
|
6933
7024
|
}
|
|
6934
7025
|
});
|
|
@@ -6950,6 +7041,10 @@ function momentumApiMiddleware(config) {
|
|
|
6950
7041
|
res.json({ doc: unpublished, message: "Document unpublished successfully" });
|
|
6951
7042
|
} catch (error) {
|
|
6952
7043
|
const message = sanitizeErrorMessage(error, "Unknown error");
|
|
7044
|
+
if (error instanceof Error && error.name === "AccessDeniedError") {
|
|
7045
|
+
res.status(403).json({ error: "Access denied" });
|
|
7046
|
+
return;
|
|
7047
|
+
}
|
|
6953
7048
|
res.status(500).json({ error: "Failed to unpublish document", message });
|
|
6954
7049
|
}
|
|
6955
7050
|
});
|
|
@@ -6972,6 +7067,10 @@ function momentumApiMiddleware(config) {
|
|
|
6972
7067
|
res.json({ version: draft, message: "Draft saved successfully" });
|
|
6973
7068
|
} catch (error) {
|
|
6974
7069
|
const message = sanitizeErrorMessage(error, "Unknown error");
|
|
7070
|
+
if (error instanceof Error && error.name === "AccessDeniedError") {
|
|
7071
|
+
res.status(403).json({ error: "Access denied" });
|
|
7072
|
+
return;
|
|
7073
|
+
}
|
|
6975
7074
|
res.status(500).json({ error: "Failed to save draft", message });
|
|
6976
7075
|
}
|
|
6977
7076
|
});
|
|
@@ -6997,10 +7096,15 @@ function momentumApiMiddleware(config) {
|
|
|
6997
7096
|
});
|
|
6998
7097
|
return;
|
|
6999
7098
|
}
|
|
7000
|
-
const
|
|
7099
|
+
const parentId = req.params["id"];
|
|
7100
|
+
const differences = await versionOps.compare(versionId1, versionId2, parentId);
|
|
7001
7101
|
res.json({ differences });
|
|
7002
7102
|
} catch (error) {
|
|
7003
7103
|
const message = sanitizeErrorMessage(error, "Unknown error");
|
|
7104
|
+
if (error instanceof Error && error.name === "AccessDeniedError") {
|
|
7105
|
+
res.status(403).json({ error: "Access denied" });
|
|
7106
|
+
return;
|
|
7107
|
+
}
|
|
7004
7108
|
res.status(500).json({ error: "Failed to compare versions", message });
|
|
7005
7109
|
}
|
|
7006
7110
|
});
|