@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.
@@ -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;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,oBAAoB,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAAA;IACrF,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,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,GAAG,cAAc,CA4ChF"}
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"}
@@ -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 expiresAt = new Date(Date.now() + 5 * 60 * 1000);
7
- const token = await new jose.SignJWT({ collection, documentId })
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
- .sign(secretKey);
13
- return { token, collection, documentId, expiresAt };
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;AAgB5B,MAAM,UAAU,oBAAoB,CAAC,MAAc,EAAE,EAAW;IAC9D,MAAM,SAAS,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IAElD,OAAO;QACL,KAAK,CAAC,oBAAoB,CAAC,UAAU,EAAE,UAAU;YAC/C,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAA;YACtD,MAAM,KAAK,GAAG,MAAM,IAAI,IAAI,CAAC,OAAO,CAAC,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC;iBAC7D,kBAAkB,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC;iBACpC,WAAW,EAAE;iBACb,iBAAiB,CAAC,SAAS,CAAC;iBAC5B,SAAS,CAAC,qBAAqB,CAAC;iBAChC,IAAI,CAAC,SAAS,CAAC,CAAA;YAClB,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,CAAA;QACrD,CAAC;QAED,KAAK,CAAC,oBAAoB,CAAC,KAAK;YAC9B,IAAI,CAAC;gBACH,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,SAAS,EAAE;oBACzD,MAAM,EAAE,qBAAqB;iBAC9B,CAAC,CAAA;gBACF,OAAO;oBACL,KAAK;oBACL,UAAU,EAAE,OAAO,CAAC,UAAoB;oBACxC,UAAU,EAAE,OAAO,CAAC,UAAoB;oBACxC,SAAS,EAAE,IAAI,IAAI,CAAC,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC;iBAC/C,CAAA;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,IAAI,CAAA;YACb,CAAC;QACH,CAAC;QAED,KAAK,CAAC,WAAW,CAAC,MAAM;YACtB,qEAAqE;QACvE,CAAC;QAED,KAAK,CAAC,cAAc,CAAC,OAAO;YAC1B,MAAM,MAAM,GAAG,EAAS,CAAA;YACxB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC;gBAC1C,KAAK,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,UAAU,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE;aAClE,CAAC,CAAA;YACF,IAAI,CAAC,GAAG;gBAAE,OAAO,IAAI,CAAA;YACrB,OAAO,EAAE,GAAG,GAAG,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAA;QACnC,CAAC;KACF,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/preview/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@actuate-media/cms-core",
3
- "version": "0.18.0",
3
+ "version": "0.19.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/actuate-media/actuatecms.git",