@actuate-media/cms-core 0.18.0 → 0.19.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/__tests__/api/preview-and-scheduling.test.d.ts +2 -0
- package/dist/__tests__/api/preview-and-scheduling.test.d.ts.map +1 -0
- package/dist/__tests__/api/preview-and-scheduling.test.js +426 -0
- package/dist/__tests__/api/preview-and-scheduling.test.js.map +1 -0
- package/dist/__tests__/preview.test.d.ts +2 -0
- package/dist/__tests__/preview.test.d.ts.map +1 -0
- package/dist/__tests__/preview.test.js +71 -0
- package/dist/__tests__/preview.test.js.map +1 -0
- package/dist/api/handlers.d.ts.map +1 -1
- package/dist/api/handlers.js +226 -17
- package/dist/api/handlers.js.map +1 -1
- package/dist/preview/index.d.ts +23 -1
- package/dist/preview/index.d.ts.map +1 -1
- package/dist/preview/index.js +30 -7
- package/dist/preview/index.js.map +1 -1
- package/package.json +1 -1
package/dist/preview/index.d.ts
CHANGED
|
@@ -2,13 +2,35 @@ export interface PreviewSession {
|
|
|
2
2
|
token: string;
|
|
3
3
|
collection: string;
|
|
4
4
|
documentId: string;
|
|
5
|
+
/** When the token expires (server clock). */
|
|
5
6
|
expiresAt: Date;
|
|
7
|
+
/** The admin who issued the token (used for audit + future revocation). */
|
|
8
|
+
issuedBy?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface CreatePreviewSessionOptions {
|
|
11
|
+
/**
|
|
12
|
+
* Token lifetime in seconds. Defaults to 7 days. The maximum is
|
|
13
|
+
* enforced server-side ({@link MAX_PREVIEW_TOKEN_TTL_SECONDS}). A short
|
|
14
|
+
* TTL is preferable for one-off client reviews; longer TTLs are okay
|
|
15
|
+
* for staging-style preview links shared with trusted partners.
|
|
16
|
+
*/
|
|
17
|
+
ttlSeconds?: number;
|
|
18
|
+
/** Issuing admin's user id (recorded in the JWT for audit). */
|
|
19
|
+
issuedBy?: string;
|
|
6
20
|
}
|
|
7
21
|
export interface PreviewAdapter {
|
|
8
|
-
createPreviewSession(collection: string, documentId: string): Promise<PreviewSession>;
|
|
22
|
+
createPreviewSession(collection: string, documentId: string, options?: CreatePreviewSessionOptions): Promise<PreviewSession>;
|
|
9
23
|
validatePreviewToken(token: string): Promise<PreviewSession | null>;
|
|
10
24
|
exitPreview(token: string): Promise<void>;
|
|
11
25
|
getPreviewData(session: PreviewSession): Promise<Record<string, unknown> | null>;
|
|
12
26
|
}
|
|
27
|
+
/** 5 minutes — fits "one tab, one read" review flows. */
|
|
28
|
+
export declare const DEFAULT_PREVIEW_TOKEN_TTL_SECONDS: number;
|
|
29
|
+
/**
|
|
30
|
+
* Upper bound for token lifetime. Tokens are JWT-signed and stateless, so a
|
|
31
|
+
* leaked token is valid until expiry — keep the ceiling reasonable. Sites
|
|
32
|
+
* that need shareable URLs for months should use draft-publishing instead.
|
|
33
|
+
*/
|
|
34
|
+
export declare const MAX_PREVIEW_TOKEN_TTL_SECONDS: number;
|
|
13
35
|
export declare function createPreviewAdapter(secret: string, db: unknown): PreviewAdapter;
|
|
14
36
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/preview/index.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,IAAI,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/preview/index.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,6CAA6C;IAC7C,SAAS,EAAE,IAAI,CAAA;IACf,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,2BAA2B;IAC1C;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,oBAAoB,CAClB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,2BAA2B,GACpC,OAAO,CAAC,cAAc,CAAC,CAAA;IAC1B,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAAA;IACnE,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACzC,cAAc,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAAA;CACjF;AAED,yDAAyD;AACzD,eAAO,MAAM,iCAAiC,QAAmB,CAAA;AAEjE;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,QAAoB,CAAA;AAE9D,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,GAAG,cAAc,CA2DhF"}
|
package/dist/preview/index.js
CHANGED
|
@@ -1,16 +1,35 @@
|
|
|
1
1
|
import * as jose from 'jose';
|
|
2
|
+
/** 5 minutes — fits "one tab, one read" review flows. */
|
|
3
|
+
export const DEFAULT_PREVIEW_TOKEN_TTL_SECONDS = 7 * 24 * 60 * 60; // 7 days
|
|
4
|
+
/**
|
|
5
|
+
* Upper bound for token lifetime. Tokens are JWT-signed and stateless, so a
|
|
6
|
+
* leaked token is valid until expiry — keep the ceiling reasonable. Sites
|
|
7
|
+
* that need shareable URLs for months should use draft-publishing instead.
|
|
8
|
+
*/
|
|
9
|
+
export const MAX_PREVIEW_TOKEN_TTL_SECONDS = 30 * 24 * 60 * 60; // 30 days
|
|
2
10
|
export function createPreviewAdapter(secret, db) {
|
|
3
11
|
const secretKey = new TextEncoder().encode(secret);
|
|
4
12
|
return {
|
|
5
|
-
async createPreviewSession(collection, documentId) {
|
|
6
|
-
const
|
|
7
|
-
const
|
|
13
|
+
async createPreviewSession(collection, documentId, options) {
|
|
14
|
+
const requested = options?.ttlSeconds ?? DEFAULT_PREVIEW_TOKEN_TTL_SECONDS;
|
|
15
|
+
const ttl = Math.max(60, Math.min(requested, MAX_PREVIEW_TOKEN_TTL_SECONDS));
|
|
16
|
+
const expiresAt = new Date(Date.now() + ttl * 1000);
|
|
17
|
+
let builder = new jose.SignJWT({ collection, documentId })
|
|
8
18
|
.setProtectedHeader({ alg: 'HS256' })
|
|
9
19
|
.setIssuedAt()
|
|
10
20
|
.setExpirationTime(expiresAt)
|
|
11
|
-
.setIssuer('actuate-cms-preview')
|
|
12
|
-
|
|
13
|
-
|
|
21
|
+
.setIssuer('actuate-cms-preview');
|
|
22
|
+
if (options?.issuedBy) {
|
|
23
|
+
builder = builder.setSubject(options.issuedBy);
|
|
24
|
+
}
|
|
25
|
+
const token = await builder.sign(secretKey);
|
|
26
|
+
return {
|
|
27
|
+
token,
|
|
28
|
+
collection,
|
|
29
|
+
documentId,
|
|
30
|
+
expiresAt,
|
|
31
|
+
issuedBy: options?.issuedBy,
|
|
32
|
+
};
|
|
14
33
|
},
|
|
15
34
|
async validatePreviewToken(token) {
|
|
16
35
|
try {
|
|
@@ -22,6 +41,7 @@ export function createPreviewAdapter(secret, db) {
|
|
|
22
41
|
collection: payload.collection,
|
|
23
42
|
documentId: payload.documentId,
|
|
24
43
|
expiresAt: new Date((payload.exp ?? 0) * 1000),
|
|
44
|
+
issuedBy: typeof payload.sub === 'string' ? payload.sub : undefined,
|
|
25
45
|
};
|
|
26
46
|
}
|
|
27
47
|
catch {
|
|
@@ -29,7 +49,10 @@ export function createPreviewAdapter(secret, db) {
|
|
|
29
49
|
}
|
|
30
50
|
},
|
|
31
51
|
async exitPreview(_token) {
|
|
32
|
-
// JWT-based tokens are stateless; no server-side invalidation needed
|
|
52
|
+
// JWT-based tokens are stateless; no server-side invalidation needed.
|
|
53
|
+
// For revocation, rotate the global CMS_SECRET (invalidates all tokens
|
|
54
|
+
// at once). Per-token revocation requires a backing table — track via
|
|
55
|
+
// future Slice if needed.
|
|
33
56
|
},
|
|
34
57
|
async getPreviewData(session) {
|
|
35
58
|
const prisma = db;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/preview/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/preview/index.ts"],"names":[],"mappings":"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"}
|