@cmssy/next 2.7.0 → 3.0.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/index.cjs +61 -19
- package/dist/index.d.cts +7 -2
- package/dist/index.d.ts +7 -2
- package/dist/index.js +61 -21
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -6,8 +6,8 @@ var react = require('@cmssy/react');
|
|
|
6
6
|
var client = require('@cmssy/react/client');
|
|
7
7
|
var jose = require('jose');
|
|
8
8
|
var jsxRuntime = require('react/jsx-runtime');
|
|
9
|
-
var crypto$1 = require('crypto');
|
|
10
9
|
var server = require('next/server');
|
|
10
|
+
var crypto$1 = require('crypto');
|
|
11
11
|
|
|
12
12
|
// src/create-cmssy-page.tsx
|
|
13
13
|
var CMSSY_SESSION_COOKIE = "cmssy_session";
|
|
@@ -204,10 +204,56 @@ function applyCmssyCsp(response, options) {
|
|
|
204
204
|
response.headers.delete("X-Frame-Options");
|
|
205
205
|
return response;
|
|
206
206
|
}
|
|
207
|
-
|
|
207
|
+
|
|
208
|
+
// src/secret-match.ts
|
|
209
|
+
var MAX_SECRET_LENGTH = 256;
|
|
210
|
+
async function cmssySecretsMatch(a, b) {
|
|
211
|
+
if (a.length > MAX_SECRET_LENGTH || b.length > MAX_SECRET_LENGTH) {
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
const encoder = new TextEncoder();
|
|
215
|
+
const [ha, hb] = await Promise.all([
|
|
216
|
+
crypto.subtle.digest("SHA-256", encoder.encode(a)),
|
|
217
|
+
crypto.subtle.digest("SHA-256", encoder.encode(b))
|
|
218
|
+
]);
|
|
219
|
+
const va = new Uint8Array(ha);
|
|
220
|
+
const vb = new Uint8Array(hb);
|
|
221
|
+
let diff = 0;
|
|
222
|
+
for (let i = 0; i < va.length; i += 1) {
|
|
223
|
+
diff |= (va[i] ?? 0) ^ (vb[i] ?? 0);
|
|
224
|
+
}
|
|
225
|
+
return diff === 0;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// src/edit-mode.ts
|
|
229
|
+
var CMSSY_EDIT_HEADER = "x-cmssy-edit";
|
|
230
|
+
var CMSSY_EDIT_QUERY_PARAM = "cmssyEdit";
|
|
231
|
+
var CMSSY_SECRET_QUERY_PARAM = "cmssySecret";
|
|
232
|
+
async function isCmssyEditRequest(request2, config) {
|
|
233
|
+
if (request2.cookies.has("__prerender_bypass")) return true;
|
|
234
|
+
if (!request2.nextUrl.searchParams.getAll(CMSSY_EDIT_QUERY_PARAM).includes("1")) {
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
237
|
+
const provided = request2.nextUrl.searchParams.get(CMSSY_SECRET_QUERY_PARAM);
|
|
238
|
+
if (!provided || !config.draftSecret) return false;
|
|
239
|
+
return cmssySecretsMatch(provided, config.draftSecret);
|
|
240
|
+
}
|
|
241
|
+
async function isCmssyEditMode() {
|
|
242
|
+
const h = await headers.headers();
|
|
243
|
+
return h.get(CMSSY_EDIT_HEADER) === "1";
|
|
244
|
+
}
|
|
208
245
|
function hasEditFlag(value) {
|
|
209
246
|
return Array.isArray(value) ? value.includes("1") : value === "1";
|
|
210
247
|
}
|
|
248
|
+
function firstValue(value) {
|
|
249
|
+
return Array.isArray(value) ? value[0] : value;
|
|
250
|
+
}
|
|
251
|
+
async function resolveEditorRequest(query, draftSecret) {
|
|
252
|
+
if (!hasEditFlag(query[CMSSY_EDIT_QUERY_PARAM])) return false;
|
|
253
|
+
const provided = firstValue(query[CMSSY_SECRET_QUERY_PARAM]);
|
|
254
|
+
if (!provided || !draftSecret) return false;
|
|
255
|
+
return cmssySecretsMatch(provided, draftSecret);
|
|
256
|
+
}
|
|
211
257
|
function createCmssyPage(config, blocks, options) {
|
|
212
258
|
if (!Array.isArray(blocks)) {
|
|
213
259
|
throw new Error(
|
|
@@ -229,9 +275,9 @@ function createCmssyPage(config, blocks, options) {
|
|
|
229
275
|
const path = fixedPath ?? (params ? (await params).path ?? void 0 : void 0);
|
|
230
276
|
const { isEnabled } = await headers.draftMode();
|
|
231
277
|
const query = searchParams ? await searchParams : {};
|
|
232
|
-
const
|
|
278
|
+
const editorActive = await resolveEditorRequest(query, config.draftSecret);
|
|
279
|
+
const editMode = isEnabled || editorActive;
|
|
233
280
|
const devAllowed = isDevelopment() && Boolean(config.devToken?.trim());
|
|
234
|
-
const editorActive = editMode;
|
|
235
281
|
let locale;
|
|
236
282
|
let pagePath = path;
|
|
237
283
|
let defaultLocale;
|
|
@@ -595,11 +641,6 @@ async function buildCmssyMetadata(config, path, options = {}) {
|
|
|
595
641
|
};
|
|
596
642
|
}
|
|
597
643
|
var MIN_SECRET_LENGTH = 16;
|
|
598
|
-
function secretsMatch(a, b) {
|
|
599
|
-
const ha = crypto$1.createHash("sha256").update(a).digest();
|
|
600
|
-
const hb = crypto$1.createHash("sha256").update(b).digest();
|
|
601
|
-
return crypto$1.timingSafeEqual(ha, hb);
|
|
602
|
-
}
|
|
603
644
|
function safeRedirect(redirect2, fallback) {
|
|
604
645
|
if (!redirect2 || !redirect2.startsWith("/")) return fallback;
|
|
605
646
|
if (redirect2.startsWith("//") || redirect2.includes("\\")) return fallback;
|
|
@@ -620,15 +661,22 @@ function createDraftRoute(config) {
|
|
|
620
661
|
);
|
|
621
662
|
}
|
|
622
663
|
return async function GET(request2) {
|
|
664
|
+
const url = new URL(request2.url);
|
|
665
|
+
if (url.searchParams.get("disable") === "1") {
|
|
666
|
+
const draft2 = await headers.draftMode();
|
|
667
|
+
draft2.disable();
|
|
668
|
+
navigation.redirect(
|
|
669
|
+
safeRedirect(url.searchParams.get("redirect"), fallbackRedirect)
|
|
670
|
+
);
|
|
671
|
+
}
|
|
623
672
|
if (config.draftSecret.length < MIN_SECRET_LENGTH) {
|
|
624
673
|
return new Response(
|
|
625
674
|
`cmssy: draftSecret must be at least ${MIN_SECRET_LENGTH} characters`,
|
|
626
675
|
{ status: 500 }
|
|
627
676
|
);
|
|
628
677
|
}
|
|
629
|
-
const url = new URL(request2.url);
|
|
630
678
|
const secret = url.searchParams.get("secret");
|
|
631
|
-
if (!secret || !
|
|
679
|
+
if (!secret || !await cmssySecretsMatch(secret, config.draftSecret)) {
|
|
632
680
|
return new Response("Invalid draft secret", { status: 401 });
|
|
633
681
|
}
|
|
634
682
|
const location = safeRedirect(
|
|
@@ -640,14 +688,6 @@ function createDraftRoute(config) {
|
|
|
640
688
|
navigation.redirect(location);
|
|
641
689
|
};
|
|
642
690
|
}
|
|
643
|
-
var CMSSY_EDIT_HEADER = "x-cmssy-edit";
|
|
644
|
-
function isCmssyEditRequest(request2) {
|
|
645
|
-
return request2.cookies.has("__prerender_bypass") || request2.nextUrl.searchParams.getAll("cmssyEdit").includes("1");
|
|
646
|
-
}
|
|
647
|
-
async function isCmssyEditMode() {
|
|
648
|
-
const h = await headers.headers();
|
|
649
|
-
return h.get(CMSSY_EDIT_HEADER) === "1";
|
|
650
|
-
}
|
|
651
691
|
var CMSSY_LOCALE_HEADER = "x-cmssy-locale";
|
|
652
692
|
async function localeForPathname(config, pathname) {
|
|
653
693
|
const siteLocales = await react.resolveSiteLocales(config);
|
|
@@ -1918,7 +1958,9 @@ Object.defineProperty(exports, "resolveApiUrl", {
|
|
|
1918
1958
|
});
|
|
1919
1959
|
exports.CMSSY_CART_COOKIE = CMSSY_CART_COOKIE;
|
|
1920
1960
|
exports.CMSSY_EDIT_HEADER = CMSSY_EDIT_HEADER;
|
|
1961
|
+
exports.CMSSY_EDIT_QUERY_PARAM = CMSSY_EDIT_QUERY_PARAM;
|
|
1921
1962
|
exports.CMSSY_LOCALE_HEADER = CMSSY_LOCALE_HEADER;
|
|
1963
|
+
exports.CMSSY_SECRET_QUERY_PARAM = CMSSY_SECRET_QUERY_PARAM;
|
|
1922
1964
|
exports.CMSSY_SESSION_COOKIE = CMSSY_SESSION_COOKIE;
|
|
1923
1965
|
exports.CmssyWebhookError = CmssyWebhookError;
|
|
1924
1966
|
exports.DEFAULT_CMSSY_EDITOR_ORIGINS = DEFAULT_CMSSY_EDITOR_ORIGINS;
|
package/dist/index.d.cts
CHANGED
|
@@ -196,6 +196,8 @@ declare function cmssyCspHeaders(options: CmssyCspOptions): Record<string, strin
|
|
|
196
196
|
declare function applyCmssyCsp<T extends MutableHeaders>(response: T, options: CmssyCspOptions): T;
|
|
197
197
|
|
|
198
198
|
declare const CMSSY_EDIT_HEADER = "x-cmssy-edit";
|
|
199
|
+
declare const CMSSY_EDIT_QUERY_PARAM = "cmssyEdit";
|
|
200
|
+
declare const CMSSY_SECRET_QUERY_PARAM = "cmssySecret";
|
|
199
201
|
interface EditRequestLike {
|
|
200
202
|
cookies: {
|
|
201
203
|
has: (name: string) => boolean;
|
|
@@ -203,10 +205,13 @@ interface EditRequestLike {
|
|
|
203
205
|
nextUrl: {
|
|
204
206
|
searchParams: {
|
|
205
207
|
getAll: (name: string) => string[];
|
|
208
|
+
get: (name: string) => string | null;
|
|
206
209
|
};
|
|
207
210
|
};
|
|
208
211
|
}
|
|
209
|
-
declare function isCmssyEditRequest(request: EditRequestLike
|
|
212
|
+
declare function isCmssyEditRequest(request: EditRequestLike, config: {
|
|
213
|
+
draftSecret: string;
|
|
214
|
+
}): Promise<boolean>;
|
|
210
215
|
declare function isCmssyEditMode(): Promise<boolean>;
|
|
211
216
|
|
|
212
217
|
declare const CMSSY_LOCALE_HEADER = "x-cmssy-locale";
|
|
@@ -299,4 +304,4 @@ declare class CmssyWebhookError extends Error {
|
|
|
299
304
|
*/
|
|
300
305
|
declare function verifyCmssyWebhook(options: VerifyCmssyWebhookOptions): CmssyWebhookEvent;
|
|
301
306
|
|
|
302
|
-
export { type BuildCmssyMetadataOptions, CMSSY_CART_COOKIE, CMSSY_EDIT_HEADER, CMSSY_LOCALE_HEADER, CMSSY_SESSION_COOKIE, type CmssyAuthMiddleware, type CmssyAuthRouteHandlers, type CmssyCartRouteHandlers, type CmssyCspOptions, type CmssyDraftRouteConfig, type CmssyEditorProps, type CmssyEnvConfig, type CmssyNextConfig, type CmssyOrdersRouteHandlers, CmssyWebhookError, type CreateCmssyNotFoundOptions, type CreateCmssyPageOptions, type CreateCmssyRobotsOptions, type CreateCmssySitemapOptions, DEFAULT_CMSSY_EDITOR_ORIGINS, SESSION_MAX_AGE_SECONDS, applyCmssyCsp, assertAuthConfig, buildCmssyMetadata, cmssyCspHeaders, createCmssyAuthMiddleware, createCmssyAuthRoute, createCmssyCartRoute, createCmssyLocaleMiddleware, createCmssyNotFound, createCmssyOrdersRoute, createCmssyPage, createCmssyRobots, createCmssySitemap, createDraftRoute, defineCmssyConfig, fetchOrderByToken, fetchProduct, fetchProducts, getCmssyAccessToken, getCmssyLocale, getCmssyUser, isAccessExpired, isCmssyEditMode, isCmssyEditRequest, localeForPathname, openSession, resolveEditorOrigin, resolveLocaleFromPathname, sealSession, sessionCookieOptions, splitCmssyLocale, verifyCmssyWebhook };
|
|
307
|
+
export { type BuildCmssyMetadataOptions, CMSSY_CART_COOKIE, CMSSY_EDIT_HEADER, CMSSY_EDIT_QUERY_PARAM, CMSSY_LOCALE_HEADER, CMSSY_SECRET_QUERY_PARAM, CMSSY_SESSION_COOKIE, type CmssyAuthMiddleware, type CmssyAuthRouteHandlers, type CmssyCartRouteHandlers, type CmssyCspOptions, type CmssyDraftRouteConfig, type CmssyEditorProps, type CmssyEnvConfig, type CmssyNextConfig, type CmssyOrdersRouteHandlers, CmssyWebhookError, type CreateCmssyNotFoundOptions, type CreateCmssyPageOptions, type CreateCmssyRobotsOptions, type CreateCmssySitemapOptions, DEFAULT_CMSSY_EDITOR_ORIGINS, SESSION_MAX_AGE_SECONDS, applyCmssyCsp, assertAuthConfig, buildCmssyMetadata, cmssyCspHeaders, createCmssyAuthMiddleware, createCmssyAuthRoute, createCmssyCartRoute, createCmssyLocaleMiddleware, createCmssyNotFound, createCmssyOrdersRoute, createCmssyPage, createCmssyRobots, createCmssySitemap, createDraftRoute, defineCmssyConfig, fetchOrderByToken, fetchProduct, fetchProducts, getCmssyAccessToken, getCmssyLocale, getCmssyUser, isAccessExpired, isCmssyEditMode, isCmssyEditRequest, localeForPathname, openSession, resolveEditorOrigin, resolveLocaleFromPathname, sealSession, sessionCookieOptions, splitCmssyLocale, verifyCmssyWebhook };
|
package/dist/index.d.ts
CHANGED
|
@@ -196,6 +196,8 @@ declare function cmssyCspHeaders(options: CmssyCspOptions): Record<string, strin
|
|
|
196
196
|
declare function applyCmssyCsp<T extends MutableHeaders>(response: T, options: CmssyCspOptions): T;
|
|
197
197
|
|
|
198
198
|
declare const CMSSY_EDIT_HEADER = "x-cmssy-edit";
|
|
199
|
+
declare const CMSSY_EDIT_QUERY_PARAM = "cmssyEdit";
|
|
200
|
+
declare const CMSSY_SECRET_QUERY_PARAM = "cmssySecret";
|
|
199
201
|
interface EditRequestLike {
|
|
200
202
|
cookies: {
|
|
201
203
|
has: (name: string) => boolean;
|
|
@@ -203,10 +205,13 @@ interface EditRequestLike {
|
|
|
203
205
|
nextUrl: {
|
|
204
206
|
searchParams: {
|
|
205
207
|
getAll: (name: string) => string[];
|
|
208
|
+
get: (name: string) => string | null;
|
|
206
209
|
};
|
|
207
210
|
};
|
|
208
211
|
}
|
|
209
|
-
declare function isCmssyEditRequest(request: EditRequestLike
|
|
212
|
+
declare function isCmssyEditRequest(request: EditRequestLike, config: {
|
|
213
|
+
draftSecret: string;
|
|
214
|
+
}): Promise<boolean>;
|
|
210
215
|
declare function isCmssyEditMode(): Promise<boolean>;
|
|
211
216
|
|
|
212
217
|
declare const CMSSY_LOCALE_HEADER = "x-cmssy-locale";
|
|
@@ -299,4 +304,4 @@ declare class CmssyWebhookError extends Error {
|
|
|
299
304
|
*/
|
|
300
305
|
declare function verifyCmssyWebhook(options: VerifyCmssyWebhookOptions): CmssyWebhookEvent;
|
|
301
306
|
|
|
302
|
-
export { type BuildCmssyMetadataOptions, CMSSY_CART_COOKIE, CMSSY_EDIT_HEADER, CMSSY_LOCALE_HEADER, CMSSY_SESSION_COOKIE, type CmssyAuthMiddleware, type CmssyAuthRouteHandlers, type CmssyCartRouteHandlers, type CmssyCspOptions, type CmssyDraftRouteConfig, type CmssyEditorProps, type CmssyEnvConfig, type CmssyNextConfig, type CmssyOrdersRouteHandlers, CmssyWebhookError, type CreateCmssyNotFoundOptions, type CreateCmssyPageOptions, type CreateCmssyRobotsOptions, type CreateCmssySitemapOptions, DEFAULT_CMSSY_EDITOR_ORIGINS, SESSION_MAX_AGE_SECONDS, applyCmssyCsp, assertAuthConfig, buildCmssyMetadata, cmssyCspHeaders, createCmssyAuthMiddleware, createCmssyAuthRoute, createCmssyCartRoute, createCmssyLocaleMiddleware, createCmssyNotFound, createCmssyOrdersRoute, createCmssyPage, createCmssyRobots, createCmssySitemap, createDraftRoute, defineCmssyConfig, fetchOrderByToken, fetchProduct, fetchProducts, getCmssyAccessToken, getCmssyLocale, getCmssyUser, isAccessExpired, isCmssyEditMode, isCmssyEditRequest, localeForPathname, openSession, resolveEditorOrigin, resolveLocaleFromPathname, sealSession, sessionCookieOptions, splitCmssyLocale, verifyCmssyWebhook };
|
|
307
|
+
export { type BuildCmssyMetadataOptions, CMSSY_CART_COOKIE, CMSSY_EDIT_HEADER, CMSSY_EDIT_QUERY_PARAM, CMSSY_LOCALE_HEADER, CMSSY_SECRET_QUERY_PARAM, CMSSY_SESSION_COOKIE, type CmssyAuthMiddleware, type CmssyAuthRouteHandlers, type CmssyCartRouteHandlers, type CmssyCspOptions, type CmssyDraftRouteConfig, type CmssyEditorProps, type CmssyEnvConfig, type CmssyNextConfig, type CmssyOrdersRouteHandlers, CmssyWebhookError, type CreateCmssyNotFoundOptions, type CreateCmssyPageOptions, type CreateCmssyRobotsOptions, type CreateCmssySitemapOptions, DEFAULT_CMSSY_EDITOR_ORIGINS, SESSION_MAX_AGE_SECONDS, applyCmssyCsp, assertAuthConfig, buildCmssyMetadata, cmssyCspHeaders, createCmssyAuthMiddleware, createCmssyAuthRoute, createCmssyCartRoute, createCmssyLocaleMiddleware, createCmssyNotFound, createCmssyOrdersRoute, createCmssyPage, createCmssyRobots, createCmssySitemap, createDraftRoute, defineCmssyConfig, fetchOrderByToken, fetchProduct, fetchProducts, getCmssyAccessToken, getCmssyLocale, getCmssyUser, isAccessExpired, isCmssyEditMode, isCmssyEditRequest, localeForPathname, openSession, resolveEditorOrigin, resolveLocaleFromPathname, sealSession, sessionCookieOptions, splitCmssyLocale, verifyCmssyWebhook };
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { headers, draftMode, cookies } from 'next/headers';
|
|
2
2
|
import { notFound, redirect } from 'next/navigation';
|
|
3
3
|
import { createCmssyClient, resolveSiteLocales, splitLocaleFromPath, fetchPage, resolveForms, CmssyServerPage, fetchSiteConfig, fetchPageById, fetchPages, fetchPageMeta, normalizeSlug as normalizeSlug$1, resolveWorkspaceId, graphqlRequest, resolveApiUrl } from '@cmssy/react';
|
|
4
4
|
export { DEFAULT_CMSSY_API_URL, evaluateFieldConditionGroup, resolveApiUrl } from '@cmssy/react';
|
|
5
5
|
import { CmssyLocaleProvider } from '@cmssy/react/client';
|
|
6
6
|
import { EncryptJWT, jwtDecrypt } from 'jose';
|
|
7
7
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
8
|
-
import { createHmac, createHash, timingSafeEqual } from 'crypto';
|
|
9
8
|
import { NextResponse } from 'next/server';
|
|
9
|
+
import { createHmac, timingSafeEqual } from 'crypto';
|
|
10
10
|
|
|
11
11
|
// src/create-cmssy-page.tsx
|
|
12
12
|
var CMSSY_SESSION_COOKIE = "cmssy_session";
|
|
@@ -203,10 +203,56 @@ function applyCmssyCsp(response, options) {
|
|
|
203
203
|
response.headers.delete("X-Frame-Options");
|
|
204
204
|
return response;
|
|
205
205
|
}
|
|
206
|
-
|
|
206
|
+
|
|
207
|
+
// src/secret-match.ts
|
|
208
|
+
var MAX_SECRET_LENGTH = 256;
|
|
209
|
+
async function cmssySecretsMatch(a, b) {
|
|
210
|
+
if (a.length > MAX_SECRET_LENGTH || b.length > MAX_SECRET_LENGTH) {
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
const encoder = new TextEncoder();
|
|
214
|
+
const [ha, hb] = await Promise.all([
|
|
215
|
+
crypto.subtle.digest("SHA-256", encoder.encode(a)),
|
|
216
|
+
crypto.subtle.digest("SHA-256", encoder.encode(b))
|
|
217
|
+
]);
|
|
218
|
+
const va = new Uint8Array(ha);
|
|
219
|
+
const vb = new Uint8Array(hb);
|
|
220
|
+
let diff = 0;
|
|
221
|
+
for (let i = 0; i < va.length; i += 1) {
|
|
222
|
+
diff |= (va[i] ?? 0) ^ (vb[i] ?? 0);
|
|
223
|
+
}
|
|
224
|
+
return diff === 0;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// src/edit-mode.ts
|
|
228
|
+
var CMSSY_EDIT_HEADER = "x-cmssy-edit";
|
|
229
|
+
var CMSSY_EDIT_QUERY_PARAM = "cmssyEdit";
|
|
230
|
+
var CMSSY_SECRET_QUERY_PARAM = "cmssySecret";
|
|
231
|
+
async function isCmssyEditRequest(request2, config) {
|
|
232
|
+
if (request2.cookies.has("__prerender_bypass")) return true;
|
|
233
|
+
if (!request2.nextUrl.searchParams.getAll(CMSSY_EDIT_QUERY_PARAM).includes("1")) {
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
const provided = request2.nextUrl.searchParams.get(CMSSY_SECRET_QUERY_PARAM);
|
|
237
|
+
if (!provided || !config.draftSecret) return false;
|
|
238
|
+
return cmssySecretsMatch(provided, config.draftSecret);
|
|
239
|
+
}
|
|
240
|
+
async function isCmssyEditMode() {
|
|
241
|
+
const h = await headers();
|
|
242
|
+
return h.get(CMSSY_EDIT_HEADER) === "1";
|
|
243
|
+
}
|
|
207
244
|
function hasEditFlag(value) {
|
|
208
245
|
return Array.isArray(value) ? value.includes("1") : value === "1";
|
|
209
246
|
}
|
|
247
|
+
function firstValue(value) {
|
|
248
|
+
return Array.isArray(value) ? value[0] : value;
|
|
249
|
+
}
|
|
250
|
+
async function resolveEditorRequest(query, draftSecret) {
|
|
251
|
+
if (!hasEditFlag(query[CMSSY_EDIT_QUERY_PARAM])) return false;
|
|
252
|
+
const provided = firstValue(query[CMSSY_SECRET_QUERY_PARAM]);
|
|
253
|
+
if (!provided || !draftSecret) return false;
|
|
254
|
+
return cmssySecretsMatch(provided, draftSecret);
|
|
255
|
+
}
|
|
210
256
|
function createCmssyPage(config, blocks, options) {
|
|
211
257
|
if (!Array.isArray(blocks)) {
|
|
212
258
|
throw new Error(
|
|
@@ -228,9 +274,9 @@ function createCmssyPage(config, blocks, options) {
|
|
|
228
274
|
const path = fixedPath ?? (params ? (await params).path ?? void 0 : void 0);
|
|
229
275
|
const { isEnabled } = await draftMode();
|
|
230
276
|
const query = searchParams ? await searchParams : {};
|
|
231
|
-
const
|
|
277
|
+
const editorActive = await resolveEditorRequest(query, config.draftSecret);
|
|
278
|
+
const editMode = isEnabled || editorActive;
|
|
232
279
|
const devAllowed = isDevelopment() && Boolean(config.devToken?.trim());
|
|
233
|
-
const editorActive = editMode;
|
|
234
280
|
let locale;
|
|
235
281
|
let pagePath = path;
|
|
236
282
|
let defaultLocale;
|
|
@@ -594,11 +640,6 @@ async function buildCmssyMetadata(config, path, options = {}) {
|
|
|
594
640
|
};
|
|
595
641
|
}
|
|
596
642
|
var MIN_SECRET_LENGTH = 16;
|
|
597
|
-
function secretsMatch(a, b) {
|
|
598
|
-
const ha = createHash("sha256").update(a).digest();
|
|
599
|
-
const hb = createHash("sha256").update(b).digest();
|
|
600
|
-
return timingSafeEqual(ha, hb);
|
|
601
|
-
}
|
|
602
643
|
function safeRedirect(redirect2, fallback) {
|
|
603
644
|
if (!redirect2 || !redirect2.startsWith("/")) return fallback;
|
|
604
645
|
if (redirect2.startsWith("//") || redirect2.includes("\\")) return fallback;
|
|
@@ -619,15 +660,22 @@ function createDraftRoute(config) {
|
|
|
619
660
|
);
|
|
620
661
|
}
|
|
621
662
|
return async function GET(request2) {
|
|
663
|
+
const url = new URL(request2.url);
|
|
664
|
+
if (url.searchParams.get("disable") === "1") {
|
|
665
|
+
const draft2 = await draftMode();
|
|
666
|
+
draft2.disable();
|
|
667
|
+
redirect(
|
|
668
|
+
safeRedirect(url.searchParams.get("redirect"), fallbackRedirect)
|
|
669
|
+
);
|
|
670
|
+
}
|
|
622
671
|
if (config.draftSecret.length < MIN_SECRET_LENGTH) {
|
|
623
672
|
return new Response(
|
|
624
673
|
`cmssy: draftSecret must be at least ${MIN_SECRET_LENGTH} characters`,
|
|
625
674
|
{ status: 500 }
|
|
626
675
|
);
|
|
627
676
|
}
|
|
628
|
-
const url = new URL(request2.url);
|
|
629
677
|
const secret = url.searchParams.get("secret");
|
|
630
|
-
if (!secret || !
|
|
678
|
+
if (!secret || !await cmssySecretsMatch(secret, config.draftSecret)) {
|
|
631
679
|
return new Response("Invalid draft secret", { status: 401 });
|
|
632
680
|
}
|
|
633
681
|
const location = safeRedirect(
|
|
@@ -639,14 +687,6 @@ function createDraftRoute(config) {
|
|
|
639
687
|
redirect(location);
|
|
640
688
|
};
|
|
641
689
|
}
|
|
642
|
-
var CMSSY_EDIT_HEADER = "x-cmssy-edit";
|
|
643
|
-
function isCmssyEditRequest(request2) {
|
|
644
|
-
return request2.cookies.has("__prerender_bypass") || request2.nextUrl.searchParams.getAll("cmssyEdit").includes("1");
|
|
645
|
-
}
|
|
646
|
-
async function isCmssyEditMode() {
|
|
647
|
-
const h = await headers();
|
|
648
|
-
return h.get(CMSSY_EDIT_HEADER) === "1";
|
|
649
|
-
}
|
|
650
690
|
var CMSSY_LOCALE_HEADER = "x-cmssy-locale";
|
|
651
691
|
async function localeForPathname(config, pathname) {
|
|
652
692
|
const siteLocales = await resolveSiteLocales(config);
|
|
@@ -1903,4 +1943,4 @@ function verifyCmssyWebhook(options) {
|
|
|
1903
1943
|
return parsed;
|
|
1904
1944
|
}
|
|
1905
1945
|
|
|
1906
|
-
export { CMSSY_CART_COOKIE, CMSSY_EDIT_HEADER, CMSSY_LOCALE_HEADER, CMSSY_SESSION_COOKIE, CmssyWebhookError, DEFAULT_CMSSY_EDITOR_ORIGINS, SESSION_MAX_AGE_SECONDS, applyCmssyCsp, assertAuthConfig, buildCmssyMetadata, cmssyCspHeaders, createCmssyAuthMiddleware, createCmssyAuthRoute, createCmssyCartRoute, createCmssyLocaleMiddleware, createCmssyNotFound, createCmssyOrdersRoute, createCmssyPage, createCmssyRobots, createCmssySitemap, createDraftRoute, defineCmssyConfig, fetchOrderByToken, fetchProduct, fetchProducts, getCmssyAccessToken, getCmssyLocale, getCmssyUser, isAccessExpired, isCmssyEditMode, isCmssyEditRequest, localeForPathname, openSession, resolveEditorOrigin, resolveLocaleFromPathname, sealSession, sessionCookieOptions, splitCmssyLocale, verifyCmssyWebhook };
|
|
1946
|
+
export { CMSSY_CART_COOKIE, CMSSY_EDIT_HEADER, CMSSY_EDIT_QUERY_PARAM, CMSSY_LOCALE_HEADER, CMSSY_SECRET_QUERY_PARAM, CMSSY_SESSION_COOKIE, CmssyWebhookError, DEFAULT_CMSSY_EDITOR_ORIGINS, SESSION_MAX_AGE_SECONDS, applyCmssyCsp, assertAuthConfig, buildCmssyMetadata, cmssyCspHeaders, createCmssyAuthMiddleware, createCmssyAuthRoute, createCmssyCartRoute, createCmssyLocaleMiddleware, createCmssyNotFound, createCmssyOrdersRoute, createCmssyPage, createCmssyRobots, createCmssySitemap, createDraftRoute, defineCmssyConfig, fetchOrderByToken, fetchProduct, fetchProducts, getCmssyAccessToken, getCmssyLocale, getCmssyUser, isAccessExpired, isCmssyEditMode, isCmssyEditRequest, localeForPathname, openSession, resolveEditorOrigin, resolveLocaleFromPathname, sealSession, sessionCookieOptions, splitCmssyLocale, verifyCmssyWebhook };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cmssy/next",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "Next.js App Router bindings for cmssy headless sites (createCmssyPage + draft preview)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cmssy",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"dist"
|
|
42
42
|
],
|
|
43
43
|
"peerDependencies": {
|
|
44
|
-
"@cmssy/react": "^
|
|
44
|
+
"@cmssy/react": "^3.0.0",
|
|
45
45
|
"next": ">=15",
|
|
46
46
|
"react": "^18.2.0 || ^19.0.0",
|
|
47
47
|
"react-dom": "^18.2.0 || ^19.0.0"
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"tsup": "^8.3.0",
|
|
56
56
|
"typescript": "^5.6.0",
|
|
57
57
|
"vitest": "^2.1.0",
|
|
58
|
-
"@cmssy/react": "
|
|
58
|
+
"@cmssy/react": "3.0.0"
|
|
59
59
|
},
|
|
60
60
|
"dependencies": {
|
|
61
61
|
"jose": "^6.2.3",
|