@strapi/content-releases 5.53.0 → 5.55.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/server/audit-logs.js +3 -16
- package/dist/server/audit-logs.js.map +1 -1
- package/dist/server/audit-logs.mjs +2 -14
- package/dist/server/audit-logs.mjs.map +1 -1
- package/dist/server/services/release.js +5 -5
- package/dist/server/services/release.js.map +1 -1
- package/dist/server/services/release.mjs +2 -2
- package/dist/server/services/release.mjs.map +1 -1
- package/dist/server/services/scheduling.js +5 -3
- package/dist/server/services/scheduling.js.map +1 -1
- package/dist/server/services/scheduling.mjs +5 -3
- package/dist/server/services/scheduling.mjs.map +1 -1
- package/dist/server/services/settings.js +2 -2
- package/dist/server/services/settings.js.map +1 -1
- package/dist/server/services/settings.mjs +1 -1
- package/dist/server/services/settings.mjs.map +1 -1
- package/dist/server/src/audit-logs.d.ts +3 -15
- package/dist/server/src/audit-logs.d.ts.map +1 -1
- package/dist/server/src/services/scheduling.d.ts.map +1 -1
- package/dist/server/src/services/settings.d.ts.map +1 -1
- package/package.json +7 -7
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var utils = require('@strapi/utils');
|
|
3
4
|
var constants = require('./constants.js');
|
|
4
5
|
|
|
5
6
|
const RELEASE_EDITABLE_FIELDS = [
|
|
@@ -34,31 +35,18 @@ const RELEASE_EDITABLE_FIELDS = [
|
|
|
34
35
|
}
|
|
35
36
|
return changes;
|
|
36
37
|
};
|
|
37
|
-
/**
|
|
38
|
-
* Emits an audit event and waits for it to be processed.
|
|
39
|
-
* A failed audit write, or a failing listener, is logged here and doesn't affect the
|
|
40
|
-
* operation that emitted the event.
|
|
41
|
-
*/ const emitAudit = async ({ strapi }, event, payload)=>{
|
|
42
|
-
try {
|
|
43
|
-
await strapi.eventHub.emit(event, payload);
|
|
44
|
-
} catch (error) {
|
|
45
|
-
strapi.log.error(`An event listener failed while handling ${event}`, {
|
|
46
|
-
error
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
38
|
/**
|
|
51
39
|
* Emits an audit event for a write that may be part of a bulk transaction.
|
|
52
40
|
* Inside one, the event waits for its commit, so a rollback leaves no rows for
|
|
53
41
|
* entries that were never added. Standalone, it is emitted right away.
|
|
54
42
|
*/ const emitAuditOnCommit = async ({ strapi }, event, payload)=>{
|
|
55
43
|
if (!strapi.db.inTransaction()) {
|
|
56
|
-
await emitAudit({
|
|
44
|
+
await utils.emitAudit({
|
|
57
45
|
strapi
|
|
58
46
|
}, event, payload);
|
|
59
47
|
return;
|
|
60
48
|
}
|
|
61
|
-
await strapi.db.transaction(({ onCommit })=>onCommit(()=>emitAudit({
|
|
49
|
+
await strapi.db.transaction(({ onCommit })=>onCommit(()=>utils.emitAudit({
|
|
62
50
|
strapi
|
|
63
51
|
}, event, payload)));
|
|
64
52
|
};
|
|
@@ -174,7 +162,6 @@ const registerAuditEvents = (auditLogsLifecycle)=>{
|
|
|
174
162
|
};
|
|
175
163
|
|
|
176
164
|
exports.RELEASE_EDITABLE_FIELDS = RELEASE_EDITABLE_FIELDS;
|
|
177
|
-
exports.emitAudit = emitAudit;
|
|
178
165
|
exports.emitAuditOnCommit = emitAuditOnCommit;
|
|
179
166
|
exports.getReleaseChanges = getReleaseChanges;
|
|
180
167
|
exports.registerAuditEvents = registerAuditEvents;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit-logs.js","sources":["../../server/src/audit-logs.ts"],"sourcesContent":["import type { Core, Modules } from '@strapi/types';\n\nimport { AUDITED_EVENTS } from './constants';\n\n/**\n * Transformers for the release audit events.\n * Each builds {resource, details, outcome} from its emitted event.\n */\n\ninterface ReleaseEvent {\n releaseId: number;\n name?: string;\n scheduledAt?: string | null;\n timezone?: string | null;\n}\n\ninterface ReleaseEntryEvent {\n releaseId: number;\n name?: string;\n contentType: string;\n entryDocumentId: string;\n locale: string | null;\n}\n\nexport interface CreateDetails {\n isScheduled: boolean;\n scheduledAt?: string | null;\n timezone?: string | null;\n}\n\nexport interface FieldChange {\n before: string | boolean | null;\n after: string | boolean | null;\n}\n\nexport const RELEASE_EDITABLE_FIELDS = ['name', 'scheduledAt', 'timezone'] as const;\n\n/** Audit rows are never pruned by size, so the failure reason is capped */\nconst MAX_REASON_LENGTH = 100;\n\ntype ReleaseChangedField = (typeof RELEASE_EDITABLE_FIELDS)[number] | 'isScheduled';\n\nexport interface UpdateDetails {\n changes: Partial<Record<ReleaseChangedField, FieldChange>>;\n}\n\n/**\n * Returns the fields that changed in a release update.\n * `isScheduled` is included when the release changes between scheduled and unscheduled.\n */\nexport const getReleaseChanges = (\n previous: { name?: string; scheduledAt?: string | null; timezone?: string | null } | null,\n next: { name?: string; scheduledAt?: string | null; timezone?: string | null }\n) => {\n const changes: Partial<Record<ReleaseChangedField, FieldChange>> = {};\n\n for (const field of RELEASE_EDITABLE_FIELDS) {\n const hasChanged = (previous?.[field] ?? null) !== (next[field] ?? null);\n\n if (hasChanged) {\n changes[field] = { before: previous?.[field] ?? null, after: next[field] ?? null };\n }\n\n if (field === 'scheduledAt' && changes.scheduledAt) {\n const wasScheduled = previous?.scheduledAt != null;\n const isScheduled = next.scheduledAt != null;\n\n if (wasScheduled !== isScheduled) {\n changes.isScheduled = { before: wasScheduled, after: isScheduled };\n }\n }\n }\n\n return changes;\n};\n\nexport interface TriggerDetails {\n published: number;\n unpublished: number;\n failed: number;\n}\n\nexport interface TriggerFailureDetails {\n /**\n * Only the error name is stored, capped in length. Error objects may contain\n * sensitive data such as database error details, and a custom error class can put\n * arbitrary text in its name.\n */\n reason: string;\n // A failed run is not atomic, so counts are not reported. Reserved for partial releases.\n published?: number;\n unpublished?: number;\n failed?: number;\n}\n\ntype TriggerEvent = ReleaseEvent &\n (\n | { outcome: 'success'; published?: number; unpublished?: number }\n | {\n outcome: 'failure';\n reason: string;\n published?: number;\n unpublished?: number;\n failed?: number;\n }\n );\n\nexport interface SettingsUpdateDetails {\n changes: Partial<Record<'defaultTimezone', FieldChange>>;\n}\n\ninterface EntryRef {\n contentType: string;\n documentId: string;\n locale: string | null;\n}\n\nexport interface EntryAddDetails {\n entry: EntryRef;\n actionType: 'publish' | 'unpublish';\n}\n\nexport interface EntryUpdateDetails {\n entry: EntryRef;\n actionType: { before: 'publish' | 'unpublish'; after: 'publish' | 'unpublish' };\n}\n\nexport interface EntryRemoveDetails {\n entry: EntryRef;\n}\n\n/**\n * Emits an audit event and waits for it to be processed.\n * A failed audit write, or a failing listener, is logged here and doesn't affect the\n * operation that emitted the event.\n */\nexport const emitAudit = async (\n { strapi }: { strapi: Core.Strapi },\n event: string,\n payload: unknown\n): Promise<void> => {\n try {\n await strapi.eventHub.emit(event, payload);\n } catch (error) {\n strapi.log.error(`An event listener failed while handling ${event}`, { error });\n }\n};\n\n/**\n * Emits an audit event for a write that may be part of a bulk transaction.\n * Inside one, the event waits for its commit, so a rollback leaves no rows for\n * entries that were never added. Standalone, it is emitted right away.\n */\nexport const emitAuditOnCommit = async (\n { strapi }: { strapi: Core.Strapi },\n event: string,\n payload: unknown\n): Promise<void> => {\n if (!strapi.db.inTransaction()) {\n await emitAudit({ strapi }, event, payload);\n return;\n }\n\n await strapi.db.transaction(({ onCommit }) =>\n onCommit(() => emitAudit({ strapi }, event, payload))\n );\n};\n\n/**\n * Runs work as a system principal so events emitted during the operation\n * are recorded with a system actor.\n */\nexport const runAsSystem = async <T>(\n { strapi }: { strapi: Core.Strapi },\n origin: Modules.AuditLogs.SystemOrigin,\n work: () => Promise<T>\n): Promise<T> => {\n let result: T;\n\n await strapi.requestContext.run(\n { state: { auditSource: origin }, request: { url: '' } } as any,\n async () => {\n result = await work();\n }\n );\n\n return result!;\n};\n\n/**\n * The part of the audit-logs lifecycle service used by this plugin.\n * The full service type lives in the Admin EE package and cannot be imported here.\n */\ninterface AuditLogsLifecycle {\n registerEvent<TDetails>(\n name: string,\n transform: Modules.AuditLogs.EventTransformer<TDetails>\n ): void;\n}\n\nexport const registerAuditEvents = (auditLogsLifecycle: AuditLogsLifecycle) => {\n const releaseResource = (event: ReleaseEvent): Modules.AuditLogs.Resource => ({\n type: 'release',\n id: event.releaseId,\n name: event.name,\n });\n\n const entryRef = (event: ReleaseEntryEvent): EntryRef => ({\n contentType: event.contentType,\n documentId: event.entryDocumentId,\n locale: event.locale ?? null,\n });\n\n auditLogsLifecycle.registerEvent<CreateDetails>(\n AUDITED_EVENTS.RELEASE_CREATE,\n (event: ReleaseEvent) => ({\n resource: releaseResource(event),\n details: {\n isScheduled: event.scheduledAt != null,\n ...(event.scheduledAt && { scheduledAt: event.scheduledAt, timezone: event.timezone }),\n },\n })\n );\n\n auditLogsLifecycle.registerEvent<UpdateDetails>(\n AUDITED_EVENTS.RELEASE_UPDATE,\n (event: ReleaseEvent & UpdateDetails) => ({\n resource: releaseResource(event),\n details: { changes: event.changes },\n })\n );\n\n auditLogsLifecycle.registerEvent(AUDITED_EVENTS.RELEASE_DELETE, (event: ReleaseEvent) => ({\n resource: releaseResource(event),\n }));\n\n auditLogsLifecycle.registerEvent<TriggerDetails | TriggerFailureDetails>(\n AUDITED_EVENTS.RELEASE_TRIGGER,\n (event: TriggerEvent) => {\n const base = { resource: releaseResource(event), outcome: event.outcome };\n\n if (event.outcome === 'failure') {\n return {\n ...base,\n details: {\n reason: event.reason?.slice(0, MAX_REASON_LENGTH),\n // Only when the emitter can attribute the counts.\n ...(event.published != null && {\n published: event.published,\n unpublished: event.unpublished,\n failed: event.failed,\n }),\n },\n };\n }\n\n if (event.published != null && event.unpublished != null) {\n return {\n ...base,\n details: { published: event.published, unpublished: event.unpublished, failed: 0 },\n };\n }\n\n return base;\n }\n );\n\n auditLogsLifecycle.registerEvent<EntryAddDetails>(\n AUDITED_EVENTS.RELEASE_ENTRY_ADD,\n (event: ReleaseEntryEvent & { type: 'publish' | 'unpublish' }) => ({\n resource: releaseResource(event),\n details: { entry: entryRef(event), actionType: event.type },\n })\n );\n\n auditLogsLifecycle.registerEvent<EntryUpdateDetails>(\n AUDITED_EVENTS.RELEASE_ENTRY_UPDATE,\n (\n event: ReleaseEntryEvent & {\n from: 'publish' | 'unpublish';\n to: 'publish' | 'unpublish';\n }\n ) => ({\n resource: releaseResource(event),\n details: { entry: entryRef(event), actionType: { before: event.from, after: event.to } },\n })\n );\n\n auditLogsLifecycle.registerEvent<EntryRemoveDetails>(\n AUDITED_EVENTS.RELEASE_ENTRY_REMOVE,\n (event: ReleaseEntryEvent) => ({\n resource: releaseResource(event),\n details: { entry: entryRef(event) },\n })\n );\n\n auditLogsLifecycle.registerEvent<SettingsUpdateDetails>(\n AUDITED_EVENTS.RELEASE_SETTINGS_UPDATE,\n (event: SettingsUpdateDetails) => ({\n resource: { type: 'release' },\n details: { changes: event.changes },\n })\n );\n};\n"],"names":["RELEASE_EDITABLE_FIELDS","MAX_REASON_LENGTH","getReleaseChanges","previous","next","changes","field","hasChanged","before","after","scheduledAt","wasScheduled","isScheduled","emitAudit","strapi","event","payload","eventHub","emit","error","log","emitAuditOnCommit","db","inTransaction","transaction","onCommit","runAsSystem","origin","work","result","requestContext","run","state","auditSource","request","url","registerAuditEvents","auditLogsLifecycle","releaseResource","type","id","releaseId","name","entryRef","contentType","documentId","entryDocumentId","locale","registerEvent","AUDITED_EVENTS","RELEASE_CREATE","resource","details","timezone","RELEASE_UPDATE","RELEASE_DELETE","RELEASE_TRIGGER","base","outcome","reason","slice","published","unpublished","failed","RELEASE_ENTRY_ADD","entry","actionType","RELEASE_ENTRY_UPDATE","from","to","RELEASE_ENTRY_REMOVE","RELEASE_SETTINGS_UPDATE"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"audit-logs.js","sources":["../../server/src/audit-logs.ts"],"sourcesContent":["import type { Core, Modules } from '@strapi/types';\nimport { emitAudit } from '@strapi/utils';\n\nimport { AUDITED_EVENTS } from './constants';\n\n/**\n * Transformers for the release audit events.\n * Each builds {resource, details, outcome} from its emitted event.\n */\n\ninterface ReleaseEvent {\n releaseId: number;\n name?: string;\n scheduledAt?: string | null;\n timezone?: string | null;\n}\n\ninterface ReleaseEntryEvent {\n releaseId: number;\n name?: string;\n contentType: string;\n entryDocumentId: string;\n locale: string | null;\n}\n\nexport interface CreateDetails {\n isScheduled: boolean;\n scheduledAt?: string | null;\n timezone?: string | null;\n}\n\nexport const RELEASE_EDITABLE_FIELDS = ['name', 'scheduledAt', 'timezone'] as const;\n\n/** Audit rows are never pruned by size, so the failure reason is capped */\nconst MAX_REASON_LENGTH = 100;\n\ntype ReleaseChangedField = (typeof RELEASE_EDITABLE_FIELDS)[number] | 'isScheduled';\n\nexport interface UpdateDetails {\n changes: Partial<Record<ReleaseChangedField, Modules.AuditLogs.FieldChange>>;\n}\n\n/**\n * Returns the fields that changed in a release update.\n * `isScheduled` is included when the release changes between scheduled and unscheduled.\n */\nexport const getReleaseChanges = (\n previous: { name?: string; scheduledAt?: string | null; timezone?: string | null } | null,\n next: { name?: string; scheduledAt?: string | null; timezone?: string | null }\n) => {\n const changes: Partial<Record<ReleaseChangedField, Modules.AuditLogs.FieldChange>> = {};\n\n for (const field of RELEASE_EDITABLE_FIELDS) {\n const hasChanged = (previous?.[field] ?? null) !== (next[field] ?? null);\n\n if (hasChanged) {\n changes[field] = { before: previous?.[field] ?? null, after: next[field] ?? null };\n }\n\n if (field === 'scheduledAt' && changes.scheduledAt) {\n const wasScheduled = previous?.scheduledAt != null;\n const isScheduled = next.scheduledAt != null;\n\n if (wasScheduled !== isScheduled) {\n changes.isScheduled = { before: wasScheduled, after: isScheduled };\n }\n }\n }\n\n return changes;\n};\n\nexport interface TriggerDetails {\n published: number;\n unpublished: number;\n failed: number;\n}\n\nexport interface TriggerFailureDetails {\n /**\n * Only the error name is stored, capped in length. Error objects may contain\n * sensitive data such as database error details, and a custom error class can put\n * arbitrary text in its name.\n */\n reason: string;\n // A failed run is not atomic, so counts are not reported. Reserved for partial releases.\n published?: number;\n unpublished?: number;\n failed?: number;\n}\n\ntype TriggerEvent = ReleaseEvent &\n (\n | { outcome: 'success'; published?: number; unpublished?: number }\n | {\n outcome: 'failure';\n reason: string;\n published?: number;\n unpublished?: number;\n failed?: number;\n }\n );\n\nexport interface SettingsUpdateDetails {\n changes: Partial<Record<'defaultTimezone', Modules.AuditLogs.FieldChange>>;\n}\n\ninterface EntryRef {\n contentType: string;\n documentId: string;\n locale: string | null;\n}\n\nexport interface EntryAddDetails {\n entry: EntryRef;\n actionType: 'publish' | 'unpublish';\n}\n\nexport interface EntryUpdateDetails {\n entry: EntryRef;\n actionType: { before: 'publish' | 'unpublish'; after: 'publish' | 'unpublish' };\n}\n\nexport interface EntryRemoveDetails {\n entry: EntryRef;\n}\n\n/**\n * Emits an audit event for a write that may be part of a bulk transaction.\n * Inside one, the event waits for its commit, so a rollback leaves no rows for\n * entries that were never added. Standalone, it is emitted right away.\n */\nexport const emitAuditOnCommit = async (\n { strapi }: { strapi: Core.Strapi },\n event: string,\n payload: unknown\n): Promise<void> => {\n if (!strapi.db.inTransaction()) {\n await emitAudit({ strapi }, event, payload);\n return;\n }\n\n await strapi.db.transaction(({ onCommit }) =>\n onCommit(() => emitAudit({ strapi }, event, payload))\n );\n};\n\n/**\n * Runs work as a system principal so events emitted during the operation\n * are recorded with a system actor.\n */\nexport const runAsSystem = async <T>(\n { strapi }: { strapi: Core.Strapi },\n origin: Modules.AuditLogs.SystemOrigin,\n work: () => Promise<T>\n): Promise<T> => {\n let result: T;\n\n await strapi.requestContext.run(\n { state: { auditSource: origin }, request: { url: '' } } as any,\n async () => {\n result = await work();\n }\n );\n\n return result!;\n};\n\n/**\n * The part of the audit-logs lifecycle service used by this plugin.\n * The full service type lives in the Admin EE package and cannot be imported here.\n */\ninterface AuditLogsLifecycle {\n registerEvent<TDetails>(\n name: string,\n transform: Modules.AuditLogs.EventTransformer<TDetails>\n ): void;\n}\n\nexport const registerAuditEvents = (auditLogsLifecycle: AuditLogsLifecycle) => {\n const releaseResource = (event: ReleaseEvent): Modules.AuditLogs.Resource => ({\n type: 'release',\n id: event.releaseId,\n name: event.name,\n });\n\n const entryRef = (event: ReleaseEntryEvent): EntryRef => ({\n contentType: event.contentType,\n documentId: event.entryDocumentId,\n locale: event.locale ?? null,\n });\n\n auditLogsLifecycle.registerEvent<CreateDetails>(\n AUDITED_EVENTS.RELEASE_CREATE,\n (event: ReleaseEvent) => ({\n resource: releaseResource(event),\n details: {\n isScheduled: event.scheduledAt != null,\n ...(event.scheduledAt && { scheduledAt: event.scheduledAt, timezone: event.timezone }),\n },\n })\n );\n\n auditLogsLifecycle.registerEvent<UpdateDetails>(\n AUDITED_EVENTS.RELEASE_UPDATE,\n (event: ReleaseEvent & UpdateDetails) => ({\n resource: releaseResource(event),\n details: { changes: event.changes },\n })\n );\n\n auditLogsLifecycle.registerEvent(AUDITED_EVENTS.RELEASE_DELETE, (event: ReleaseEvent) => ({\n resource: releaseResource(event),\n }));\n\n auditLogsLifecycle.registerEvent<TriggerDetails | TriggerFailureDetails>(\n AUDITED_EVENTS.RELEASE_TRIGGER,\n (event: TriggerEvent) => {\n const base = { resource: releaseResource(event), outcome: event.outcome };\n\n if (event.outcome === 'failure') {\n return {\n ...base,\n details: {\n reason: event.reason?.slice(0, MAX_REASON_LENGTH),\n // Only when the emitter can attribute the counts.\n ...(event.published != null && {\n published: event.published,\n unpublished: event.unpublished,\n failed: event.failed,\n }),\n },\n };\n }\n\n if (event.published != null && event.unpublished != null) {\n return {\n ...base,\n details: { published: event.published, unpublished: event.unpublished, failed: 0 },\n };\n }\n\n return base;\n }\n );\n\n auditLogsLifecycle.registerEvent<EntryAddDetails>(\n AUDITED_EVENTS.RELEASE_ENTRY_ADD,\n (event: ReleaseEntryEvent & { type: 'publish' | 'unpublish' }) => ({\n resource: releaseResource(event),\n details: { entry: entryRef(event), actionType: event.type },\n })\n );\n\n auditLogsLifecycle.registerEvent<EntryUpdateDetails>(\n AUDITED_EVENTS.RELEASE_ENTRY_UPDATE,\n (\n event: ReleaseEntryEvent & {\n from: 'publish' | 'unpublish';\n to: 'publish' | 'unpublish';\n }\n ) => ({\n resource: releaseResource(event),\n details: { entry: entryRef(event), actionType: { before: event.from, after: event.to } },\n })\n );\n\n auditLogsLifecycle.registerEvent<EntryRemoveDetails>(\n AUDITED_EVENTS.RELEASE_ENTRY_REMOVE,\n (event: ReleaseEntryEvent) => ({\n resource: releaseResource(event),\n details: { entry: entryRef(event) },\n })\n );\n\n auditLogsLifecycle.registerEvent<SettingsUpdateDetails>(\n AUDITED_EVENTS.RELEASE_SETTINGS_UPDATE,\n (event: SettingsUpdateDetails) => ({\n resource: { type: 'release' },\n details: { changes: event.changes },\n })\n );\n};\n"],"names":["RELEASE_EDITABLE_FIELDS","MAX_REASON_LENGTH","getReleaseChanges","previous","next","changes","field","hasChanged","before","after","scheduledAt","wasScheduled","isScheduled","emitAuditOnCommit","strapi","event","payload","db","inTransaction","emitAudit","transaction","onCommit","runAsSystem","origin","work","result","requestContext","run","state","auditSource","request","url","registerAuditEvents","auditLogsLifecycle","releaseResource","type","id","releaseId","name","entryRef","contentType","documentId","entryDocumentId","locale","registerEvent","AUDITED_EVENTS","RELEASE_CREATE","resource","details","timezone","RELEASE_UPDATE","RELEASE_DELETE","RELEASE_TRIGGER","base","outcome","reason","slice","published","unpublished","failed","RELEASE_ENTRY_ADD","entry","actionType","RELEASE_ENTRY_UPDATE","from","to","RELEASE_ENTRY_REMOVE","RELEASE_SETTINGS_UPDATE"],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { emitAudit } from '@strapi/utils';
|
|
1
2
|
import { AUDITED_EVENTS } from './constants.mjs';
|
|
2
3
|
|
|
3
4
|
const RELEASE_EDITABLE_FIELDS = [
|
|
@@ -32,19 +33,6 @@ const RELEASE_EDITABLE_FIELDS = [
|
|
|
32
33
|
}
|
|
33
34
|
return changes;
|
|
34
35
|
};
|
|
35
|
-
/**
|
|
36
|
-
* Emits an audit event and waits for it to be processed.
|
|
37
|
-
* A failed audit write, or a failing listener, is logged here and doesn't affect the
|
|
38
|
-
* operation that emitted the event.
|
|
39
|
-
*/ const emitAudit = async ({ strapi }, event, payload)=>{
|
|
40
|
-
try {
|
|
41
|
-
await strapi.eventHub.emit(event, payload);
|
|
42
|
-
} catch (error) {
|
|
43
|
-
strapi.log.error(`An event listener failed while handling ${event}`, {
|
|
44
|
-
error
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
36
|
/**
|
|
49
37
|
* Emits an audit event for a write that may be part of a bulk transaction.
|
|
50
38
|
* Inside one, the event waits for its commit, so a rollback leaves no rows for
|
|
@@ -171,5 +159,5 @@ const registerAuditEvents = (auditLogsLifecycle)=>{
|
|
|
171
159
|
}));
|
|
172
160
|
};
|
|
173
161
|
|
|
174
|
-
export { RELEASE_EDITABLE_FIELDS,
|
|
162
|
+
export { RELEASE_EDITABLE_FIELDS, emitAuditOnCommit, getReleaseChanges, registerAuditEvents, runAsSystem };
|
|
175
163
|
//# sourceMappingURL=audit-logs.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit-logs.mjs","sources":["../../server/src/audit-logs.ts"],"sourcesContent":["import type { Core, Modules } from '@strapi/types';\n\nimport { AUDITED_EVENTS } from './constants';\n\n/**\n * Transformers for the release audit events.\n * Each builds {resource, details, outcome} from its emitted event.\n */\n\ninterface ReleaseEvent {\n releaseId: number;\n name?: string;\n scheduledAt?: string | null;\n timezone?: string | null;\n}\n\ninterface ReleaseEntryEvent {\n releaseId: number;\n name?: string;\n contentType: string;\n entryDocumentId: string;\n locale: string | null;\n}\n\nexport interface CreateDetails {\n isScheduled: boolean;\n scheduledAt?: string | null;\n timezone?: string | null;\n}\n\nexport interface FieldChange {\n before: string | boolean | null;\n after: string | boolean | null;\n}\n\nexport const RELEASE_EDITABLE_FIELDS = ['name', 'scheduledAt', 'timezone'] as const;\n\n/** Audit rows are never pruned by size, so the failure reason is capped */\nconst MAX_REASON_LENGTH = 100;\n\ntype ReleaseChangedField = (typeof RELEASE_EDITABLE_FIELDS)[number] | 'isScheduled';\n\nexport interface UpdateDetails {\n changes: Partial<Record<ReleaseChangedField, FieldChange>>;\n}\n\n/**\n * Returns the fields that changed in a release update.\n * `isScheduled` is included when the release changes between scheduled and unscheduled.\n */\nexport const getReleaseChanges = (\n previous: { name?: string; scheduledAt?: string | null; timezone?: string | null } | null,\n next: { name?: string; scheduledAt?: string | null; timezone?: string | null }\n) => {\n const changes: Partial<Record<ReleaseChangedField, FieldChange>> = {};\n\n for (const field of RELEASE_EDITABLE_FIELDS) {\n const hasChanged = (previous?.[field] ?? null) !== (next[field] ?? null);\n\n if (hasChanged) {\n changes[field] = { before: previous?.[field] ?? null, after: next[field] ?? null };\n }\n\n if (field === 'scheduledAt' && changes.scheduledAt) {\n const wasScheduled = previous?.scheduledAt != null;\n const isScheduled = next.scheduledAt != null;\n\n if (wasScheduled !== isScheduled) {\n changes.isScheduled = { before: wasScheduled, after: isScheduled };\n }\n }\n }\n\n return changes;\n};\n\nexport interface TriggerDetails {\n published: number;\n unpublished: number;\n failed: number;\n}\n\nexport interface TriggerFailureDetails {\n /**\n * Only the error name is stored, capped in length. Error objects may contain\n * sensitive data such as database error details, and a custom error class can put\n * arbitrary text in its name.\n */\n reason: string;\n // A failed run is not atomic, so counts are not reported. Reserved for partial releases.\n published?: number;\n unpublished?: number;\n failed?: number;\n}\n\ntype TriggerEvent = ReleaseEvent &\n (\n | { outcome: 'success'; published?: number; unpublished?: number }\n | {\n outcome: 'failure';\n reason: string;\n published?: number;\n unpublished?: number;\n failed?: number;\n }\n );\n\nexport interface SettingsUpdateDetails {\n changes: Partial<Record<'defaultTimezone', FieldChange>>;\n}\n\ninterface EntryRef {\n contentType: string;\n documentId: string;\n locale: string | null;\n}\n\nexport interface EntryAddDetails {\n entry: EntryRef;\n actionType: 'publish' | 'unpublish';\n}\n\nexport interface EntryUpdateDetails {\n entry: EntryRef;\n actionType: { before: 'publish' | 'unpublish'; after: 'publish' | 'unpublish' };\n}\n\nexport interface EntryRemoveDetails {\n entry: EntryRef;\n}\n\n/**\n * Emits an audit event and waits for it to be processed.\n * A failed audit write, or a failing listener, is logged here and doesn't affect the\n * operation that emitted the event.\n */\nexport const emitAudit = async (\n { strapi }: { strapi: Core.Strapi },\n event: string,\n payload: unknown\n): Promise<void> => {\n try {\n await strapi.eventHub.emit(event, payload);\n } catch (error) {\n strapi.log.error(`An event listener failed while handling ${event}`, { error });\n }\n};\n\n/**\n * Emits an audit event for a write that may be part of a bulk transaction.\n * Inside one, the event waits for its commit, so a rollback leaves no rows for\n * entries that were never added. Standalone, it is emitted right away.\n */\nexport const emitAuditOnCommit = async (\n { strapi }: { strapi: Core.Strapi },\n event: string,\n payload: unknown\n): Promise<void> => {\n if (!strapi.db.inTransaction()) {\n await emitAudit({ strapi }, event, payload);\n return;\n }\n\n await strapi.db.transaction(({ onCommit }) =>\n onCommit(() => emitAudit({ strapi }, event, payload))\n );\n};\n\n/**\n * Runs work as a system principal so events emitted during the operation\n * are recorded with a system actor.\n */\nexport const runAsSystem = async <T>(\n { strapi }: { strapi: Core.Strapi },\n origin: Modules.AuditLogs.SystemOrigin,\n work: () => Promise<T>\n): Promise<T> => {\n let result: T;\n\n await strapi.requestContext.run(\n { state: { auditSource: origin }, request: { url: '' } } as any,\n async () => {\n result = await work();\n }\n );\n\n return result!;\n};\n\n/**\n * The part of the audit-logs lifecycle service used by this plugin.\n * The full service type lives in the Admin EE package and cannot be imported here.\n */\ninterface AuditLogsLifecycle {\n registerEvent<TDetails>(\n name: string,\n transform: Modules.AuditLogs.EventTransformer<TDetails>\n ): void;\n}\n\nexport const registerAuditEvents = (auditLogsLifecycle: AuditLogsLifecycle) => {\n const releaseResource = (event: ReleaseEvent): Modules.AuditLogs.Resource => ({\n type: 'release',\n id: event.releaseId,\n name: event.name,\n });\n\n const entryRef = (event: ReleaseEntryEvent): EntryRef => ({\n contentType: event.contentType,\n documentId: event.entryDocumentId,\n locale: event.locale ?? null,\n });\n\n auditLogsLifecycle.registerEvent<CreateDetails>(\n AUDITED_EVENTS.RELEASE_CREATE,\n (event: ReleaseEvent) => ({\n resource: releaseResource(event),\n details: {\n isScheduled: event.scheduledAt != null,\n ...(event.scheduledAt && { scheduledAt: event.scheduledAt, timezone: event.timezone }),\n },\n })\n );\n\n auditLogsLifecycle.registerEvent<UpdateDetails>(\n AUDITED_EVENTS.RELEASE_UPDATE,\n (event: ReleaseEvent & UpdateDetails) => ({\n resource: releaseResource(event),\n details: { changes: event.changes },\n })\n );\n\n auditLogsLifecycle.registerEvent(AUDITED_EVENTS.RELEASE_DELETE, (event: ReleaseEvent) => ({\n resource: releaseResource(event),\n }));\n\n auditLogsLifecycle.registerEvent<TriggerDetails | TriggerFailureDetails>(\n AUDITED_EVENTS.RELEASE_TRIGGER,\n (event: TriggerEvent) => {\n const base = { resource: releaseResource(event), outcome: event.outcome };\n\n if (event.outcome === 'failure') {\n return {\n ...base,\n details: {\n reason: event.reason?.slice(0, MAX_REASON_LENGTH),\n // Only when the emitter can attribute the counts.\n ...(event.published != null && {\n published: event.published,\n unpublished: event.unpublished,\n failed: event.failed,\n }),\n },\n };\n }\n\n if (event.published != null && event.unpublished != null) {\n return {\n ...base,\n details: { published: event.published, unpublished: event.unpublished, failed: 0 },\n };\n }\n\n return base;\n }\n );\n\n auditLogsLifecycle.registerEvent<EntryAddDetails>(\n AUDITED_EVENTS.RELEASE_ENTRY_ADD,\n (event: ReleaseEntryEvent & { type: 'publish' | 'unpublish' }) => ({\n resource: releaseResource(event),\n details: { entry: entryRef(event), actionType: event.type },\n })\n );\n\n auditLogsLifecycle.registerEvent<EntryUpdateDetails>(\n AUDITED_EVENTS.RELEASE_ENTRY_UPDATE,\n (\n event: ReleaseEntryEvent & {\n from: 'publish' | 'unpublish';\n to: 'publish' | 'unpublish';\n }\n ) => ({\n resource: releaseResource(event),\n details: { entry: entryRef(event), actionType: { before: event.from, after: event.to } },\n })\n );\n\n auditLogsLifecycle.registerEvent<EntryRemoveDetails>(\n AUDITED_EVENTS.RELEASE_ENTRY_REMOVE,\n (event: ReleaseEntryEvent) => ({\n resource: releaseResource(event),\n details: { entry: entryRef(event) },\n })\n );\n\n auditLogsLifecycle.registerEvent<SettingsUpdateDetails>(\n AUDITED_EVENTS.RELEASE_SETTINGS_UPDATE,\n (event: SettingsUpdateDetails) => ({\n resource: { type: 'release' },\n details: { changes: event.changes },\n })\n );\n};\n"],"names":["RELEASE_EDITABLE_FIELDS","MAX_REASON_LENGTH","getReleaseChanges","previous","next","changes","field","hasChanged","before","after","scheduledAt","wasScheduled","isScheduled","emitAudit","strapi","event","payload","eventHub","emit","error","log","emitAuditOnCommit","db","inTransaction","transaction","onCommit","runAsSystem","origin","work","result","requestContext","run","state","auditSource","request","url","registerAuditEvents","auditLogsLifecycle","releaseResource","type","id","releaseId","name","entryRef","contentType","documentId","entryDocumentId","locale","registerEvent","AUDITED_EVENTS","RELEASE_CREATE","resource","details","timezone","RELEASE_UPDATE","RELEASE_DELETE","RELEASE_TRIGGER","base","outcome","reason","slice","published","unpublished","failed","RELEASE_ENTRY_ADD","entry","actionType","RELEASE_ENTRY_UPDATE","from","to","RELEASE_ENTRY_REMOVE","RELEASE_SETTINGS_UPDATE"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"audit-logs.mjs","sources":["../../server/src/audit-logs.ts"],"sourcesContent":["import type { Core, Modules } from '@strapi/types';\nimport { emitAudit } from '@strapi/utils';\n\nimport { AUDITED_EVENTS } from './constants';\n\n/**\n * Transformers for the release audit events.\n * Each builds {resource, details, outcome} from its emitted event.\n */\n\ninterface ReleaseEvent {\n releaseId: number;\n name?: string;\n scheduledAt?: string | null;\n timezone?: string | null;\n}\n\ninterface ReleaseEntryEvent {\n releaseId: number;\n name?: string;\n contentType: string;\n entryDocumentId: string;\n locale: string | null;\n}\n\nexport interface CreateDetails {\n isScheduled: boolean;\n scheduledAt?: string | null;\n timezone?: string | null;\n}\n\nexport const RELEASE_EDITABLE_FIELDS = ['name', 'scheduledAt', 'timezone'] as const;\n\n/** Audit rows are never pruned by size, so the failure reason is capped */\nconst MAX_REASON_LENGTH = 100;\n\ntype ReleaseChangedField = (typeof RELEASE_EDITABLE_FIELDS)[number] | 'isScheduled';\n\nexport interface UpdateDetails {\n changes: Partial<Record<ReleaseChangedField, Modules.AuditLogs.FieldChange>>;\n}\n\n/**\n * Returns the fields that changed in a release update.\n * `isScheduled` is included when the release changes between scheduled and unscheduled.\n */\nexport const getReleaseChanges = (\n previous: { name?: string; scheduledAt?: string | null; timezone?: string | null } | null,\n next: { name?: string; scheduledAt?: string | null; timezone?: string | null }\n) => {\n const changes: Partial<Record<ReleaseChangedField, Modules.AuditLogs.FieldChange>> = {};\n\n for (const field of RELEASE_EDITABLE_FIELDS) {\n const hasChanged = (previous?.[field] ?? null) !== (next[field] ?? null);\n\n if (hasChanged) {\n changes[field] = { before: previous?.[field] ?? null, after: next[field] ?? null };\n }\n\n if (field === 'scheduledAt' && changes.scheduledAt) {\n const wasScheduled = previous?.scheduledAt != null;\n const isScheduled = next.scheduledAt != null;\n\n if (wasScheduled !== isScheduled) {\n changes.isScheduled = { before: wasScheduled, after: isScheduled };\n }\n }\n }\n\n return changes;\n};\n\nexport interface TriggerDetails {\n published: number;\n unpublished: number;\n failed: number;\n}\n\nexport interface TriggerFailureDetails {\n /**\n * Only the error name is stored, capped in length. Error objects may contain\n * sensitive data such as database error details, and a custom error class can put\n * arbitrary text in its name.\n */\n reason: string;\n // A failed run is not atomic, so counts are not reported. Reserved for partial releases.\n published?: number;\n unpublished?: number;\n failed?: number;\n}\n\ntype TriggerEvent = ReleaseEvent &\n (\n | { outcome: 'success'; published?: number; unpublished?: number }\n | {\n outcome: 'failure';\n reason: string;\n published?: number;\n unpublished?: number;\n failed?: number;\n }\n );\n\nexport interface SettingsUpdateDetails {\n changes: Partial<Record<'defaultTimezone', Modules.AuditLogs.FieldChange>>;\n}\n\ninterface EntryRef {\n contentType: string;\n documentId: string;\n locale: string | null;\n}\n\nexport interface EntryAddDetails {\n entry: EntryRef;\n actionType: 'publish' | 'unpublish';\n}\n\nexport interface EntryUpdateDetails {\n entry: EntryRef;\n actionType: { before: 'publish' | 'unpublish'; after: 'publish' | 'unpublish' };\n}\n\nexport interface EntryRemoveDetails {\n entry: EntryRef;\n}\n\n/**\n * Emits an audit event for a write that may be part of a bulk transaction.\n * Inside one, the event waits for its commit, so a rollback leaves no rows for\n * entries that were never added. Standalone, it is emitted right away.\n */\nexport const emitAuditOnCommit = async (\n { strapi }: { strapi: Core.Strapi },\n event: string,\n payload: unknown\n): Promise<void> => {\n if (!strapi.db.inTransaction()) {\n await emitAudit({ strapi }, event, payload);\n return;\n }\n\n await strapi.db.transaction(({ onCommit }) =>\n onCommit(() => emitAudit({ strapi }, event, payload))\n );\n};\n\n/**\n * Runs work as a system principal so events emitted during the operation\n * are recorded with a system actor.\n */\nexport const runAsSystem = async <T>(\n { strapi }: { strapi: Core.Strapi },\n origin: Modules.AuditLogs.SystemOrigin,\n work: () => Promise<T>\n): Promise<T> => {\n let result: T;\n\n await strapi.requestContext.run(\n { state: { auditSource: origin }, request: { url: '' } } as any,\n async () => {\n result = await work();\n }\n );\n\n return result!;\n};\n\n/**\n * The part of the audit-logs lifecycle service used by this plugin.\n * The full service type lives in the Admin EE package and cannot be imported here.\n */\ninterface AuditLogsLifecycle {\n registerEvent<TDetails>(\n name: string,\n transform: Modules.AuditLogs.EventTransformer<TDetails>\n ): void;\n}\n\nexport const registerAuditEvents = (auditLogsLifecycle: AuditLogsLifecycle) => {\n const releaseResource = (event: ReleaseEvent): Modules.AuditLogs.Resource => ({\n type: 'release',\n id: event.releaseId,\n name: event.name,\n });\n\n const entryRef = (event: ReleaseEntryEvent): EntryRef => ({\n contentType: event.contentType,\n documentId: event.entryDocumentId,\n locale: event.locale ?? null,\n });\n\n auditLogsLifecycle.registerEvent<CreateDetails>(\n AUDITED_EVENTS.RELEASE_CREATE,\n (event: ReleaseEvent) => ({\n resource: releaseResource(event),\n details: {\n isScheduled: event.scheduledAt != null,\n ...(event.scheduledAt && { scheduledAt: event.scheduledAt, timezone: event.timezone }),\n },\n })\n );\n\n auditLogsLifecycle.registerEvent<UpdateDetails>(\n AUDITED_EVENTS.RELEASE_UPDATE,\n (event: ReleaseEvent & UpdateDetails) => ({\n resource: releaseResource(event),\n details: { changes: event.changes },\n })\n );\n\n auditLogsLifecycle.registerEvent(AUDITED_EVENTS.RELEASE_DELETE, (event: ReleaseEvent) => ({\n resource: releaseResource(event),\n }));\n\n auditLogsLifecycle.registerEvent<TriggerDetails | TriggerFailureDetails>(\n AUDITED_EVENTS.RELEASE_TRIGGER,\n (event: TriggerEvent) => {\n const base = { resource: releaseResource(event), outcome: event.outcome };\n\n if (event.outcome === 'failure') {\n return {\n ...base,\n details: {\n reason: event.reason?.slice(0, MAX_REASON_LENGTH),\n // Only when the emitter can attribute the counts.\n ...(event.published != null && {\n published: event.published,\n unpublished: event.unpublished,\n failed: event.failed,\n }),\n },\n };\n }\n\n if (event.published != null && event.unpublished != null) {\n return {\n ...base,\n details: { published: event.published, unpublished: event.unpublished, failed: 0 },\n };\n }\n\n return base;\n }\n );\n\n auditLogsLifecycle.registerEvent<EntryAddDetails>(\n AUDITED_EVENTS.RELEASE_ENTRY_ADD,\n (event: ReleaseEntryEvent & { type: 'publish' | 'unpublish' }) => ({\n resource: releaseResource(event),\n details: { entry: entryRef(event), actionType: event.type },\n })\n );\n\n auditLogsLifecycle.registerEvent<EntryUpdateDetails>(\n AUDITED_EVENTS.RELEASE_ENTRY_UPDATE,\n (\n event: ReleaseEntryEvent & {\n from: 'publish' | 'unpublish';\n to: 'publish' | 'unpublish';\n }\n ) => ({\n resource: releaseResource(event),\n details: { entry: entryRef(event), actionType: { before: event.from, after: event.to } },\n })\n );\n\n auditLogsLifecycle.registerEvent<EntryRemoveDetails>(\n AUDITED_EVENTS.RELEASE_ENTRY_REMOVE,\n (event: ReleaseEntryEvent) => ({\n resource: releaseResource(event),\n details: { entry: entryRef(event) },\n })\n );\n\n auditLogsLifecycle.registerEvent<SettingsUpdateDetails>(\n AUDITED_EVENTS.RELEASE_SETTINGS_UPDATE,\n (event: SettingsUpdateDetails) => ({\n resource: { type: 'release' },\n details: { changes: event.changes },\n })\n );\n};\n"],"names":["RELEASE_EDITABLE_FIELDS","MAX_REASON_LENGTH","getReleaseChanges","previous","next","changes","field","hasChanged","before","after","scheduledAt","wasScheduled","isScheduled","emitAuditOnCommit","strapi","event","payload","db","inTransaction","emitAudit","transaction","onCommit","runAsSystem","origin","work","result","requestContext","run","state","auditSource","request","url","registerAuditEvents","auditLogsLifecycle","releaseResource","type","id","releaseId","name","entryRef","contentType","documentId","entryDocumentId","locale","registerEvent","AUDITED_EVENTS","RELEASE_CREATE","resource","details","timezone","RELEASE_UPDATE","RELEASE_DELETE","RELEASE_TRIGGER","base","outcome","reason","slice","published","unpublished","failed","RELEASE_ENTRY_ADD","entry","actionType","RELEASE_ENTRY_UPDATE","from","to","RELEASE_ENTRY_REMOVE","RELEASE_SETTINGS_UPDATE"],"mappings":"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"}
|
|
@@ -66,7 +66,7 @@ const createReleaseService = ({ strapi })=>{
|
|
|
66
66
|
});
|
|
67
67
|
// Audited before scheduling: a scheduling failure shouldn't leave the write
|
|
68
68
|
// unrecorded.
|
|
69
|
-
await
|
|
69
|
+
await utils.emitAudit({
|
|
70
70
|
strapi
|
|
71
71
|
}, constants.AUDITED_EVENTS.RELEASE_CREATE, {
|
|
72
72
|
releaseId: release.id,
|
|
@@ -146,7 +146,7 @@ const createReleaseService = ({ strapi })=>{
|
|
|
146
146
|
if (updatedRelease) {
|
|
147
147
|
const changes = auditLogs.getReleaseChanges(release, updatedRelease);
|
|
148
148
|
if (Object.keys(changes).length > 0) {
|
|
149
|
-
await
|
|
149
|
+
await utils.emitAudit({
|
|
150
150
|
strapi
|
|
151
151
|
}, constants.AUDITED_EVENTS.RELEASE_UPDATE, {
|
|
152
152
|
releaseId: updatedRelease.id,
|
|
@@ -220,7 +220,7 @@ const createReleaseService = ({ strapi })=>{
|
|
|
220
220
|
await schedulingService.cancel(release.id);
|
|
221
221
|
}
|
|
222
222
|
strapi.telemetry.send('didDeleteContentRelease');
|
|
223
|
-
await
|
|
223
|
+
await utils.emitAudit({
|
|
224
224
|
strapi
|
|
225
225
|
}, constants.AUDITED_EVENTS.RELEASE_DELETE, {
|
|
226
226
|
releaseId: release.id,
|
|
@@ -317,7 +317,7 @@ const createReleaseService = ({ strapi })=>{
|
|
|
317
317
|
// The 'failed' status is already committed, so this emit cannot be rolled back.
|
|
318
318
|
// Pre-flight rejections threw before the run and never reach it.
|
|
319
319
|
if (error !== null) {
|
|
320
|
-
await
|
|
320
|
+
await utils.emitAudit({
|
|
321
321
|
strapi
|
|
322
322
|
}, constants.AUDITED_EVENTS.RELEASE_TRIGGER, {
|
|
323
323
|
releaseId: lockedRelease.id,
|
|
@@ -369,7 +369,7 @@ const createReleaseService = ({ strapi })=>{
|
|
|
369
369
|
error: countsError
|
|
370
370
|
});
|
|
371
371
|
}
|
|
372
|
-
await
|
|
372
|
+
await utils.emitAudit({
|
|
373
373
|
strapi
|
|
374
374
|
}, constants.AUDITED_EVENTS.RELEASE_TRIGGER, {
|
|
375
375
|
releaseId: lockedRelease.id,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"release.js","sources":["../../../server/src/services/release.ts"],"sourcesContent":["import { setCreatorFields, errors } from '@strapi/utils';\n\nimport type { Core, Struct, UID, Data } from '@strapi/types';\n\nimport {\n ALLOWED_WEBHOOK_EVENTS,\n AUDITED_EVENTS,\n RELEASE_ACTION_MODEL_UID,\n RELEASE_MODEL_UID,\n} from '../constants';\nimport type {\n GetReleases,\n CreateRelease,\n UpdateRelease,\n PublishRelease,\n GetRelease,\n Release,\n DeleteRelease,\n} from '../../../shared/contracts/releases';\nimport type { ReleaseAction } from '../../../shared/contracts/release-actions';\nimport type { UserInfo } from '../../../shared/types';\nimport { getService, getPublishOrderForContentTypes } from '../utils';\nimport { emitAudit, getReleaseChanges } from '../audit-logs';\n\nconst createReleaseService = ({ strapi }: { strapi: Core.Strapi }) => {\n const dispatchWebhook = (\n event: string,\n { isPublished, release, error }: { isPublished: boolean; release?: any; error?: unknown }\n ) => {\n strapi.eventHub.emit(event, {\n isPublished,\n error,\n release,\n });\n };\n\n /**\n * Given a release id, it returns the actions formatted ready to be used to publish them.\n * We split them by contentType and type (publish/unpublish) and extract only the documentIds and locales.\n */\n const getFormattedActions = async (releaseId: Release['id']) => {\n const actions = (await strapi.db.query(RELEASE_ACTION_MODEL_UID).findMany({\n where: {\n release: {\n id: releaseId,\n },\n },\n })) as ReleaseAction[];\n\n if (actions.length === 0) {\n throw new errors.ValidationError('No entries to publish');\n }\n\n /**\n * We separate publish and unpublish actions, grouping them by contentType and extracting only their documentIds and locales.\n */\n const formattedActions: {\n [key: UID.ContentType]: {\n publish: { documentId: ReleaseAction['entryDocumentId']; locale?: string }[];\n unpublish: { documentId: ReleaseAction['entryDocumentId']; locale?: string }[];\n };\n } = {};\n\n for (const action of actions) {\n const contentTypeUid: UID.ContentType = action.contentType;\n\n if (!formattedActions[contentTypeUid]) {\n formattedActions[contentTypeUid] = {\n publish: [],\n unpublish: [],\n };\n }\n\n formattedActions[contentTypeUid][action.type].push({\n documentId: action.entryDocumentId,\n locale: action.locale,\n });\n }\n\n return formattedActions;\n };\n\n return {\n async create(releaseData: CreateRelease.Request['body'], { user }: { user: UserInfo }) {\n const releaseWithCreatorFields = await setCreatorFields({ user })(releaseData);\n\n const {\n validatePendingReleasesLimit,\n validateUniqueNameForPendingRelease,\n validateScheduledAtIsLaterThanNow,\n } = getService('release-validation', { strapi });\n\n await Promise.all([\n validatePendingReleasesLimit(),\n validateUniqueNameForPendingRelease(releaseWithCreatorFields.name),\n validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt),\n ]);\n\n const release = await strapi.db.query(RELEASE_MODEL_UID).create({\n data: {\n ...releaseWithCreatorFields,\n status: 'empty',\n },\n });\n\n // Audited before scheduling: a scheduling failure shouldn't leave the write\n // unrecorded.\n await emitAudit({ strapi }, AUDITED_EVENTS.RELEASE_CREATE, {\n releaseId: release.id,\n name: release.name,\n ...(release.scheduledAt && {\n scheduledAt: release.scheduledAt,\n timezone: release.timezone,\n }),\n });\n\n if (releaseWithCreatorFields.scheduledAt) {\n const schedulingService = getService('scheduling', { strapi });\n\n await schedulingService.set(release.id, release.scheduledAt);\n }\n\n strapi.telemetry.send('didCreateContentRelease');\n\n return release;\n },\n\n async findOne(id: GetRelease.Request['params']['id'], query = {}) {\n const dbQuery = strapi.get('query-params').transform(RELEASE_MODEL_UID, query);\n const release = await strapi.db.query(RELEASE_MODEL_UID).findOne({\n ...dbQuery,\n where: { id },\n });\n\n return release;\n },\n\n findPage(query?: GetReleases.Request['query']) {\n const dbQuery = strapi.get('query-params').transform(RELEASE_MODEL_UID, query ?? {});\n\n return strapi.db.query(RELEASE_MODEL_UID).findPage({\n ...dbQuery,\n populate: {\n actions: {\n count: true,\n },\n },\n });\n },\n\n findMany(query?: any) {\n const dbQuery = strapi.get('query-params').transform(RELEASE_MODEL_UID, query ?? {});\n\n return strapi.db.query(RELEASE_MODEL_UID).findMany({\n ...dbQuery,\n });\n },\n\n async update(\n id: Data.ID,\n releaseData: UpdateRelease.Request['body'],\n { user }: { user: UserInfo }\n ) {\n const releaseWithCreatorFields = await setCreatorFields({ user, isEdition: true })(\n releaseData\n );\n\n const { validateUniqueNameForPendingRelease, validateScheduledAtIsLaterThanNow } = getService(\n 'release-validation',\n { strapi }\n );\n\n await Promise.all([\n validateUniqueNameForPendingRelease(releaseWithCreatorFields.name, id),\n validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt),\n ]);\n\n const release = await strapi.db.query(RELEASE_MODEL_UID).findOne({ where: { id } });\n\n if (!release) {\n throw new errors.NotFoundError(`No release found for id ${id}`);\n }\n\n if (release.releasedAt) {\n throw new errors.ValidationError('Release already published');\n }\n\n const updatedRelease = await strapi.db.query(RELEASE_MODEL_UID).update({\n where: { id },\n data: releaseWithCreatorFields,\n });\n\n // Audited before scheduling, against the pre-image read next to the write.\n // A write that matched no row (deleted in between) has nothing to report.\n if (updatedRelease) {\n const changes = getReleaseChanges(release, updatedRelease);\n\n if (Object.keys(changes).length > 0) {\n await emitAudit({ strapi }, AUDITED_EVENTS.RELEASE_UPDATE, {\n releaseId: updatedRelease.id,\n name: updatedRelease.name,\n changes,\n });\n }\n }\n\n const schedulingService = getService('scheduling', { strapi });\n\n if (releaseData.scheduledAt) {\n // set function always cancel the previous job if it exists, so we can call it directly\n await schedulingService.set(id, releaseData.scheduledAt);\n } else if (release.scheduledAt) {\n // When user don't send a scheduledAt and we have one on the release, means that user want to unschedule it\n schedulingService.cancel(id);\n }\n\n this.updateReleaseStatus(id);\n\n strapi.telemetry.send('didUpdateContentRelease');\n\n return updatedRelease;\n },\n\n async getAllComponents() {\n const contentManagerComponentsService = strapi\n .plugin('content-manager')\n .service('components');\n\n const components = await contentManagerComponentsService.findAllComponents();\n\n const componentsMap = components.reduce(\n (\n acc: { [key: Struct.ComponentSchema['uid']]: Struct.ComponentSchema },\n component: Struct.ComponentSchema\n ) => {\n acc[component.uid] = component;\n\n return acc;\n },\n {}\n );\n\n return componentsMap;\n },\n\n async delete(releaseId: DeleteRelease.Request['params']['id']) {\n const release: Release = await strapi.db.query(RELEASE_MODEL_UID).findOne({\n where: { id: releaseId },\n populate: {\n actions: {\n select: ['id'],\n },\n },\n });\n\n if (!release) {\n throw new errors.NotFoundError(`No release found for id ${releaseId}`);\n }\n\n if (release.releasedAt) {\n throw new errors.ValidationError('Release already published');\n }\n\n // Only delete the release and its actions is you in fact can delete all the actions and the release\n // Otherwise, if the transaction fails it throws an error\n await strapi.db.transaction(async () => {\n await strapi.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({\n where: {\n id: {\n $in: release.actions.map((action) => action.id),\n },\n },\n });\n\n await strapi.db.query(RELEASE_MODEL_UID).delete({\n where: {\n id: releaseId,\n },\n });\n });\n\n if (release.scheduledAt) {\n const schedulingService = getService('scheduling', { strapi });\n await schedulingService.cancel(release.id);\n }\n\n strapi.telemetry.send('didDeleteContentRelease');\n\n await emitAudit({ strapi }, AUDITED_EVENTS.RELEASE_DELETE, {\n releaseId: release.id,\n name: release.name,\n });\n\n return release;\n },\n\n async publish(releaseId: PublishRelease.Request['params']['id']) {\n const {\n release,\n error,\n lockedRelease,\n }: {\n release: Pick<Release, 'id' | 'releasedAt' | 'status'> | null;\n error: unknown;\n lockedRelease: Pick<Release, 'id' | 'name'>;\n } = await strapi.db.transaction(async ({ trx }) => {\n /**\n * We lock the release in this transaction, so any other process trying to publish it will wait until this transaction is finished\n * In this transaction we don't care about rollback, becasue we want to persist the lock until the end and if it fails we want to change the release status to failed\n */\n const lockedRelease = (await strapi.db\n ?.queryBuilder(RELEASE_MODEL_UID)\n .where({ id: releaseId })\n .select(['id', 'name', 'releasedAt', 'status'])\n .first()\n .transacting(trx)\n .forUpdate()\n .execute()) as Pick<Release, 'id' | 'name' | 'releasedAt' | 'status'> | undefined;\n\n if (!lockedRelease) {\n throw new errors.NotFoundError(`No release found for id ${releaseId}`);\n }\n\n if (lockedRelease.releasedAt) {\n throw new errors.ValidationError('Release already published');\n }\n\n if (lockedRelease.status === 'failed') {\n throw new errors.ValidationError('Release failed to publish');\n }\n\n try {\n strapi.log.info(`[Content Releases] Starting to publish release ${lockedRelease.name}`);\n\n const formattedActions = await getFormattedActions(releaseId);\n\n // Publish content types in dependency order so that when entity A has a relation\n // to entity B, B is published first to keep this relation.\n const contentTypeUids = getPublishOrderForContentTypes(\n Object.keys(formattedActions) as UID.ContentType[],\n { strapi }\n );\n\n await strapi.db.transaction(async () => {\n for (const contentTypeUid of contentTypeUids) {\n const contentType = contentTypeUid as UID.ContentType;\n const { publish, unpublish } = formattedActions[contentType];\n\n // Serialize within a content type: concurrent publishes of related documents\n // can race on shared join-table state (notably self-referential relations) and\n // leave inconsistent FK rows when one branch deletes a row another branch is\n // about to reference.\n for (const params of publish) {\n await strapi.documents(contentType).publish(params);\n }\n\n for (const params of unpublish) {\n await strapi.documents(contentType).unpublish(params);\n }\n }\n });\n\n const release = await strapi.db.query(RELEASE_MODEL_UID).update({\n where: {\n id: releaseId,\n },\n data: {\n status: 'done',\n releasedAt: new Date(),\n },\n });\n\n dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {\n isPublished: true,\n release,\n });\n\n strapi.telemetry.send('didPublishContentRelease');\n\n return { release, error: null, lockedRelease };\n } catch (error) {\n dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {\n isPublished: false,\n error,\n });\n\n // We need to run the update in the same transaction because the release is locked\n await strapi.db\n ?.queryBuilder(RELEASE_MODEL_UID)\n .where({ id: releaseId })\n .update({\n status: 'failed',\n })\n .transacting(trx)\n .execute();\n\n // At this point, we don't want to throw the error because if that happen we rollback the change in the release status\n // We want to throw the error after the transaction is finished, so we return the error\n return {\n release: null,\n // A rejection can carry any value, even null: never mistake it for success\n error: error ?? new Error('Release publish failed with an empty error'),\n lockedRelease,\n };\n }\n });\n\n // The 'failed' status is already committed, so this emit cannot be rolled back.\n // Pre-flight rejections threw before the run and never reach it.\n if (error !== null) {\n await emitAudit({ strapi }, AUDITED_EVENTS.RELEASE_TRIGGER, {\n releaseId: lockedRelease.id,\n name: lockedRelease.name,\n outcome: 'failure',\n // The error's name and nothing else from it: driver errors can carry row\n // contents in their properties\n reason: error instanceof Error ? error.name : 'Error',\n });\n\n // Swallowing a non-Error throwable would report the publish as successful\n if (error instanceof Error) {\n throw error;\n }\n throw new Error('Release publish failed', { cause: error });\n }\n\n // Counted after the publish, while the actions still exist.\n // A count failure doesn't fail the committed publish: the error is returned.\n const releaseActionService = getService('release-action', { strapi });\n let counts = null;\n let countsError: Error | null = null;\n\n try {\n const [published, unpublished] = await Promise.all([\n releaseActionService.countActions({ filters: { release: releaseId, type: 'publish' } }),\n releaseActionService.countActions({\n filters: { release: releaseId, type: 'unpublish' },\n }),\n ]);\n counts = { published, unpublished };\n } catch (err) {\n countsError = err instanceof Error ? err : new Error(String(err), { cause: err });\n strapi.log.error(\n `Failed to count the entries for the release.trigger entry of release ${releaseId}`,\n { error: countsError }\n );\n }\n\n await emitAudit({ strapi }, AUDITED_EVENTS.RELEASE_TRIGGER, {\n releaseId: lockedRelease.id,\n name: lockedRelease.name,\n outcome: 'success',\n published: counts?.published,\n unpublished: counts?.unpublished,\n });\n\n return { release, counts, countsError };\n },\n\n async updateReleaseStatus(releaseId: Release['id']) {\n const releaseActionService = getService('release-action', { strapi });\n\n const [totalActions, invalidActions] = await Promise.all([\n releaseActionService.countActions({\n filters: {\n release: releaseId,\n },\n }),\n releaseActionService.countActions({\n filters: {\n release: releaseId,\n isEntryValid: false,\n },\n }),\n ]);\n\n if (totalActions > 0) {\n if (invalidActions > 0) {\n return strapi.db.query(RELEASE_MODEL_UID).update({\n where: {\n id: releaseId,\n },\n data: {\n status: 'blocked',\n },\n });\n }\n\n return strapi.db.query(RELEASE_MODEL_UID).update({\n where: {\n id: releaseId,\n },\n data: {\n status: 'ready',\n },\n });\n }\n\n return strapi.db.query(RELEASE_MODEL_UID).update({\n where: {\n id: releaseId,\n },\n data: {\n status: 'empty',\n },\n });\n },\n };\n};\n\nexport type ReleaseService = ReturnType<typeof createReleaseService>;\n\nexport default createReleaseService;\n"],"names":["createReleaseService","strapi","dispatchWebhook","event","isPublished","release","error","eventHub","emit","getFormattedActions","releaseId","actions","db","query","RELEASE_ACTION_MODEL_UID","findMany","where","id","length","errors","ValidationError","formattedActions","action","contentTypeUid","contentType","publish","unpublish","type","push","documentId","entryDocumentId","locale","create","releaseData","user","releaseWithCreatorFields","setCreatorFields","validatePendingReleasesLimit","validateUniqueNameForPendingRelease","validateScheduledAtIsLaterThanNow","getService","Promise","all","name","scheduledAt","RELEASE_MODEL_UID","data","status","emitAudit","AUDITED_EVENTS","RELEASE_CREATE","timezone","schedulingService","set","telemetry","send","findOne","dbQuery","get","transform","findPage","populate","count","update","isEdition","NotFoundError","releasedAt","updatedRelease","changes","getReleaseChanges","Object","keys","RELEASE_UPDATE","cancel","updateReleaseStatus","getAllComponents","contentManagerComponentsService","plugin","service","components","findAllComponents","componentsMap","reduce","acc","component","uid","delete","select","transaction","deleteMany","$in","map","RELEASE_DELETE","lockedRelease","trx","queryBuilder","first","transacting","forUpdate","execute","log","info","contentTypeUids","getPublishOrderForContentTypes","params","documents","Date","ALLOWED_WEBHOOK_EVENTS","RELEASES_PUBLISH","Error","RELEASE_TRIGGER","outcome","reason","cause","releaseActionService","counts","countsError","published","unpublished","countActions","filters","err","String","totalActions","invalidActions","isEntryValid"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"release.js","sources":["../../../server/src/services/release.ts"],"sourcesContent":["import { setCreatorFields, errors, emitAudit } from '@strapi/utils';\n\nimport type { Core, Struct, UID, Data } from '@strapi/types';\n\nimport {\n ALLOWED_WEBHOOK_EVENTS,\n AUDITED_EVENTS,\n RELEASE_ACTION_MODEL_UID,\n RELEASE_MODEL_UID,\n} from '../constants';\nimport type {\n GetReleases,\n CreateRelease,\n UpdateRelease,\n PublishRelease,\n GetRelease,\n Release,\n DeleteRelease,\n} from '../../../shared/contracts/releases';\nimport type { ReleaseAction } from '../../../shared/contracts/release-actions';\nimport type { UserInfo } from '../../../shared/types';\nimport { getService, getPublishOrderForContentTypes } from '../utils';\nimport { getReleaseChanges } from '../audit-logs';\n\nconst createReleaseService = ({ strapi }: { strapi: Core.Strapi }) => {\n const dispatchWebhook = (\n event: string,\n { isPublished, release, error }: { isPublished: boolean; release?: any; error?: unknown }\n ) => {\n strapi.eventHub.emit(event, {\n isPublished,\n error,\n release,\n });\n };\n\n /**\n * Given a release id, it returns the actions formatted ready to be used to publish them.\n * We split them by contentType and type (publish/unpublish) and extract only the documentIds and locales.\n */\n const getFormattedActions = async (releaseId: Release['id']) => {\n const actions = (await strapi.db.query(RELEASE_ACTION_MODEL_UID).findMany({\n where: {\n release: {\n id: releaseId,\n },\n },\n })) as ReleaseAction[];\n\n if (actions.length === 0) {\n throw new errors.ValidationError('No entries to publish');\n }\n\n /**\n * We separate publish and unpublish actions, grouping them by contentType and extracting only their documentIds and locales.\n */\n const formattedActions: {\n [key: UID.ContentType]: {\n publish: { documentId: ReleaseAction['entryDocumentId']; locale?: string }[];\n unpublish: { documentId: ReleaseAction['entryDocumentId']; locale?: string }[];\n };\n } = {};\n\n for (const action of actions) {\n const contentTypeUid: UID.ContentType = action.contentType;\n\n if (!formattedActions[contentTypeUid]) {\n formattedActions[contentTypeUid] = {\n publish: [],\n unpublish: [],\n };\n }\n\n formattedActions[contentTypeUid][action.type].push({\n documentId: action.entryDocumentId,\n locale: action.locale,\n });\n }\n\n return formattedActions;\n };\n\n return {\n async create(releaseData: CreateRelease.Request['body'], { user }: { user: UserInfo }) {\n const releaseWithCreatorFields = await setCreatorFields({ user })(releaseData);\n\n const {\n validatePendingReleasesLimit,\n validateUniqueNameForPendingRelease,\n validateScheduledAtIsLaterThanNow,\n } = getService('release-validation', { strapi });\n\n await Promise.all([\n validatePendingReleasesLimit(),\n validateUniqueNameForPendingRelease(releaseWithCreatorFields.name),\n validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt),\n ]);\n\n const release = await strapi.db.query(RELEASE_MODEL_UID).create({\n data: {\n ...releaseWithCreatorFields,\n status: 'empty',\n },\n });\n\n // Audited before scheduling: a scheduling failure shouldn't leave the write\n // unrecorded.\n await emitAudit({ strapi }, AUDITED_EVENTS.RELEASE_CREATE, {\n releaseId: release.id,\n name: release.name,\n ...(release.scheduledAt && {\n scheduledAt: release.scheduledAt,\n timezone: release.timezone,\n }),\n });\n\n if (releaseWithCreatorFields.scheduledAt) {\n const schedulingService = getService('scheduling', { strapi });\n\n await schedulingService.set(release.id, release.scheduledAt);\n }\n\n strapi.telemetry.send('didCreateContentRelease');\n\n return release;\n },\n\n async findOne(id: GetRelease.Request['params']['id'], query = {}) {\n const dbQuery = strapi.get('query-params').transform(RELEASE_MODEL_UID, query);\n const release = await strapi.db.query(RELEASE_MODEL_UID).findOne({\n ...dbQuery,\n where: { id },\n });\n\n return release;\n },\n\n findPage(query?: GetReleases.Request['query']) {\n const dbQuery = strapi.get('query-params').transform(RELEASE_MODEL_UID, query ?? {});\n\n return strapi.db.query(RELEASE_MODEL_UID).findPage({\n ...dbQuery,\n populate: {\n actions: {\n count: true,\n },\n },\n });\n },\n\n findMany(query?: any) {\n const dbQuery = strapi.get('query-params').transform(RELEASE_MODEL_UID, query ?? {});\n\n return strapi.db.query(RELEASE_MODEL_UID).findMany({\n ...dbQuery,\n });\n },\n\n async update(\n id: Data.ID,\n releaseData: UpdateRelease.Request['body'],\n { user }: { user: UserInfo }\n ) {\n const releaseWithCreatorFields = await setCreatorFields({ user, isEdition: true })(\n releaseData\n );\n\n const { validateUniqueNameForPendingRelease, validateScheduledAtIsLaterThanNow } = getService(\n 'release-validation',\n { strapi }\n );\n\n await Promise.all([\n validateUniqueNameForPendingRelease(releaseWithCreatorFields.name, id),\n validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt),\n ]);\n\n const release = await strapi.db.query(RELEASE_MODEL_UID).findOne({ where: { id } });\n\n if (!release) {\n throw new errors.NotFoundError(`No release found for id ${id}`);\n }\n\n if (release.releasedAt) {\n throw new errors.ValidationError('Release already published');\n }\n\n const updatedRelease = await strapi.db.query(RELEASE_MODEL_UID).update({\n where: { id },\n data: releaseWithCreatorFields,\n });\n\n // Audited before scheduling, against the pre-image read next to the write.\n // A write that matched no row (deleted in between) has nothing to report.\n if (updatedRelease) {\n const changes = getReleaseChanges(release, updatedRelease);\n\n if (Object.keys(changes).length > 0) {\n await emitAudit({ strapi }, AUDITED_EVENTS.RELEASE_UPDATE, {\n releaseId: updatedRelease.id,\n name: updatedRelease.name,\n changes,\n });\n }\n }\n\n const schedulingService = getService('scheduling', { strapi });\n\n if (releaseData.scheduledAt) {\n // set function always cancel the previous job if it exists, so we can call it directly\n await schedulingService.set(id, releaseData.scheduledAt);\n } else if (release.scheduledAt) {\n // When user don't send a scheduledAt and we have one on the release, means that user want to unschedule it\n schedulingService.cancel(id);\n }\n\n this.updateReleaseStatus(id);\n\n strapi.telemetry.send('didUpdateContentRelease');\n\n return updatedRelease;\n },\n\n async getAllComponents() {\n const contentManagerComponentsService = strapi\n .plugin('content-manager')\n .service('components');\n\n const components = await contentManagerComponentsService.findAllComponents();\n\n const componentsMap = components.reduce(\n (\n acc: { [key: Struct.ComponentSchema['uid']]: Struct.ComponentSchema },\n component: Struct.ComponentSchema\n ) => {\n acc[component.uid] = component;\n\n return acc;\n },\n {}\n );\n\n return componentsMap;\n },\n\n async delete(releaseId: DeleteRelease.Request['params']['id']) {\n const release: Release = await strapi.db.query(RELEASE_MODEL_UID).findOne({\n where: { id: releaseId },\n populate: {\n actions: {\n select: ['id'],\n },\n },\n });\n\n if (!release) {\n throw new errors.NotFoundError(`No release found for id ${releaseId}`);\n }\n\n if (release.releasedAt) {\n throw new errors.ValidationError('Release already published');\n }\n\n // Only delete the release and its actions is you in fact can delete all the actions and the release\n // Otherwise, if the transaction fails it throws an error\n await strapi.db.transaction(async () => {\n await strapi.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({\n where: {\n id: {\n $in: release.actions.map((action) => action.id),\n },\n },\n });\n\n await strapi.db.query(RELEASE_MODEL_UID).delete({\n where: {\n id: releaseId,\n },\n });\n });\n\n if (release.scheduledAt) {\n const schedulingService = getService('scheduling', { strapi });\n await schedulingService.cancel(release.id);\n }\n\n strapi.telemetry.send('didDeleteContentRelease');\n\n await emitAudit({ strapi }, AUDITED_EVENTS.RELEASE_DELETE, {\n releaseId: release.id,\n name: release.name,\n });\n\n return release;\n },\n\n async publish(releaseId: PublishRelease.Request['params']['id']) {\n const {\n release,\n error,\n lockedRelease,\n }: {\n release: Pick<Release, 'id' | 'releasedAt' | 'status'> | null;\n error: unknown;\n lockedRelease: Pick<Release, 'id' | 'name'>;\n } = await strapi.db.transaction(async ({ trx }) => {\n /**\n * We lock the release in this transaction, so any other process trying to publish it will wait until this transaction is finished\n * In this transaction we don't care about rollback, becasue we want to persist the lock until the end and if it fails we want to change the release status to failed\n */\n const lockedRelease = (await strapi.db\n ?.queryBuilder(RELEASE_MODEL_UID)\n .where({ id: releaseId })\n .select(['id', 'name', 'releasedAt', 'status'])\n .first()\n .transacting(trx)\n .forUpdate()\n .execute()) as Pick<Release, 'id' | 'name' | 'releasedAt' | 'status'> | undefined;\n\n if (!lockedRelease) {\n throw new errors.NotFoundError(`No release found for id ${releaseId}`);\n }\n\n if (lockedRelease.releasedAt) {\n throw new errors.ValidationError('Release already published');\n }\n\n if (lockedRelease.status === 'failed') {\n throw new errors.ValidationError('Release failed to publish');\n }\n\n try {\n strapi.log.info(`[Content Releases] Starting to publish release ${lockedRelease.name}`);\n\n const formattedActions = await getFormattedActions(releaseId);\n\n // Publish content types in dependency order so that when entity A has a relation\n // to entity B, B is published first to keep this relation.\n const contentTypeUids = getPublishOrderForContentTypes(\n Object.keys(formattedActions) as UID.ContentType[],\n { strapi }\n );\n\n await strapi.db.transaction(async () => {\n for (const contentTypeUid of contentTypeUids) {\n const contentType = contentTypeUid as UID.ContentType;\n const { publish, unpublish } = formattedActions[contentType];\n\n // Serialize within a content type: concurrent publishes of related documents\n // can race on shared join-table state (notably self-referential relations) and\n // leave inconsistent FK rows when one branch deletes a row another branch is\n // about to reference.\n for (const params of publish) {\n await strapi.documents(contentType).publish(params);\n }\n\n for (const params of unpublish) {\n await strapi.documents(contentType).unpublish(params);\n }\n }\n });\n\n const release = await strapi.db.query(RELEASE_MODEL_UID).update({\n where: {\n id: releaseId,\n },\n data: {\n status: 'done',\n releasedAt: new Date(),\n },\n });\n\n dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {\n isPublished: true,\n release,\n });\n\n strapi.telemetry.send('didPublishContentRelease');\n\n return { release, error: null, lockedRelease };\n } catch (error) {\n dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {\n isPublished: false,\n error,\n });\n\n // We need to run the update in the same transaction because the release is locked\n await strapi.db\n ?.queryBuilder(RELEASE_MODEL_UID)\n .where({ id: releaseId })\n .update({\n status: 'failed',\n })\n .transacting(trx)\n .execute();\n\n // At this point, we don't want to throw the error because if that happen we rollback the change in the release status\n // We want to throw the error after the transaction is finished, so we return the error\n return {\n release: null,\n // A rejection can carry any value, even null: never mistake it for success\n error: error ?? new Error('Release publish failed with an empty error'),\n lockedRelease,\n };\n }\n });\n\n // The 'failed' status is already committed, so this emit cannot be rolled back.\n // Pre-flight rejections threw before the run and never reach it.\n if (error !== null) {\n await emitAudit({ strapi }, AUDITED_EVENTS.RELEASE_TRIGGER, {\n releaseId: lockedRelease.id,\n name: lockedRelease.name,\n outcome: 'failure',\n // The error's name and nothing else from it: driver errors can carry row\n // contents in their properties\n reason: error instanceof Error ? error.name : 'Error',\n });\n\n // Swallowing a non-Error throwable would report the publish as successful\n if (error instanceof Error) {\n throw error;\n }\n throw new Error('Release publish failed', { cause: error });\n }\n\n // Counted after the publish, while the actions still exist.\n // A count failure doesn't fail the committed publish: the error is returned.\n const releaseActionService = getService('release-action', { strapi });\n let counts = null;\n let countsError: Error | null = null;\n\n try {\n const [published, unpublished] = await Promise.all([\n releaseActionService.countActions({ filters: { release: releaseId, type: 'publish' } }),\n releaseActionService.countActions({\n filters: { release: releaseId, type: 'unpublish' },\n }),\n ]);\n counts = { published, unpublished };\n } catch (err) {\n countsError = err instanceof Error ? err : new Error(String(err), { cause: err });\n strapi.log.error(\n `Failed to count the entries for the release.trigger entry of release ${releaseId}`,\n { error: countsError }\n );\n }\n\n await emitAudit({ strapi }, AUDITED_EVENTS.RELEASE_TRIGGER, {\n releaseId: lockedRelease.id,\n name: lockedRelease.name,\n outcome: 'success',\n published: counts?.published,\n unpublished: counts?.unpublished,\n });\n\n return { release, counts, countsError };\n },\n\n async updateReleaseStatus(releaseId: Release['id']) {\n const releaseActionService = getService('release-action', { strapi });\n\n const [totalActions, invalidActions] = await Promise.all([\n releaseActionService.countActions({\n filters: {\n release: releaseId,\n },\n }),\n releaseActionService.countActions({\n filters: {\n release: releaseId,\n isEntryValid: false,\n },\n }),\n ]);\n\n if (totalActions > 0) {\n if (invalidActions > 0) {\n return strapi.db.query(RELEASE_MODEL_UID).update({\n where: {\n id: releaseId,\n },\n data: {\n status: 'blocked',\n },\n });\n }\n\n return strapi.db.query(RELEASE_MODEL_UID).update({\n where: {\n id: releaseId,\n },\n data: {\n status: 'ready',\n },\n });\n }\n\n return strapi.db.query(RELEASE_MODEL_UID).update({\n where: {\n id: releaseId,\n },\n data: {\n status: 'empty',\n },\n });\n },\n };\n};\n\nexport type ReleaseService = ReturnType<typeof createReleaseService>;\n\nexport default createReleaseService;\n"],"names":["createReleaseService","strapi","dispatchWebhook","event","isPublished","release","error","eventHub","emit","getFormattedActions","releaseId","actions","db","query","RELEASE_ACTION_MODEL_UID","findMany","where","id","length","errors","ValidationError","formattedActions","action","contentTypeUid","contentType","publish","unpublish","type","push","documentId","entryDocumentId","locale","create","releaseData","user","releaseWithCreatorFields","setCreatorFields","validatePendingReleasesLimit","validateUniqueNameForPendingRelease","validateScheduledAtIsLaterThanNow","getService","Promise","all","name","scheduledAt","RELEASE_MODEL_UID","data","status","emitAudit","AUDITED_EVENTS","RELEASE_CREATE","timezone","schedulingService","set","telemetry","send","findOne","dbQuery","get","transform","findPage","populate","count","update","isEdition","NotFoundError","releasedAt","updatedRelease","changes","getReleaseChanges","Object","keys","RELEASE_UPDATE","cancel","updateReleaseStatus","getAllComponents","contentManagerComponentsService","plugin","service","components","findAllComponents","componentsMap","reduce","acc","component","uid","delete","select","transaction","deleteMany","$in","map","RELEASE_DELETE","lockedRelease","trx","queryBuilder","first","transacting","forUpdate","execute","log","info","contentTypeUids","getPublishOrderForContentTypes","params","documents","Date","ALLOWED_WEBHOOK_EVENTS","RELEASES_PUBLISH","Error","RELEASE_TRIGGER","outcome","reason","cause","releaseActionService","counts","countsError","published","unpublished","countActions","filters","err","String","totalActions","invalidActions","isEntryValid"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { errors, setCreatorFields } from '@strapi/utils';
|
|
1
|
+
import { errors, emitAudit, setCreatorFields } from '@strapi/utils';
|
|
2
2
|
import { RELEASE_MODEL_UID, ALLOWED_WEBHOOK_EVENTS, AUDITED_EVENTS, RELEASE_ACTION_MODEL_UID } from '../constants.mjs';
|
|
3
3
|
import { getService, getPublishOrderForContentTypes } from '../utils/index.mjs';
|
|
4
|
-
import {
|
|
4
|
+
import { getReleaseChanges } from '../audit-logs.mjs';
|
|
5
5
|
|
|
6
6
|
const createReleaseService = ({ strapi })=>{
|
|
7
7
|
const dispatchWebhook = (event, { isPublished, release, error })=>{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"release.mjs","sources":["../../../server/src/services/release.ts"],"sourcesContent":["import { setCreatorFields, errors } from '@strapi/utils';\n\nimport type { Core, Struct, UID, Data } from '@strapi/types';\n\nimport {\n ALLOWED_WEBHOOK_EVENTS,\n AUDITED_EVENTS,\n RELEASE_ACTION_MODEL_UID,\n RELEASE_MODEL_UID,\n} from '../constants';\nimport type {\n GetReleases,\n CreateRelease,\n UpdateRelease,\n PublishRelease,\n GetRelease,\n Release,\n DeleteRelease,\n} from '../../../shared/contracts/releases';\nimport type { ReleaseAction } from '../../../shared/contracts/release-actions';\nimport type { UserInfo } from '../../../shared/types';\nimport { getService, getPublishOrderForContentTypes } from '../utils';\nimport { emitAudit, getReleaseChanges } from '../audit-logs';\n\nconst createReleaseService = ({ strapi }: { strapi: Core.Strapi }) => {\n const dispatchWebhook = (\n event: string,\n { isPublished, release, error }: { isPublished: boolean; release?: any; error?: unknown }\n ) => {\n strapi.eventHub.emit(event, {\n isPublished,\n error,\n release,\n });\n };\n\n /**\n * Given a release id, it returns the actions formatted ready to be used to publish them.\n * We split them by contentType and type (publish/unpublish) and extract only the documentIds and locales.\n */\n const getFormattedActions = async (releaseId: Release['id']) => {\n const actions = (await strapi.db.query(RELEASE_ACTION_MODEL_UID).findMany({\n where: {\n release: {\n id: releaseId,\n },\n },\n })) as ReleaseAction[];\n\n if (actions.length === 0) {\n throw new errors.ValidationError('No entries to publish');\n }\n\n /**\n * We separate publish and unpublish actions, grouping them by contentType and extracting only their documentIds and locales.\n */\n const formattedActions: {\n [key: UID.ContentType]: {\n publish: { documentId: ReleaseAction['entryDocumentId']; locale?: string }[];\n unpublish: { documentId: ReleaseAction['entryDocumentId']; locale?: string }[];\n };\n } = {};\n\n for (const action of actions) {\n const contentTypeUid: UID.ContentType = action.contentType;\n\n if (!formattedActions[contentTypeUid]) {\n formattedActions[contentTypeUid] = {\n publish: [],\n unpublish: [],\n };\n }\n\n formattedActions[contentTypeUid][action.type].push({\n documentId: action.entryDocumentId,\n locale: action.locale,\n });\n }\n\n return formattedActions;\n };\n\n return {\n async create(releaseData: CreateRelease.Request['body'], { user }: { user: UserInfo }) {\n const releaseWithCreatorFields = await setCreatorFields({ user })(releaseData);\n\n const {\n validatePendingReleasesLimit,\n validateUniqueNameForPendingRelease,\n validateScheduledAtIsLaterThanNow,\n } = getService('release-validation', { strapi });\n\n await Promise.all([\n validatePendingReleasesLimit(),\n validateUniqueNameForPendingRelease(releaseWithCreatorFields.name),\n validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt),\n ]);\n\n const release = await strapi.db.query(RELEASE_MODEL_UID).create({\n data: {\n ...releaseWithCreatorFields,\n status: 'empty',\n },\n });\n\n // Audited before scheduling: a scheduling failure shouldn't leave the write\n // unrecorded.\n await emitAudit({ strapi }, AUDITED_EVENTS.RELEASE_CREATE, {\n releaseId: release.id,\n name: release.name,\n ...(release.scheduledAt && {\n scheduledAt: release.scheduledAt,\n timezone: release.timezone,\n }),\n });\n\n if (releaseWithCreatorFields.scheduledAt) {\n const schedulingService = getService('scheduling', { strapi });\n\n await schedulingService.set(release.id, release.scheduledAt);\n }\n\n strapi.telemetry.send('didCreateContentRelease');\n\n return release;\n },\n\n async findOne(id: GetRelease.Request['params']['id'], query = {}) {\n const dbQuery = strapi.get('query-params').transform(RELEASE_MODEL_UID, query);\n const release = await strapi.db.query(RELEASE_MODEL_UID).findOne({\n ...dbQuery,\n where: { id },\n });\n\n return release;\n },\n\n findPage(query?: GetReleases.Request['query']) {\n const dbQuery = strapi.get('query-params').transform(RELEASE_MODEL_UID, query ?? {});\n\n return strapi.db.query(RELEASE_MODEL_UID).findPage({\n ...dbQuery,\n populate: {\n actions: {\n count: true,\n },\n },\n });\n },\n\n findMany(query?: any) {\n const dbQuery = strapi.get('query-params').transform(RELEASE_MODEL_UID, query ?? {});\n\n return strapi.db.query(RELEASE_MODEL_UID).findMany({\n ...dbQuery,\n });\n },\n\n async update(\n id: Data.ID,\n releaseData: UpdateRelease.Request['body'],\n { user }: { user: UserInfo }\n ) {\n const releaseWithCreatorFields = await setCreatorFields({ user, isEdition: true })(\n releaseData\n );\n\n const { validateUniqueNameForPendingRelease, validateScheduledAtIsLaterThanNow } = getService(\n 'release-validation',\n { strapi }\n );\n\n await Promise.all([\n validateUniqueNameForPendingRelease(releaseWithCreatorFields.name, id),\n validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt),\n ]);\n\n const release = await strapi.db.query(RELEASE_MODEL_UID).findOne({ where: { id } });\n\n if (!release) {\n throw new errors.NotFoundError(`No release found for id ${id}`);\n }\n\n if (release.releasedAt) {\n throw new errors.ValidationError('Release already published');\n }\n\n const updatedRelease = await strapi.db.query(RELEASE_MODEL_UID).update({\n where: { id },\n data: releaseWithCreatorFields,\n });\n\n // Audited before scheduling, against the pre-image read next to the write.\n // A write that matched no row (deleted in between) has nothing to report.\n if (updatedRelease) {\n const changes = getReleaseChanges(release, updatedRelease);\n\n if (Object.keys(changes).length > 0) {\n await emitAudit({ strapi }, AUDITED_EVENTS.RELEASE_UPDATE, {\n releaseId: updatedRelease.id,\n name: updatedRelease.name,\n changes,\n });\n }\n }\n\n const schedulingService = getService('scheduling', { strapi });\n\n if (releaseData.scheduledAt) {\n // set function always cancel the previous job if it exists, so we can call it directly\n await schedulingService.set(id, releaseData.scheduledAt);\n } else if (release.scheduledAt) {\n // When user don't send a scheduledAt and we have one on the release, means that user want to unschedule it\n schedulingService.cancel(id);\n }\n\n this.updateReleaseStatus(id);\n\n strapi.telemetry.send('didUpdateContentRelease');\n\n return updatedRelease;\n },\n\n async getAllComponents() {\n const contentManagerComponentsService = strapi\n .plugin('content-manager')\n .service('components');\n\n const components = await contentManagerComponentsService.findAllComponents();\n\n const componentsMap = components.reduce(\n (\n acc: { [key: Struct.ComponentSchema['uid']]: Struct.ComponentSchema },\n component: Struct.ComponentSchema\n ) => {\n acc[component.uid] = component;\n\n return acc;\n },\n {}\n );\n\n return componentsMap;\n },\n\n async delete(releaseId: DeleteRelease.Request['params']['id']) {\n const release: Release = await strapi.db.query(RELEASE_MODEL_UID).findOne({\n where: { id: releaseId },\n populate: {\n actions: {\n select: ['id'],\n },\n },\n });\n\n if (!release) {\n throw new errors.NotFoundError(`No release found for id ${releaseId}`);\n }\n\n if (release.releasedAt) {\n throw new errors.ValidationError('Release already published');\n }\n\n // Only delete the release and its actions is you in fact can delete all the actions and the release\n // Otherwise, if the transaction fails it throws an error\n await strapi.db.transaction(async () => {\n await strapi.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({\n where: {\n id: {\n $in: release.actions.map((action) => action.id),\n },\n },\n });\n\n await strapi.db.query(RELEASE_MODEL_UID).delete({\n where: {\n id: releaseId,\n },\n });\n });\n\n if (release.scheduledAt) {\n const schedulingService = getService('scheduling', { strapi });\n await schedulingService.cancel(release.id);\n }\n\n strapi.telemetry.send('didDeleteContentRelease');\n\n await emitAudit({ strapi }, AUDITED_EVENTS.RELEASE_DELETE, {\n releaseId: release.id,\n name: release.name,\n });\n\n return release;\n },\n\n async publish(releaseId: PublishRelease.Request['params']['id']) {\n const {\n release,\n error,\n lockedRelease,\n }: {\n release: Pick<Release, 'id' | 'releasedAt' | 'status'> | null;\n error: unknown;\n lockedRelease: Pick<Release, 'id' | 'name'>;\n } = await strapi.db.transaction(async ({ trx }) => {\n /**\n * We lock the release in this transaction, so any other process trying to publish it will wait until this transaction is finished\n * In this transaction we don't care about rollback, becasue we want to persist the lock until the end and if it fails we want to change the release status to failed\n */\n const lockedRelease = (await strapi.db\n ?.queryBuilder(RELEASE_MODEL_UID)\n .where({ id: releaseId })\n .select(['id', 'name', 'releasedAt', 'status'])\n .first()\n .transacting(trx)\n .forUpdate()\n .execute()) as Pick<Release, 'id' | 'name' | 'releasedAt' | 'status'> | undefined;\n\n if (!lockedRelease) {\n throw new errors.NotFoundError(`No release found for id ${releaseId}`);\n }\n\n if (lockedRelease.releasedAt) {\n throw new errors.ValidationError('Release already published');\n }\n\n if (lockedRelease.status === 'failed') {\n throw new errors.ValidationError('Release failed to publish');\n }\n\n try {\n strapi.log.info(`[Content Releases] Starting to publish release ${lockedRelease.name}`);\n\n const formattedActions = await getFormattedActions(releaseId);\n\n // Publish content types in dependency order so that when entity A has a relation\n // to entity B, B is published first to keep this relation.\n const contentTypeUids = getPublishOrderForContentTypes(\n Object.keys(formattedActions) as UID.ContentType[],\n { strapi }\n );\n\n await strapi.db.transaction(async () => {\n for (const contentTypeUid of contentTypeUids) {\n const contentType = contentTypeUid as UID.ContentType;\n const { publish, unpublish } = formattedActions[contentType];\n\n // Serialize within a content type: concurrent publishes of related documents\n // can race on shared join-table state (notably self-referential relations) and\n // leave inconsistent FK rows when one branch deletes a row another branch is\n // about to reference.\n for (const params of publish) {\n await strapi.documents(contentType).publish(params);\n }\n\n for (const params of unpublish) {\n await strapi.documents(contentType).unpublish(params);\n }\n }\n });\n\n const release = await strapi.db.query(RELEASE_MODEL_UID).update({\n where: {\n id: releaseId,\n },\n data: {\n status: 'done',\n releasedAt: new Date(),\n },\n });\n\n dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {\n isPublished: true,\n release,\n });\n\n strapi.telemetry.send('didPublishContentRelease');\n\n return { release, error: null, lockedRelease };\n } catch (error) {\n dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {\n isPublished: false,\n error,\n });\n\n // We need to run the update in the same transaction because the release is locked\n await strapi.db\n ?.queryBuilder(RELEASE_MODEL_UID)\n .where({ id: releaseId })\n .update({\n status: 'failed',\n })\n .transacting(trx)\n .execute();\n\n // At this point, we don't want to throw the error because if that happen we rollback the change in the release status\n // We want to throw the error after the transaction is finished, so we return the error\n return {\n release: null,\n // A rejection can carry any value, even null: never mistake it for success\n error: error ?? new Error('Release publish failed with an empty error'),\n lockedRelease,\n };\n }\n });\n\n // The 'failed' status is already committed, so this emit cannot be rolled back.\n // Pre-flight rejections threw before the run and never reach it.\n if (error !== null) {\n await emitAudit({ strapi }, AUDITED_EVENTS.RELEASE_TRIGGER, {\n releaseId: lockedRelease.id,\n name: lockedRelease.name,\n outcome: 'failure',\n // The error's name and nothing else from it: driver errors can carry row\n // contents in their properties\n reason: error instanceof Error ? error.name : 'Error',\n });\n\n // Swallowing a non-Error throwable would report the publish as successful\n if (error instanceof Error) {\n throw error;\n }\n throw new Error('Release publish failed', { cause: error });\n }\n\n // Counted after the publish, while the actions still exist.\n // A count failure doesn't fail the committed publish: the error is returned.\n const releaseActionService = getService('release-action', { strapi });\n let counts = null;\n let countsError: Error | null = null;\n\n try {\n const [published, unpublished] = await Promise.all([\n releaseActionService.countActions({ filters: { release: releaseId, type: 'publish' } }),\n releaseActionService.countActions({\n filters: { release: releaseId, type: 'unpublish' },\n }),\n ]);\n counts = { published, unpublished };\n } catch (err) {\n countsError = err instanceof Error ? err : new Error(String(err), { cause: err });\n strapi.log.error(\n `Failed to count the entries for the release.trigger entry of release ${releaseId}`,\n { error: countsError }\n );\n }\n\n await emitAudit({ strapi }, AUDITED_EVENTS.RELEASE_TRIGGER, {\n releaseId: lockedRelease.id,\n name: lockedRelease.name,\n outcome: 'success',\n published: counts?.published,\n unpublished: counts?.unpublished,\n });\n\n return { release, counts, countsError };\n },\n\n async updateReleaseStatus(releaseId: Release['id']) {\n const releaseActionService = getService('release-action', { strapi });\n\n const [totalActions, invalidActions] = await Promise.all([\n releaseActionService.countActions({\n filters: {\n release: releaseId,\n },\n }),\n releaseActionService.countActions({\n filters: {\n release: releaseId,\n isEntryValid: false,\n },\n }),\n ]);\n\n if (totalActions > 0) {\n if (invalidActions > 0) {\n return strapi.db.query(RELEASE_MODEL_UID).update({\n where: {\n id: releaseId,\n },\n data: {\n status: 'blocked',\n },\n });\n }\n\n return strapi.db.query(RELEASE_MODEL_UID).update({\n where: {\n id: releaseId,\n },\n data: {\n status: 'ready',\n },\n });\n }\n\n return strapi.db.query(RELEASE_MODEL_UID).update({\n where: {\n id: releaseId,\n },\n data: {\n status: 'empty',\n },\n });\n },\n };\n};\n\nexport type ReleaseService = ReturnType<typeof createReleaseService>;\n\nexport default createReleaseService;\n"],"names":["createReleaseService","strapi","dispatchWebhook","event","isPublished","release","error","eventHub","emit","getFormattedActions","releaseId","actions","db","query","RELEASE_ACTION_MODEL_UID","findMany","where","id","length","errors","ValidationError","formattedActions","action","contentTypeUid","contentType","publish","unpublish","type","push","documentId","entryDocumentId","locale","create","releaseData","user","releaseWithCreatorFields","setCreatorFields","validatePendingReleasesLimit","validateUniqueNameForPendingRelease","validateScheduledAtIsLaterThanNow","getService","Promise","all","name","scheduledAt","RELEASE_MODEL_UID","data","status","emitAudit","AUDITED_EVENTS","RELEASE_CREATE","timezone","schedulingService","set","telemetry","send","findOne","dbQuery","get","transform","findPage","populate","count","update","isEdition","NotFoundError","releasedAt","updatedRelease","changes","getReleaseChanges","Object","keys","RELEASE_UPDATE","cancel","updateReleaseStatus","getAllComponents","contentManagerComponentsService","plugin","service","components","findAllComponents","componentsMap","reduce","acc","component","uid","delete","select","transaction","deleteMany","$in","map","RELEASE_DELETE","lockedRelease","trx","queryBuilder","first","transacting","forUpdate","execute","log","info","contentTypeUids","getPublishOrderForContentTypes","params","documents","Date","ALLOWED_WEBHOOK_EVENTS","RELEASES_PUBLISH","Error","RELEASE_TRIGGER","outcome","reason","cause","releaseActionService","counts","countsError","published","unpublished","countActions","filters","err","String","totalActions","invalidActions","isEntryValid"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"release.mjs","sources":["../../../server/src/services/release.ts"],"sourcesContent":["import { setCreatorFields, errors, emitAudit } from '@strapi/utils';\n\nimport type { Core, Struct, UID, Data } from '@strapi/types';\n\nimport {\n ALLOWED_WEBHOOK_EVENTS,\n AUDITED_EVENTS,\n RELEASE_ACTION_MODEL_UID,\n RELEASE_MODEL_UID,\n} from '../constants';\nimport type {\n GetReleases,\n CreateRelease,\n UpdateRelease,\n PublishRelease,\n GetRelease,\n Release,\n DeleteRelease,\n} from '../../../shared/contracts/releases';\nimport type { ReleaseAction } from '../../../shared/contracts/release-actions';\nimport type { UserInfo } from '../../../shared/types';\nimport { getService, getPublishOrderForContentTypes } from '../utils';\nimport { getReleaseChanges } from '../audit-logs';\n\nconst createReleaseService = ({ strapi }: { strapi: Core.Strapi }) => {\n const dispatchWebhook = (\n event: string,\n { isPublished, release, error }: { isPublished: boolean; release?: any; error?: unknown }\n ) => {\n strapi.eventHub.emit(event, {\n isPublished,\n error,\n release,\n });\n };\n\n /**\n * Given a release id, it returns the actions formatted ready to be used to publish them.\n * We split them by contentType and type (publish/unpublish) and extract only the documentIds and locales.\n */\n const getFormattedActions = async (releaseId: Release['id']) => {\n const actions = (await strapi.db.query(RELEASE_ACTION_MODEL_UID).findMany({\n where: {\n release: {\n id: releaseId,\n },\n },\n })) as ReleaseAction[];\n\n if (actions.length === 0) {\n throw new errors.ValidationError('No entries to publish');\n }\n\n /**\n * We separate publish and unpublish actions, grouping them by contentType and extracting only their documentIds and locales.\n */\n const formattedActions: {\n [key: UID.ContentType]: {\n publish: { documentId: ReleaseAction['entryDocumentId']; locale?: string }[];\n unpublish: { documentId: ReleaseAction['entryDocumentId']; locale?: string }[];\n };\n } = {};\n\n for (const action of actions) {\n const contentTypeUid: UID.ContentType = action.contentType;\n\n if (!formattedActions[contentTypeUid]) {\n formattedActions[contentTypeUid] = {\n publish: [],\n unpublish: [],\n };\n }\n\n formattedActions[contentTypeUid][action.type].push({\n documentId: action.entryDocumentId,\n locale: action.locale,\n });\n }\n\n return formattedActions;\n };\n\n return {\n async create(releaseData: CreateRelease.Request['body'], { user }: { user: UserInfo }) {\n const releaseWithCreatorFields = await setCreatorFields({ user })(releaseData);\n\n const {\n validatePendingReleasesLimit,\n validateUniqueNameForPendingRelease,\n validateScheduledAtIsLaterThanNow,\n } = getService('release-validation', { strapi });\n\n await Promise.all([\n validatePendingReleasesLimit(),\n validateUniqueNameForPendingRelease(releaseWithCreatorFields.name),\n validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt),\n ]);\n\n const release = await strapi.db.query(RELEASE_MODEL_UID).create({\n data: {\n ...releaseWithCreatorFields,\n status: 'empty',\n },\n });\n\n // Audited before scheduling: a scheduling failure shouldn't leave the write\n // unrecorded.\n await emitAudit({ strapi }, AUDITED_EVENTS.RELEASE_CREATE, {\n releaseId: release.id,\n name: release.name,\n ...(release.scheduledAt && {\n scheduledAt: release.scheduledAt,\n timezone: release.timezone,\n }),\n });\n\n if (releaseWithCreatorFields.scheduledAt) {\n const schedulingService = getService('scheduling', { strapi });\n\n await schedulingService.set(release.id, release.scheduledAt);\n }\n\n strapi.telemetry.send('didCreateContentRelease');\n\n return release;\n },\n\n async findOne(id: GetRelease.Request['params']['id'], query = {}) {\n const dbQuery = strapi.get('query-params').transform(RELEASE_MODEL_UID, query);\n const release = await strapi.db.query(RELEASE_MODEL_UID).findOne({\n ...dbQuery,\n where: { id },\n });\n\n return release;\n },\n\n findPage(query?: GetReleases.Request['query']) {\n const dbQuery = strapi.get('query-params').transform(RELEASE_MODEL_UID, query ?? {});\n\n return strapi.db.query(RELEASE_MODEL_UID).findPage({\n ...dbQuery,\n populate: {\n actions: {\n count: true,\n },\n },\n });\n },\n\n findMany(query?: any) {\n const dbQuery = strapi.get('query-params').transform(RELEASE_MODEL_UID, query ?? {});\n\n return strapi.db.query(RELEASE_MODEL_UID).findMany({\n ...dbQuery,\n });\n },\n\n async update(\n id: Data.ID,\n releaseData: UpdateRelease.Request['body'],\n { user }: { user: UserInfo }\n ) {\n const releaseWithCreatorFields = await setCreatorFields({ user, isEdition: true })(\n releaseData\n );\n\n const { validateUniqueNameForPendingRelease, validateScheduledAtIsLaterThanNow } = getService(\n 'release-validation',\n { strapi }\n );\n\n await Promise.all([\n validateUniqueNameForPendingRelease(releaseWithCreatorFields.name, id),\n validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt),\n ]);\n\n const release = await strapi.db.query(RELEASE_MODEL_UID).findOne({ where: { id } });\n\n if (!release) {\n throw new errors.NotFoundError(`No release found for id ${id}`);\n }\n\n if (release.releasedAt) {\n throw new errors.ValidationError('Release already published');\n }\n\n const updatedRelease = await strapi.db.query(RELEASE_MODEL_UID).update({\n where: { id },\n data: releaseWithCreatorFields,\n });\n\n // Audited before scheduling, against the pre-image read next to the write.\n // A write that matched no row (deleted in between) has nothing to report.\n if (updatedRelease) {\n const changes = getReleaseChanges(release, updatedRelease);\n\n if (Object.keys(changes).length > 0) {\n await emitAudit({ strapi }, AUDITED_EVENTS.RELEASE_UPDATE, {\n releaseId: updatedRelease.id,\n name: updatedRelease.name,\n changes,\n });\n }\n }\n\n const schedulingService = getService('scheduling', { strapi });\n\n if (releaseData.scheduledAt) {\n // set function always cancel the previous job if it exists, so we can call it directly\n await schedulingService.set(id, releaseData.scheduledAt);\n } else if (release.scheduledAt) {\n // When user don't send a scheduledAt and we have one on the release, means that user want to unschedule it\n schedulingService.cancel(id);\n }\n\n this.updateReleaseStatus(id);\n\n strapi.telemetry.send('didUpdateContentRelease');\n\n return updatedRelease;\n },\n\n async getAllComponents() {\n const contentManagerComponentsService = strapi\n .plugin('content-manager')\n .service('components');\n\n const components = await contentManagerComponentsService.findAllComponents();\n\n const componentsMap = components.reduce(\n (\n acc: { [key: Struct.ComponentSchema['uid']]: Struct.ComponentSchema },\n component: Struct.ComponentSchema\n ) => {\n acc[component.uid] = component;\n\n return acc;\n },\n {}\n );\n\n return componentsMap;\n },\n\n async delete(releaseId: DeleteRelease.Request['params']['id']) {\n const release: Release = await strapi.db.query(RELEASE_MODEL_UID).findOne({\n where: { id: releaseId },\n populate: {\n actions: {\n select: ['id'],\n },\n },\n });\n\n if (!release) {\n throw new errors.NotFoundError(`No release found for id ${releaseId}`);\n }\n\n if (release.releasedAt) {\n throw new errors.ValidationError('Release already published');\n }\n\n // Only delete the release and its actions is you in fact can delete all the actions and the release\n // Otherwise, if the transaction fails it throws an error\n await strapi.db.transaction(async () => {\n await strapi.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({\n where: {\n id: {\n $in: release.actions.map((action) => action.id),\n },\n },\n });\n\n await strapi.db.query(RELEASE_MODEL_UID).delete({\n where: {\n id: releaseId,\n },\n });\n });\n\n if (release.scheduledAt) {\n const schedulingService = getService('scheduling', { strapi });\n await schedulingService.cancel(release.id);\n }\n\n strapi.telemetry.send('didDeleteContentRelease');\n\n await emitAudit({ strapi }, AUDITED_EVENTS.RELEASE_DELETE, {\n releaseId: release.id,\n name: release.name,\n });\n\n return release;\n },\n\n async publish(releaseId: PublishRelease.Request['params']['id']) {\n const {\n release,\n error,\n lockedRelease,\n }: {\n release: Pick<Release, 'id' | 'releasedAt' | 'status'> | null;\n error: unknown;\n lockedRelease: Pick<Release, 'id' | 'name'>;\n } = await strapi.db.transaction(async ({ trx }) => {\n /**\n * We lock the release in this transaction, so any other process trying to publish it will wait until this transaction is finished\n * In this transaction we don't care about rollback, becasue we want to persist the lock until the end and if it fails we want to change the release status to failed\n */\n const lockedRelease = (await strapi.db\n ?.queryBuilder(RELEASE_MODEL_UID)\n .where({ id: releaseId })\n .select(['id', 'name', 'releasedAt', 'status'])\n .first()\n .transacting(trx)\n .forUpdate()\n .execute()) as Pick<Release, 'id' | 'name' | 'releasedAt' | 'status'> | undefined;\n\n if (!lockedRelease) {\n throw new errors.NotFoundError(`No release found for id ${releaseId}`);\n }\n\n if (lockedRelease.releasedAt) {\n throw new errors.ValidationError('Release already published');\n }\n\n if (lockedRelease.status === 'failed') {\n throw new errors.ValidationError('Release failed to publish');\n }\n\n try {\n strapi.log.info(`[Content Releases] Starting to publish release ${lockedRelease.name}`);\n\n const formattedActions = await getFormattedActions(releaseId);\n\n // Publish content types in dependency order so that when entity A has a relation\n // to entity B, B is published first to keep this relation.\n const contentTypeUids = getPublishOrderForContentTypes(\n Object.keys(formattedActions) as UID.ContentType[],\n { strapi }\n );\n\n await strapi.db.transaction(async () => {\n for (const contentTypeUid of contentTypeUids) {\n const contentType = contentTypeUid as UID.ContentType;\n const { publish, unpublish } = formattedActions[contentType];\n\n // Serialize within a content type: concurrent publishes of related documents\n // can race on shared join-table state (notably self-referential relations) and\n // leave inconsistent FK rows when one branch deletes a row another branch is\n // about to reference.\n for (const params of publish) {\n await strapi.documents(contentType).publish(params);\n }\n\n for (const params of unpublish) {\n await strapi.documents(contentType).unpublish(params);\n }\n }\n });\n\n const release = await strapi.db.query(RELEASE_MODEL_UID).update({\n where: {\n id: releaseId,\n },\n data: {\n status: 'done',\n releasedAt: new Date(),\n },\n });\n\n dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {\n isPublished: true,\n release,\n });\n\n strapi.telemetry.send('didPublishContentRelease');\n\n return { release, error: null, lockedRelease };\n } catch (error) {\n dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {\n isPublished: false,\n error,\n });\n\n // We need to run the update in the same transaction because the release is locked\n await strapi.db\n ?.queryBuilder(RELEASE_MODEL_UID)\n .where({ id: releaseId })\n .update({\n status: 'failed',\n })\n .transacting(trx)\n .execute();\n\n // At this point, we don't want to throw the error because if that happen we rollback the change in the release status\n // We want to throw the error after the transaction is finished, so we return the error\n return {\n release: null,\n // A rejection can carry any value, even null: never mistake it for success\n error: error ?? new Error('Release publish failed with an empty error'),\n lockedRelease,\n };\n }\n });\n\n // The 'failed' status is already committed, so this emit cannot be rolled back.\n // Pre-flight rejections threw before the run and never reach it.\n if (error !== null) {\n await emitAudit({ strapi }, AUDITED_EVENTS.RELEASE_TRIGGER, {\n releaseId: lockedRelease.id,\n name: lockedRelease.name,\n outcome: 'failure',\n // The error's name and nothing else from it: driver errors can carry row\n // contents in their properties\n reason: error instanceof Error ? error.name : 'Error',\n });\n\n // Swallowing a non-Error throwable would report the publish as successful\n if (error instanceof Error) {\n throw error;\n }\n throw new Error('Release publish failed', { cause: error });\n }\n\n // Counted after the publish, while the actions still exist.\n // A count failure doesn't fail the committed publish: the error is returned.\n const releaseActionService = getService('release-action', { strapi });\n let counts = null;\n let countsError: Error | null = null;\n\n try {\n const [published, unpublished] = await Promise.all([\n releaseActionService.countActions({ filters: { release: releaseId, type: 'publish' } }),\n releaseActionService.countActions({\n filters: { release: releaseId, type: 'unpublish' },\n }),\n ]);\n counts = { published, unpublished };\n } catch (err) {\n countsError = err instanceof Error ? err : new Error(String(err), { cause: err });\n strapi.log.error(\n `Failed to count the entries for the release.trigger entry of release ${releaseId}`,\n { error: countsError }\n );\n }\n\n await emitAudit({ strapi }, AUDITED_EVENTS.RELEASE_TRIGGER, {\n releaseId: lockedRelease.id,\n name: lockedRelease.name,\n outcome: 'success',\n published: counts?.published,\n unpublished: counts?.unpublished,\n });\n\n return { release, counts, countsError };\n },\n\n async updateReleaseStatus(releaseId: Release['id']) {\n const releaseActionService = getService('release-action', { strapi });\n\n const [totalActions, invalidActions] = await Promise.all([\n releaseActionService.countActions({\n filters: {\n release: releaseId,\n },\n }),\n releaseActionService.countActions({\n filters: {\n release: releaseId,\n isEntryValid: false,\n },\n }),\n ]);\n\n if (totalActions > 0) {\n if (invalidActions > 0) {\n return strapi.db.query(RELEASE_MODEL_UID).update({\n where: {\n id: releaseId,\n },\n data: {\n status: 'blocked',\n },\n });\n }\n\n return strapi.db.query(RELEASE_MODEL_UID).update({\n where: {\n id: releaseId,\n },\n data: {\n status: 'ready',\n },\n });\n }\n\n return strapi.db.query(RELEASE_MODEL_UID).update({\n where: {\n id: releaseId,\n },\n data: {\n status: 'empty',\n },\n });\n },\n };\n};\n\nexport type ReleaseService = ReturnType<typeof createReleaseService>;\n\nexport default createReleaseService;\n"],"names":["createReleaseService","strapi","dispatchWebhook","event","isPublished","release","error","eventHub","emit","getFormattedActions","releaseId","actions","db","query","RELEASE_ACTION_MODEL_UID","findMany","where","id","length","errors","ValidationError","formattedActions","action","contentTypeUid","contentType","publish","unpublish","type","push","documentId","entryDocumentId","locale","create","releaseData","user","releaseWithCreatorFields","setCreatorFields","validatePendingReleasesLimit","validateUniqueNameForPendingRelease","validateScheduledAtIsLaterThanNow","getService","Promise","all","name","scheduledAt","RELEASE_MODEL_UID","data","status","emitAudit","AUDITED_EVENTS","RELEASE_CREATE","timezone","schedulingService","set","telemetry","send","findOne","dbQuery","get","transform","findPage","populate","count","update","isEdition","NotFoundError","releasedAt","updatedRelease","changes","getReleaseChanges","Object","keys","RELEASE_UPDATE","cancel","updateReleaseStatus","getAllComponents","contentManagerComponentsService","plugin","service","components","findAllComponents","componentsMap","reduce","acc","component","uid","delete","select","transaction","deleteMany","$in","map","RELEASE_DELETE","lockedRelease","trx","queryBuilder","first","transacting","forUpdate","execute","log","info","contentTypeUids","getPublishOrderForContentTypes","params","documents","Date","ALLOWED_WEBHOOK_EVENTS","RELEASES_PUBLISH","Error","RELEASE_TRIGGER","outcome","reason","cause","releaseActionService","counts","countsError","published","unpublished","countActions","filters","err","String","totalActions","invalidActions","isEntryValid"],"mappings":";;;;;AAwBA,MAAMA,oBAAAA,GAAuB,CAAC,EAAEC,MAAM,EAA2B,GAAA;IAC/D,MAAMC,eAAAA,GAAkB,CACtBC,KAAAA,EACA,EAAEC,WAAW,EAAEC,OAAO,EAAEC,KAAK,EAA4D,GAAA;AAEzFL,QAAAA,MAAAA,CAAOM,QAAQ,CAACC,IAAI,CAACL,KAAAA,EAAO;AAC1BC,YAAAA,WAAAA;AACAE,YAAAA,KAAAA;AACAD,YAAAA;AACF,SAAA,CAAA;AACF,IAAA,CAAA;AAEA;;;MAIA,MAAMI,sBAAsB,OAAOC,SAAAA,GAAAA;QACjC,MAAMC,OAAAA,GAAW,MAAMV,MAAAA,CAAOW,EAAE,CAACC,KAAK,CAACC,wBAAAA,CAAAA,CAA0BC,QAAQ,CAAC;YACxEC,KAAAA,EAAO;gBACLX,OAAAA,EAAS;oBACPY,EAAAA,EAAIP;AACN;AACF;AACF,SAAA,CAAA;QAEA,IAAIC,OAAAA,CAAQO,MAAM,KAAK,CAAA,EAAG;YACxB,MAAM,IAAIC,MAAAA,CAAOC,eAAe,CAAC,uBAAA,CAAA;AACnC,QAAA;AAEA;;QAGA,MAAMC,mBAKF,EAAC;QAEL,KAAK,MAAMC,UAAUX,OAAAA,CAAS;YAC5B,MAAMY,cAAAA,GAAkCD,OAAOE,WAAW;AAE1D,YAAA,IAAI,CAACH,gBAAgB,CAACE,cAAAA,CAAe,EAAE;gBACrCF,gBAAgB,CAACE,eAAe,GAAG;AACjCE,oBAAAA,OAAAA,EAAS,EAAE;AACXC,oBAAAA,SAAAA,EAAW;AACb,iBAAA;AACF,YAAA;YAEAL,gBAAgB,CAACE,eAAe,CAACD,MAAAA,CAAOK,IAAI,CAAC,CAACC,IAAI,CAAC;AACjDC,gBAAAA,UAAAA,EAAYP,OAAOQ,eAAe;AAClCC,gBAAAA,MAAAA,EAAQT,OAAOS;AACjB,aAAA,CAAA;AACF,QAAA;QAEA,OAAOV,gBAAAA;AACT,IAAA,CAAA;IAEA,OAAO;AACL,QAAA,MAAMW,MAAAA,CAAAA,CAAOC,WAA0C,EAAE,EAAEC,IAAI,EAAsB,EAAA;YACnF,MAAMC,wBAAAA,GAA2B,MAAMC,gBAAAA,CAAiB;AAAEF,gBAAAA;aAAK,CAAA,CAAGD,WAAAA,CAAAA;YAElE,MAAM,EACJI,4BAA4B,EAC5BC,mCAAmC,EACnCC,iCAAiC,EAClC,GAAGC,UAAAA,CAAW,oBAAA,EAAsB;AAAEvC,gBAAAA;AAAO,aAAA,CAAA;YAE9C,MAAMwC,OAAAA,CAAQC,GAAG,CAAC;AAChBL,gBAAAA,4BAAAA,EAAAA;AACAC,gBAAAA,mCAAAA,CAAoCH,yBAAyBQ,IAAI,CAAA;AACjEJ,gBAAAA,iCAAAA,CAAkCJ,yBAAyBS,WAAW;AACvE,aAAA,CAAA;YAED,MAAMvC,OAAAA,GAAU,MAAMJ,MAAAA,CAAOW,EAAE,CAACC,KAAK,CAACgC,iBAAAA,CAAAA,CAAmBb,MAAM,CAAC;gBAC9Dc,IAAAA,EAAM;AACJ,oBAAA,GAAGX,wBAAwB;oBAC3BY,MAAAA,EAAQ;AACV;AACF,aAAA,CAAA;;;AAIA,YAAA,MAAMC,SAAAA,CAAU;AAAE/C,gBAAAA;aAAO,EAAGgD,cAAAA,CAAeC,cAAc,EAAE;AACzDxC,gBAAAA,SAAAA,EAAWL,QAAQY,EAAE;AACrB0B,gBAAAA,IAAAA,EAAMtC,QAAQsC,IAAI;gBAClB,GAAItC,OAAAA,CAAQuC,WAAW,IAAI;AACzBA,oBAAAA,WAAAA,EAAavC,QAAQuC,WAAW;AAChCO,oBAAAA,QAAAA,EAAU9C,QAAQ8C;;AAEtB,aAAA,CAAA;YAEA,IAAIhB,wBAAAA,CAAyBS,WAAW,EAAE;gBACxC,MAAMQ,iBAAAA,GAAoBZ,WAAW,YAAA,EAAc;AAAEvC,oBAAAA;AAAO,iBAAA,CAAA;AAE5D,gBAAA,MAAMmD,kBAAkBC,GAAG,CAAChD,QAAQY,EAAE,EAAEZ,QAAQuC,WAAW,CAAA;AAC7D,YAAA;YAEA3C,MAAAA,CAAOqD,SAAS,CAACC,IAAI,CAAC,yBAAA,CAAA;YAEtB,OAAOlD,OAAAA;AACT,QAAA,CAAA;AAEA,QAAA,MAAMmD,OAAAA,CAAAA,CAAQvC,EAAsC,EAAEJ,KAAAA,GAAQ,EAAE,EAAA;AAC9D,YAAA,MAAM4C,UAAUxD,MAAAA,CAAOyD,GAAG,CAAC,cAAA,CAAA,CAAgBC,SAAS,CAACd,iBAAAA,EAAmBhC,KAAAA,CAAAA;YACxE,MAAMR,OAAAA,GAAU,MAAMJ,MAAAA,CAAOW,EAAE,CAACC,KAAK,CAACgC,iBAAAA,CAAAA,CAAmBW,OAAO,CAAC;AAC/D,gBAAA,GAAGC,OAAO;gBACVzC,KAAAA,EAAO;AAAEC,oBAAAA;AAAG;AACd,aAAA,CAAA;YAEA,OAAOZ,OAAAA;AACT,QAAA,CAAA;AAEAuD,QAAAA,QAAAA,CAAAA,CAAS/C,KAAoC,EAAA;YAC3C,MAAM4C,OAAAA,GAAUxD,OAAOyD,GAAG,CAAC,gBAAgBC,SAAS,CAACd,iBAAAA,EAAmBhC,KAAAA,IAAS,EAAC,CAAA;AAElF,YAAA,OAAOZ,OAAOW,EAAE,CAACC,KAAK,CAACgC,iBAAAA,CAAAA,CAAmBe,QAAQ,CAAC;AACjD,gBAAA,GAAGH,OAAO;gBACVI,QAAAA,EAAU;oBACRlD,OAAAA,EAAS;wBACPmD,KAAAA,EAAO;AACT;AACF;AACF,aAAA,CAAA;AACF,QAAA,CAAA;AAEA/C,QAAAA,QAAAA,CAAAA,CAASF,KAAW,EAAA;YAClB,MAAM4C,OAAAA,GAAUxD,OAAOyD,GAAG,CAAC,gBAAgBC,SAAS,CAACd,iBAAAA,EAAmBhC,KAAAA,IAAS,EAAC,CAAA;AAElF,YAAA,OAAOZ,OAAOW,EAAE,CAACC,KAAK,CAACgC,iBAAAA,CAAAA,CAAmB9B,QAAQ,CAAC;AACjD,gBAAA,GAAG0C;AACL,aAAA,CAAA;AACF,QAAA,CAAA;AAEA,QAAA,MAAMM,QACJ9C,EAAW,EACXgB,WAA0C,EAC1C,EAAEC,IAAI,EAAsB,EAAA;YAE5B,MAAMC,wBAAAA,GAA2B,MAAMC,gBAAAA,CAAiB;AAAEF,gBAAAA,IAAAA;gBAAM8B,SAAAA,EAAW;aAAK,CAAA,CAC9E/B,WAAAA,CAAAA;AAGF,YAAA,MAAM,EAAEK,mCAAmC,EAAEC,iCAAiC,EAAE,GAAGC,WACjF,oBAAA,EACA;AAAEvC,gBAAAA;AAAO,aAAA,CAAA;YAGX,MAAMwC,OAAAA,CAAQC,GAAG,CAAC;gBAChBJ,mCAAAA,CAAoCH,wBAAAA,CAAyBQ,IAAI,EAAE1B,EAAAA,CAAAA;AACnEsB,gBAAAA,iCAAAA,CAAkCJ,yBAAyBS,WAAW;AACvE,aAAA,CAAA;YAED,MAAMvC,OAAAA,GAAU,MAAMJ,MAAAA,CAAOW,EAAE,CAACC,KAAK,CAACgC,iBAAAA,CAAAA,CAAmBW,OAAO,CAAC;gBAAExC,KAAAA,EAAO;AAAEC,oBAAAA;AAAG;AAAE,aAAA,CAAA;AAEjF,YAAA,IAAI,CAACZ,OAAAA,EAAS;AACZ,gBAAA,MAAM,IAAIc,MAAAA,CAAO8C,aAAa,CAAC,CAAC,wBAAwB,EAAEhD,EAAAA,CAAAA,CAAI,CAAA;AAChE,YAAA;YAEA,IAAIZ,OAAAA,CAAQ6D,UAAU,EAAE;gBACtB,MAAM,IAAI/C,MAAAA,CAAOC,eAAe,CAAC,2BAAA,CAAA;AACnC,YAAA;YAEA,MAAM+C,cAAAA,GAAiB,MAAMlE,MAAAA,CAAOW,EAAE,CAACC,KAAK,CAACgC,iBAAAA,CAAAA,CAAmBkB,MAAM,CAAC;gBACrE/C,KAAAA,EAAO;AAAEC,oBAAAA;AAAG,iBAAA;gBACZ6B,IAAAA,EAAMX;AACR,aAAA,CAAA;;;AAIA,YAAA,IAAIgC,cAAAA,EAAgB;gBAClB,MAAMC,OAAAA,GAAUC,kBAAkBhE,OAAAA,EAAS8D,cAAAA,CAAAA;AAE3C,gBAAA,IAAIG,OAAOC,IAAI,CAACH,OAAAA,CAAAA,CAASlD,MAAM,GAAG,CAAA,EAAG;AACnC,oBAAA,MAAM8B,SAAAA,CAAU;AAAE/C,wBAAAA;qBAAO,EAAGgD,cAAAA,CAAeuB,cAAc,EAAE;AACzD9D,wBAAAA,SAAAA,EAAWyD,eAAelD,EAAE;AAC5B0B,wBAAAA,IAAAA,EAAMwB,eAAexB,IAAI;AACzByB,wBAAAA;AACF,qBAAA,CAAA;AACF,gBAAA;AACF,YAAA;YAEA,MAAMhB,iBAAAA,GAAoBZ,WAAW,YAAA,EAAc;AAAEvC,gBAAAA;AAAO,aAAA,CAAA;YAE5D,IAAIgC,WAAAA,CAAYW,WAAW,EAAE;;AAE3B,gBAAA,MAAMQ,iBAAAA,CAAkBC,GAAG,CAACpC,EAAAA,EAAIgB,YAAYW,WAAW,CAAA;YACzD,CAAA,MAAO,IAAIvC,OAAAA,CAAQuC,WAAW,EAAE;;AAE9BQ,gBAAAA,iBAAAA,CAAkBqB,MAAM,CAACxD,EAAAA,CAAAA;AAC3B,YAAA;YAEA,IAAI,CAACyD,mBAAmB,CAACzD,EAAAA,CAAAA;YAEzBhB,MAAAA,CAAOqD,SAAS,CAACC,IAAI,CAAC,yBAAA,CAAA;YAEtB,OAAOY,cAAAA;AACT,QAAA,CAAA;QAEA,MAAMQ,gBAAAA,CAAAA,GAAAA;AACJ,YAAA,MAAMC,kCAAkC3E,MAAAA,CACrC4E,MAAM,CAAC,iBAAA,CAAA,CACPC,OAAO,CAAC,YAAA,CAAA;YAEX,MAAMC,UAAAA,GAAa,MAAMH,+BAAAA,CAAgCI,iBAAiB,EAAA;AAE1E,YAAA,MAAMC,aAAAA,GAAgBF,UAAAA,CAAWG,MAAM,CACrC,CACEC,GAAAA,EACAC,SAAAA,GAAAA;AAEAD,gBAAAA,GAAG,CAACC,SAAAA,CAAUC,GAAG,CAAC,GAAGD,SAAAA;gBAErB,OAAOD,GAAAA;AACT,YAAA,CAAA,EACA,EAAC,CAAA;YAGH,OAAOF,aAAAA;AACT,QAAA,CAAA;AAEA,QAAA,MAAMK,QAAO5E,SAAgD,EAAA;YAC3D,MAAML,OAAAA,GAAmB,MAAMJ,MAAAA,CAAOW,EAAE,CAACC,KAAK,CAACgC,iBAAAA,CAAAA,CAAmBW,OAAO,CAAC;gBACxExC,KAAAA,EAAO;oBAAEC,EAAAA,EAAIP;AAAU,iBAAA;gBACvBmD,QAAAA,EAAU;oBACRlD,OAAAA,EAAS;wBACP4E,MAAAA,EAAQ;AAAC,4BAAA;AAAK;AAChB;AACF;AACF,aAAA,CAAA;AAEA,YAAA,IAAI,CAAClF,OAAAA,EAAS;AACZ,gBAAA,MAAM,IAAIc,MAAAA,CAAO8C,aAAa,CAAC,CAAC,wBAAwB,EAAEvD,SAAAA,CAAAA,CAAW,CAAA;AACvE,YAAA;YAEA,IAAIL,OAAAA,CAAQ6D,UAAU,EAAE;gBACtB,MAAM,IAAI/C,MAAAA,CAAOC,eAAe,CAAC,2BAAA,CAAA;AACnC,YAAA;;;AAIA,YAAA,MAAMnB,MAAAA,CAAOW,EAAE,CAAC4E,WAAW,CAAC,UAAA;AAC1B,gBAAA,MAAMvF,OAAOW,EAAE,CAACC,KAAK,CAACC,wBAAAA,CAAAA,CAA0B2E,UAAU,CAAC;oBACzDzE,KAAAA,EAAO;wBACLC,EAAAA,EAAI;4BACFyE,GAAAA,EAAKrF,OAAAA,CAAQM,OAAO,CAACgF,GAAG,CAAC,CAACrE,MAAAA,GAAWA,OAAOL,EAAE;AAChD;AACF;AACF,iBAAA,CAAA;AAEA,gBAAA,MAAMhB,OAAOW,EAAE,CAACC,KAAK,CAACgC,iBAAAA,CAAAA,CAAmByC,MAAM,CAAC;oBAC9CtE,KAAAA,EAAO;wBACLC,EAAAA,EAAIP;AACN;AACF,iBAAA,CAAA;AACF,YAAA,CAAA,CAAA;YAEA,IAAIL,OAAAA,CAAQuC,WAAW,EAAE;gBACvB,MAAMQ,iBAAAA,GAAoBZ,WAAW,YAAA,EAAc;AAAEvC,oBAAAA;AAAO,iBAAA,CAAA;AAC5D,gBAAA,MAAMmD,iBAAAA,CAAkBqB,MAAM,CAACpE,OAAAA,CAAQY,EAAE,CAAA;AAC3C,YAAA;YAEAhB,MAAAA,CAAOqD,SAAS,CAACC,IAAI,CAAC,yBAAA,CAAA;AAEtB,YAAA,MAAMP,SAAAA,CAAU;AAAE/C,gBAAAA;aAAO,EAAGgD,cAAAA,CAAe2C,cAAc,EAAE;AACzDlF,gBAAAA,SAAAA,EAAWL,QAAQY,EAAE;AACrB0B,gBAAAA,IAAAA,EAAMtC,QAAQsC;AAChB,aAAA,CAAA;YAEA,OAAOtC,OAAAA;AACT,QAAA,CAAA;AAEA,QAAA,MAAMoB,SAAQf,SAAiD,EAAA;AAC7D,YAAA,MAAM,EACJL,OAAO,EACPC,KAAK,EACLuF,aAAa,EACd,GAIG,MAAM5F,MAAAA,CAAOW,EAAE,CAAC4E,WAAW,CAAC,OAAO,EAAEM,GAAG,EAAE,GAAA;AAC5C;;;YAIA,MAAMD,gBAAiB,MAAM5F,MAAAA,CAAOW,EAAE,EAClCmF,YAAAA,CAAalD,mBACd7B,KAAAA,CAAM;oBAAEC,EAAAA,EAAIP;AAAU,iBAAA,CAAA,CACtB6E,MAAAA,CAAO;AAAC,oBAAA,IAAA;AAAM,oBAAA,MAAA;AAAQ,oBAAA,YAAA;AAAc,oBAAA;iBAAS,CAAA,CAC7CS,KAAAA,EAAAA,CACAC,WAAAA,CAAYH,GAAAA,CAAAA,CACZI,SAAAA,EAAAA,CACAC,OAAAA,EAAAA;AAEH,gBAAA,IAAI,CAACN,aAAAA,EAAe;AAClB,oBAAA,MAAM,IAAI1E,MAAAA,CAAO8C,aAAa,CAAC,CAAC,wBAAwB,EAAEvD,SAAAA,CAAAA,CAAW,CAAA;AACvE,gBAAA;gBAEA,IAAImF,aAAAA,CAAc3B,UAAU,EAAE;oBAC5B,MAAM,IAAI/C,MAAAA,CAAOC,eAAe,CAAC,2BAAA,CAAA;AACnC,gBAAA;gBAEA,IAAIyE,aAAAA,CAAc9C,MAAM,KAAK,QAAA,EAAU;oBACrC,MAAM,IAAI5B,MAAAA,CAAOC,eAAe,CAAC,2BAAA,CAAA;AACnC,gBAAA;gBAEA,IAAI;oBACFnB,MAAAA,CAAOmG,GAAG,CAACC,IAAI,CAAC,CAAC,+CAA+C,EAAER,aAAAA,CAAclD,IAAI,CAAA,CAAE,CAAA;oBAEtF,MAAMtB,gBAAAA,GAAmB,MAAMZ,mBAAAA,CAAoBC,SAAAA,CAAAA;;;AAInD,oBAAA,MAAM4F,eAAAA,GAAkBC,8BAAAA,CACtBjC,MAAAA,CAAOC,IAAI,CAAClD,gBAAAA,CAAAA,EACZ;AAAEpB,wBAAAA;AAAO,qBAAA,CAAA;AAGX,oBAAA,MAAMA,MAAAA,CAAOW,EAAE,CAAC4E,WAAW,CAAC,UAAA;wBAC1B,KAAK,MAAMjE,kBAAkB+E,eAAAA,CAAiB;AAC5C,4BAAA,MAAM9E,WAAAA,GAAcD,cAAAA;4BACpB,MAAM,EAAEE,OAAO,EAAEC,SAAS,EAAE,GAAGL,gBAAgB,CAACG,WAAAA,CAAY;;;;;4BAM5D,KAAK,MAAMgF,UAAU/E,OAAAA,CAAS;AAC5B,gCAAA,MAAMxB,MAAAA,CAAOwG,SAAS,CAACjF,WAAAA,CAAAA,CAAaC,OAAO,CAAC+E,MAAAA,CAAAA;AAC9C,4BAAA;4BAEA,KAAK,MAAMA,UAAU9E,SAAAA,CAAW;AAC9B,gCAAA,MAAMzB,MAAAA,CAAOwG,SAAS,CAACjF,WAAAA,CAAAA,CAAaE,SAAS,CAAC8E,MAAAA,CAAAA;AAChD,4BAAA;AACF,wBAAA;AACF,oBAAA,CAAA,CAAA;oBAEA,MAAMnG,OAAAA,GAAU,MAAMJ,MAAAA,CAAOW,EAAE,CAACC,KAAK,CAACgC,iBAAAA,CAAAA,CAAmBkB,MAAM,CAAC;wBAC9D/C,KAAAA,EAAO;4BACLC,EAAAA,EAAIP;AACN,yBAAA;wBACAoC,IAAAA,EAAM;4BACJC,MAAAA,EAAQ,MAAA;AACRmB,4BAAAA,UAAAA,EAAY,IAAIwC,IAAAA;AAClB;AACF,qBAAA,CAAA;oBAEAxG,eAAAA,CAAgByG,sBAAAA,CAAuBC,gBAAgB,EAAE;wBACvDxG,WAAAA,EAAa,IAAA;AACbC,wBAAAA;AACF,qBAAA,CAAA;oBAEAJ,MAAAA,CAAOqD,SAAS,CAACC,IAAI,CAAC,0BAAA,CAAA;oBAEtB,OAAO;AAAElD,wBAAAA,OAAAA;wBAASC,KAAAA,EAAO,IAAA;AAAMuF,wBAAAA;AAAc,qBAAA;AAC/C,gBAAA,CAAA,CAAE,OAAOvF,KAAAA,EAAO;oBACdJ,eAAAA,CAAgByG,sBAAAA,CAAuBC,gBAAgB,EAAE;wBACvDxG,WAAAA,EAAa,KAAA;AACbE,wBAAAA;AACF,qBAAA,CAAA;;AAGA,oBAAA,MAAML,MAAAA,CAAOW,EAAE,EACXmF,YAAAA,CAAalD,mBACd7B,KAAAA,CAAM;wBAAEC,EAAAA,EAAIP;AAAU,qBAAA,CAAA,CACtBqD,MAAAA,CAAO;wBACNhB,MAAAA,EAAQ;AACV,qBAAA,CAAA,CACCkD,YAAYH,GAAAA,CAAAA,CACZK,OAAAA,EAAAA;;;oBAIH,OAAO;wBACL9F,OAAAA,EAAS,IAAA;;wBAETC,KAAAA,EAAOA,KAAAA,IAAS,IAAIuG,KAAAA,CAAM,4CAAA,CAAA;AAC1BhB,wBAAAA;AACF,qBAAA;AACF,gBAAA;AACF,YAAA,CAAA,CAAA;;;AAIA,YAAA,IAAIvF,UAAU,IAAA,EAAM;AAClB,gBAAA,MAAM0C,SAAAA,CAAU;AAAE/C,oBAAAA;iBAAO,EAAGgD,cAAAA,CAAe6D,eAAe,EAAE;AAC1DpG,oBAAAA,SAAAA,EAAWmF,cAAc5E,EAAE;AAC3B0B,oBAAAA,IAAAA,EAAMkD,cAAclD,IAAI;oBACxBoE,OAAAA,EAAS,SAAA;;;AAGTC,oBAAAA,MAAAA,EAAQ1G,KAAAA,YAAiBuG,KAAAA,GAAQvG,KAAAA,CAAMqC,IAAI,GAAG;AAChD,iBAAA,CAAA;;AAGA,gBAAA,IAAIrC,iBAAiBuG,KAAAA,EAAO;oBAC1B,MAAMvG,KAAAA;AACR,gBAAA;gBACA,MAAM,IAAIuG,MAAM,wBAAA,EAA0B;oBAAEI,KAAAA,EAAO3G;AAAM,iBAAA,CAAA;AAC3D,YAAA;;;YAIA,MAAM4G,oBAAAA,GAAuB1E,WAAW,gBAAA,EAAkB;AAAEvC,gBAAAA;AAAO,aAAA,CAAA;AACnE,YAAA,IAAIkH,MAAAA,GAAS,IAAA;AACb,YAAA,IAAIC,WAAAA,GAA4B,IAAA;YAEhC,IAAI;AACF,gBAAA,MAAM,CAACC,SAAAA,EAAWC,WAAAA,CAAY,GAAG,MAAM7E,OAAAA,CAAQC,GAAG,CAAC;AACjDwE,oBAAAA,oBAAAA,CAAqBK,YAAY,CAAC;wBAAEC,OAAAA,EAAS;4BAAEnH,OAAAA,EAASK,SAAAA;4BAAWiB,IAAAA,EAAM;AAAU;AAAE,qBAAA,CAAA;AACrFuF,oBAAAA,oBAAAA,CAAqBK,YAAY,CAAC;wBAChCC,OAAAA,EAAS;4BAAEnH,OAAAA,EAASK,SAAAA;4BAAWiB,IAAAA,EAAM;AAAY;AACnD,qBAAA;AACD,iBAAA,CAAA;gBACDwF,MAAAA,GAAS;AAAEE,oBAAAA,SAAAA;AAAWC,oBAAAA;AAAY,iBAAA;AACpC,YAAA,CAAA,CAAE,OAAOG,GAAAA,EAAK;AACZL,gBAAAA,WAAAA,GAAcK,eAAeZ,KAAAA,GAAQY,GAAAA,GAAM,IAAIZ,KAAAA,CAAMa,OAAOD,GAAAA,CAAAA,EAAM;oBAAER,KAAAA,EAAOQ;AAAI,iBAAA,CAAA;gBAC/ExH,MAAAA,CAAOmG,GAAG,CAAC9F,KAAK,CACd,CAAC,qEAAqE,EAAEI,WAAW,EACnF;oBAAEJ,KAAAA,EAAO8G;AAAY,iBAAA,CAAA;AAEzB,YAAA;AAEA,YAAA,MAAMpE,SAAAA,CAAU;AAAE/C,gBAAAA;aAAO,EAAGgD,cAAAA,CAAe6D,eAAe,EAAE;AAC1DpG,gBAAAA,SAAAA,EAAWmF,cAAc5E,EAAE;AAC3B0B,gBAAAA,IAAAA,EAAMkD,cAAclD,IAAI;gBACxBoE,OAAAA,EAAS,SAAA;AACTM,gBAAAA,SAAAA,EAAWF,MAAAA,EAAQE,SAAAA;AACnBC,gBAAAA,WAAAA,EAAaH,MAAAA,EAAQG;AACvB,aAAA,CAAA;YAEA,OAAO;AAAEjH,gBAAAA,OAAAA;AAAS8G,gBAAAA,MAAAA;AAAQC,gBAAAA;AAAY,aAAA;AACxC,QAAA,CAAA;AAEA,QAAA,MAAM1C,qBAAoBhE,SAAwB,EAAA;YAChD,MAAMwG,oBAAAA,GAAuB1E,WAAW,gBAAA,EAAkB;AAAEvC,gBAAAA;AAAO,aAAA,CAAA;AAEnE,YAAA,MAAM,CAAC0H,YAAAA,EAAcC,cAAAA,CAAe,GAAG,MAAMnF,OAAAA,CAAQC,GAAG,CAAC;AACvDwE,gBAAAA,oBAAAA,CAAqBK,YAAY,CAAC;oBAChCC,OAAAA,EAAS;wBACPnH,OAAAA,EAASK;AACX;AACF,iBAAA,CAAA;AACAwG,gBAAAA,oBAAAA,CAAqBK,YAAY,CAAC;oBAChCC,OAAAA,EAAS;wBACPnH,OAAAA,EAASK,SAAAA;wBACTmH,YAAAA,EAAc;AAChB;AACF,iBAAA;AACD,aAAA,CAAA;AAED,YAAA,IAAIF,eAAe,CAAA,EAAG;AACpB,gBAAA,IAAIC,iBAAiB,CAAA,EAAG;AACtB,oBAAA,OAAO3H,OAAOW,EAAE,CAACC,KAAK,CAACgC,iBAAAA,CAAAA,CAAmBkB,MAAM,CAAC;wBAC/C/C,KAAAA,EAAO;4BACLC,EAAAA,EAAIP;AACN,yBAAA;wBACAoC,IAAAA,EAAM;4BACJC,MAAAA,EAAQ;AACV;AACF,qBAAA,CAAA;AACF,gBAAA;AAEA,gBAAA,OAAO9C,OAAOW,EAAE,CAACC,KAAK,CAACgC,iBAAAA,CAAAA,CAAmBkB,MAAM,CAAC;oBAC/C/C,KAAAA,EAAO;wBACLC,EAAAA,EAAIP;AACN,qBAAA;oBACAoC,IAAAA,EAAM;wBACJC,MAAAA,EAAQ;AACV;AACF,iBAAA,CAAA;AACF,YAAA;AAEA,YAAA,OAAO9C,OAAOW,EAAE,CAACC,KAAK,CAACgC,iBAAAA,CAAAA,CAAmBkB,MAAM,CAAC;gBAC/C/C,KAAAA,EAAO;oBACLC,EAAAA,EAAIP;AACN,iBAAA;gBACAoC,IAAAA,EAAM;oBACJC,MAAAA,EAAQ;AACV;AACF,aAAA,CAAA;AACF,QAAA;AACF,KAAA;AACF;;;;"}
|
|
@@ -20,6 +20,11 @@ const createSchedulingService = ({ strapi })=>{
|
|
|
20
20
|
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
21
21
|
}
|
|
22
22
|
const taskName = `publishRelease_${id}`;
|
|
23
|
+
// Cancel first: strapi.cron.remove(name) stops every job with that name,
|
|
24
|
+
// and Croner also rejects duplicate names if the scheduler assigns them.
|
|
25
|
+
if (scheduledJobs.has(id)) {
|
|
26
|
+
this.cancel(id);
|
|
27
|
+
}
|
|
23
28
|
strapi.cron.add({
|
|
24
29
|
[taskName]: {
|
|
25
30
|
async task () {
|
|
@@ -34,9 +39,6 @@ const createSchedulingService = ({ strapi })=>{
|
|
|
34
39
|
options: scheduleDate
|
|
35
40
|
}
|
|
36
41
|
});
|
|
37
|
-
if (scheduledJobs.has(id)) {
|
|
38
|
-
this.cancel(id);
|
|
39
|
-
}
|
|
40
42
|
scheduledJobs.set(id, taskName);
|
|
41
43
|
return scheduledJobs;
|
|
42
44
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scheduling.js","sources":["../../../server/src/services/scheduling.ts"],"sourcesContent":["import type { Core } from '@strapi/types';\n\nimport { errors } from '@strapi/utils';\nimport { Release } from '../../../shared/contracts/releases';\nimport { getService } from '../utils';\nimport { RELEASE_MODEL_UID } from '../constants';\nimport { runAsSystem } from '../audit-logs';\n\nconst createSchedulingService = ({ strapi }: { strapi: Core.Strapi }) => {\n const scheduledJobs = new Map<Release['id'], string>();\n\n return {\n async set(releaseId: Release['id'], scheduleDate: Date) {\n const id = String(releaseId); // callers pass both numeric (db row) and string (ctx.params) ids\n\n const release = await strapi.db\n .query(RELEASE_MODEL_UID)\n .findOne({ where: { id: releaseId, releasedAt: null } });\n\n if (!release) {\n throw new errors.NotFoundError(`No release found for id ${releaseId}`);\n }\n\n const taskName = `publishRelease_${id}`;\n\n strapi.cron.add({\n [taskName]: {\n async task() {\n const releaseService = getService('release', { strapi });\n\n // The system context attributes this run and its entry.publish/unpublish events to the scheduler.\n await runAsSystem({ strapi }, 'scheduler', () => releaseService.publish(releaseId));\n },\n options: scheduleDate,\n },\n });\n\n
|
|
1
|
+
{"version":3,"file":"scheduling.js","sources":["../../../server/src/services/scheduling.ts"],"sourcesContent":["import type { Core } from '@strapi/types';\n\nimport { errors } from '@strapi/utils';\nimport { Release } from '../../../shared/contracts/releases';\nimport { getService } from '../utils';\nimport { RELEASE_MODEL_UID } from '../constants';\nimport { runAsSystem } from '../audit-logs';\n\nconst createSchedulingService = ({ strapi }: { strapi: Core.Strapi }) => {\n const scheduledJobs = new Map<Release['id'], string>();\n\n return {\n async set(releaseId: Release['id'], scheduleDate: Date) {\n const id = String(releaseId); // callers pass both numeric (db row) and string (ctx.params) ids\n\n const release = await strapi.db\n .query(RELEASE_MODEL_UID)\n .findOne({ where: { id: releaseId, releasedAt: null } });\n\n if (!release) {\n throw new errors.NotFoundError(`No release found for id ${releaseId}`);\n }\n\n const taskName = `publishRelease_${id}`;\n\n // Cancel first: strapi.cron.remove(name) stops every job with that name,\n // and Croner also rejects duplicate names if the scheduler assigns them.\n if (scheduledJobs.has(id)) {\n this.cancel(id);\n }\n\n strapi.cron.add({\n [taskName]: {\n async task() {\n const releaseService = getService('release', { strapi });\n\n // The system context attributes this run and its entry.publish/unpublish events to the scheduler.\n await runAsSystem({ strapi }, 'scheduler', () => releaseService.publish(releaseId));\n },\n options: scheduleDate,\n },\n });\n\n scheduledJobs.set(id, taskName);\n\n return scheduledJobs;\n },\n\n cancel(releaseId: Release['id']) {\n const id = String(releaseId); // normalise to match key from set()\n\n if (scheduledJobs.has(id)) {\n strapi.cron.remove(scheduledJobs.get(id)!);\n scheduledJobs.delete(id);\n }\n\n return scheduledJobs;\n },\n\n getAll() {\n return scheduledJobs;\n },\n\n /**\n * On bootstrap, we can use this function to make sure to sync the scheduled jobs from the database that are not yet released\n * This is useful in case the server was restarted and the scheduled jobs were lost\n * This also could be used to sync different Strapi instances in case of a cluster\n */\n async syncFromDatabase() {\n const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({\n where: {\n scheduledAt: {\n $gte: new Date(),\n },\n releasedAt: null,\n },\n });\n\n for (const release of releases) {\n this.set(release.id, release.scheduledAt);\n }\n\n return scheduledJobs;\n },\n };\n};\n\nexport default createSchedulingService;\n"],"names":["createSchedulingService","strapi","scheduledJobs","Map","set","releaseId","scheduleDate","id","String","release","db","query","RELEASE_MODEL_UID","findOne","where","releasedAt","errors","NotFoundError","taskName","has","cancel","cron","add","task","releaseService","getService","runAsSystem","publish","options","remove","get","delete","getAll","syncFromDatabase","releases","findMany","scheduledAt","$gte","Date"],"mappings":";;;;;;;AAQA,MAAMA,uBAAAA,GAA0B,CAAC,EAAEC,MAAM,EAA2B,GAAA;AAClE,IAAA,MAAMC,gBAAgB,IAAIC,GAAAA,EAAAA;IAE1B,OAAO;QACL,MAAMC,GAAAA,CAAAA,CAAIC,SAAwB,EAAEC,YAAkB,EAAA;YACpD,MAAMC,EAAAA,GAAKC,MAAAA,CAAOH,SAAAA,CAAAA,CAAAA;YAElB,MAAMI,OAAAA,GAAU,MAAMR,MAAAA,CAAOS,EAAE,CAC5BC,KAAK,CAACC,2BAAAA,CAAAA,CACNC,OAAO,CAAC;gBAAEC,KAAAA,EAAO;oBAAEP,EAAAA,EAAIF,SAAAA;oBAAWU,UAAAA,EAAY;AAAK;AAAE,aAAA,CAAA;AAExD,YAAA,IAAI,CAACN,OAAAA,EAAS;AACZ,gBAAA,MAAM,IAAIO,YAAAA,CAAOC,aAAa,CAAC,CAAC,wBAAwB,EAAEZ,SAAAA,CAAAA,CAAW,CAAA;AACvE,YAAA;AAEA,YAAA,MAAMa,QAAAA,GAAW,CAAC,eAAe,EAAEX,EAAAA,CAAAA,CAAI;;;YAIvC,IAAIL,aAAAA,CAAciB,GAAG,CAACZ,EAAAA,CAAAA,EAAK;gBACzB,IAAI,CAACa,MAAM,CAACb,EAAAA,CAAAA;AACd,YAAA;YAEAN,MAAAA,CAAOoB,IAAI,CAACC,GAAG,CAAC;AACd,gBAAA,CAACJ,WAAW;oBACV,MAAMK,IAAAA,CAAAA,GAAAA;wBACJ,MAAMC,cAAAA,GAAiBC,iBAAW,SAAA,EAAW;AAAExB,4BAAAA;AAAO,yBAAA,CAAA;;AAGtD,wBAAA,MAAMyB,qBAAAA,CAAY;AAAEzB,4BAAAA;AAAO,yBAAA,EAAG,WAAA,EAAa,IAAMuB,cAAAA,CAAeG,OAAO,CAACtB,SAAAA,CAAAA,CAAAA;AAC1E,oBAAA,CAAA;oBACAuB,OAAAA,EAAStB;AACX;AACF,aAAA,CAAA;YAEAJ,aAAAA,CAAcE,GAAG,CAACG,EAAAA,EAAIW,QAAAA,CAAAA;YAEtB,OAAOhB,aAAAA;AACT,QAAA,CAAA;AAEAkB,QAAAA,MAAAA,CAAAA,CAAOf,SAAwB,EAAA;YAC7B,MAAME,EAAAA,GAAKC,MAAAA,CAAOH,SAAAA,CAAAA,CAAAA;YAElB,IAAIH,aAAAA,CAAciB,GAAG,CAACZ,EAAAA,CAAAA,EAAK;AACzBN,gBAAAA,MAAAA,CAAOoB,IAAI,CAACQ,MAAM,CAAC3B,aAAAA,CAAc4B,GAAG,CAACvB,EAAAA,CAAAA,CAAAA;AACrCL,gBAAAA,aAAAA,CAAc6B,MAAM,CAACxB,EAAAA,CAAAA;AACvB,YAAA;YAEA,OAAOL,aAAAA;AACT,QAAA,CAAA;AAEA8B,QAAAA,MAAAA,CAAAA,GAAAA;YACE,OAAO9B,aAAAA;AACT,QAAA,CAAA;AAEA;;;;AAIC,QACD,MAAM+B,gBAAAA,CAAAA,GAAAA;YACJ,MAAMC,QAAAA,GAAW,MAAMjC,MAAAA,CAAOS,EAAE,CAACC,KAAK,CAACC,2BAAAA,CAAAA,CAAmBuB,QAAQ,CAAC;gBACjErB,KAAAA,EAAO;oBACLsB,WAAAA,EAAa;AACXC,wBAAAA,IAAAA,EAAM,IAAIC,IAAAA;AACZ,qBAAA;oBACAvB,UAAAA,EAAY;AACd;AACF,aAAA,CAAA;YAEA,KAAK,MAAMN,WAAWyB,QAAAA,CAAU;AAC9B,gBAAA,IAAI,CAAC9B,GAAG,CAACK,QAAQF,EAAE,EAAEE,QAAQ2B,WAAW,CAAA;AAC1C,YAAA;YAEA,OAAOlC,aAAAA;AACT,QAAA;AACF,KAAA;AACF;;;;"}
|
|
@@ -18,6 +18,11 @@ const createSchedulingService = ({ strapi })=>{
|
|
|
18
18
|
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
19
19
|
}
|
|
20
20
|
const taskName = `publishRelease_${id}`;
|
|
21
|
+
// Cancel first: strapi.cron.remove(name) stops every job with that name,
|
|
22
|
+
// and Croner also rejects duplicate names if the scheduler assigns them.
|
|
23
|
+
if (scheduledJobs.has(id)) {
|
|
24
|
+
this.cancel(id);
|
|
25
|
+
}
|
|
21
26
|
strapi.cron.add({
|
|
22
27
|
[taskName]: {
|
|
23
28
|
async task () {
|
|
@@ -32,9 +37,6 @@ const createSchedulingService = ({ strapi })=>{
|
|
|
32
37
|
options: scheduleDate
|
|
33
38
|
}
|
|
34
39
|
});
|
|
35
|
-
if (scheduledJobs.has(id)) {
|
|
36
|
-
this.cancel(id);
|
|
37
|
-
}
|
|
38
40
|
scheduledJobs.set(id, taskName);
|
|
39
41
|
return scheduledJobs;
|
|
40
42
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scheduling.mjs","sources":["../../../server/src/services/scheduling.ts"],"sourcesContent":["import type { Core } from '@strapi/types';\n\nimport { errors } from '@strapi/utils';\nimport { Release } from '../../../shared/contracts/releases';\nimport { getService } from '../utils';\nimport { RELEASE_MODEL_UID } from '../constants';\nimport { runAsSystem } from '../audit-logs';\n\nconst createSchedulingService = ({ strapi }: { strapi: Core.Strapi }) => {\n const scheduledJobs = new Map<Release['id'], string>();\n\n return {\n async set(releaseId: Release['id'], scheduleDate: Date) {\n const id = String(releaseId); // callers pass both numeric (db row) and string (ctx.params) ids\n\n const release = await strapi.db\n .query(RELEASE_MODEL_UID)\n .findOne({ where: { id: releaseId, releasedAt: null } });\n\n if (!release) {\n throw new errors.NotFoundError(`No release found for id ${releaseId}`);\n }\n\n const taskName = `publishRelease_${id}`;\n\n strapi.cron.add({\n [taskName]: {\n async task() {\n const releaseService = getService('release', { strapi });\n\n // The system context attributes this run and its entry.publish/unpublish events to the scheduler.\n await runAsSystem({ strapi }, 'scheduler', () => releaseService.publish(releaseId));\n },\n options: scheduleDate,\n },\n });\n\n
|
|
1
|
+
{"version":3,"file":"scheduling.mjs","sources":["../../../server/src/services/scheduling.ts"],"sourcesContent":["import type { Core } from '@strapi/types';\n\nimport { errors } from '@strapi/utils';\nimport { Release } from '../../../shared/contracts/releases';\nimport { getService } from '../utils';\nimport { RELEASE_MODEL_UID } from '../constants';\nimport { runAsSystem } from '../audit-logs';\n\nconst createSchedulingService = ({ strapi }: { strapi: Core.Strapi }) => {\n const scheduledJobs = new Map<Release['id'], string>();\n\n return {\n async set(releaseId: Release['id'], scheduleDate: Date) {\n const id = String(releaseId); // callers pass both numeric (db row) and string (ctx.params) ids\n\n const release = await strapi.db\n .query(RELEASE_MODEL_UID)\n .findOne({ where: { id: releaseId, releasedAt: null } });\n\n if (!release) {\n throw new errors.NotFoundError(`No release found for id ${releaseId}`);\n }\n\n const taskName = `publishRelease_${id}`;\n\n // Cancel first: strapi.cron.remove(name) stops every job with that name,\n // and Croner also rejects duplicate names if the scheduler assigns them.\n if (scheduledJobs.has(id)) {\n this.cancel(id);\n }\n\n strapi.cron.add({\n [taskName]: {\n async task() {\n const releaseService = getService('release', { strapi });\n\n // The system context attributes this run and its entry.publish/unpublish events to the scheduler.\n await runAsSystem({ strapi }, 'scheduler', () => releaseService.publish(releaseId));\n },\n options: scheduleDate,\n },\n });\n\n scheduledJobs.set(id, taskName);\n\n return scheduledJobs;\n },\n\n cancel(releaseId: Release['id']) {\n const id = String(releaseId); // normalise to match key from set()\n\n if (scheduledJobs.has(id)) {\n strapi.cron.remove(scheduledJobs.get(id)!);\n scheduledJobs.delete(id);\n }\n\n return scheduledJobs;\n },\n\n getAll() {\n return scheduledJobs;\n },\n\n /**\n * On bootstrap, we can use this function to make sure to sync the scheduled jobs from the database that are not yet released\n * This is useful in case the server was restarted and the scheduled jobs were lost\n * This also could be used to sync different Strapi instances in case of a cluster\n */\n async syncFromDatabase() {\n const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({\n where: {\n scheduledAt: {\n $gte: new Date(),\n },\n releasedAt: null,\n },\n });\n\n for (const release of releases) {\n this.set(release.id, release.scheduledAt);\n }\n\n return scheduledJobs;\n },\n };\n};\n\nexport default createSchedulingService;\n"],"names":["createSchedulingService","strapi","scheduledJobs","Map","set","releaseId","scheduleDate","id","String","release","db","query","RELEASE_MODEL_UID","findOne","where","releasedAt","errors","NotFoundError","taskName","has","cancel","cron","add","task","releaseService","getService","runAsSystem","publish","options","remove","get","delete","getAll","syncFromDatabase","releases","findMany","scheduledAt","$gte","Date"],"mappings":";;;;;AAQA,MAAMA,uBAAAA,GAA0B,CAAC,EAAEC,MAAM,EAA2B,GAAA;AAClE,IAAA,MAAMC,gBAAgB,IAAIC,GAAAA,EAAAA;IAE1B,OAAO;QACL,MAAMC,GAAAA,CAAAA,CAAIC,SAAwB,EAAEC,YAAkB,EAAA;YACpD,MAAMC,EAAAA,GAAKC,MAAAA,CAAOH,SAAAA,CAAAA,CAAAA;YAElB,MAAMI,OAAAA,GAAU,MAAMR,MAAAA,CAAOS,EAAE,CAC5BC,KAAK,CAACC,iBAAAA,CAAAA,CACNC,OAAO,CAAC;gBAAEC,KAAAA,EAAO;oBAAEP,EAAAA,EAAIF,SAAAA;oBAAWU,UAAAA,EAAY;AAAK;AAAE,aAAA,CAAA;AAExD,YAAA,IAAI,CAACN,OAAAA,EAAS;AACZ,gBAAA,MAAM,IAAIO,MAAAA,CAAOC,aAAa,CAAC,CAAC,wBAAwB,EAAEZ,SAAAA,CAAAA,CAAW,CAAA;AACvE,YAAA;AAEA,YAAA,MAAMa,QAAAA,GAAW,CAAC,eAAe,EAAEX,EAAAA,CAAAA,CAAI;;;YAIvC,IAAIL,aAAAA,CAAciB,GAAG,CAACZ,EAAAA,CAAAA,EAAK;gBACzB,IAAI,CAACa,MAAM,CAACb,EAAAA,CAAAA;AACd,YAAA;YAEAN,MAAAA,CAAOoB,IAAI,CAACC,GAAG,CAAC;AACd,gBAAA,CAACJ,WAAW;oBACV,MAAMK,IAAAA,CAAAA,GAAAA;wBACJ,MAAMC,cAAAA,GAAiBC,WAAW,SAAA,EAAW;AAAExB,4BAAAA;AAAO,yBAAA,CAAA;;AAGtD,wBAAA,MAAMyB,WAAAA,CAAY;AAAEzB,4BAAAA;AAAO,yBAAA,EAAG,WAAA,EAAa,IAAMuB,cAAAA,CAAeG,OAAO,CAACtB,SAAAA,CAAAA,CAAAA;AAC1E,oBAAA,CAAA;oBACAuB,OAAAA,EAAStB;AACX;AACF,aAAA,CAAA;YAEAJ,aAAAA,CAAcE,GAAG,CAACG,EAAAA,EAAIW,QAAAA,CAAAA;YAEtB,OAAOhB,aAAAA;AACT,QAAA,CAAA;AAEAkB,QAAAA,MAAAA,CAAAA,CAAOf,SAAwB,EAAA;YAC7B,MAAME,EAAAA,GAAKC,MAAAA,CAAOH,SAAAA,CAAAA,CAAAA;YAElB,IAAIH,aAAAA,CAAciB,GAAG,CAACZ,EAAAA,CAAAA,EAAK;AACzBN,gBAAAA,MAAAA,CAAOoB,IAAI,CAACQ,MAAM,CAAC3B,aAAAA,CAAc4B,GAAG,CAACvB,EAAAA,CAAAA,CAAAA;AACrCL,gBAAAA,aAAAA,CAAc6B,MAAM,CAACxB,EAAAA,CAAAA;AACvB,YAAA;YAEA,OAAOL,aAAAA;AACT,QAAA,CAAA;AAEA8B,QAAAA,MAAAA,CAAAA,GAAAA;YACE,OAAO9B,aAAAA;AACT,QAAA,CAAA;AAEA;;;;AAIC,QACD,MAAM+B,gBAAAA,CAAAA,GAAAA;YACJ,MAAMC,QAAAA,GAAW,MAAMjC,MAAAA,CAAOS,EAAE,CAACC,KAAK,CAACC,iBAAAA,CAAAA,CAAmBuB,QAAQ,CAAC;gBACjErB,KAAAA,EAAO;oBACLsB,WAAAA,EAAa;AACXC,wBAAAA,IAAAA,EAAM,IAAIC,IAAAA;AACZ,qBAAA;oBACAvB,UAAAA,EAAY;AACd;AACF,aAAA,CAAA;YAEA,KAAK,MAAMN,WAAWyB,QAAAA,CAAU;AAC9B,gBAAA,IAAI,CAAC9B,GAAG,CAACK,QAAQF,EAAE,EAAEE,QAAQ2B,WAAW,CAAA;AAC1C,YAAA;YAEA,OAAOlC,aAAAA;AACT,QAAA;AACF,KAAA;AACF;;;;"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var utils = require('@strapi/utils');
|
|
3
4
|
var constants = require('../constants.js');
|
|
4
|
-
var auditLogs = require('../audit-logs.js');
|
|
5
5
|
|
|
6
6
|
const DEFAULT_SETTINGS = {
|
|
7
7
|
defaultTimezone: null
|
|
@@ -37,7 +37,7 @@ const createSettingsService = ({ strapi })=>{
|
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
if (Object.keys(changes).length > 0) {
|
|
40
|
-
await
|
|
40
|
+
await utils.emitAudit({
|
|
41
41
|
strapi
|
|
42
42
|
}, constants.AUDITED_EVENTS.RELEASE_SETTINGS_UPDATE, {
|
|
43
43
|
changes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings.js","sources":["../../../server/src/services/settings.ts"],"sourcesContent":["import type { Core } from '@strapi/types';\n\nimport type { Settings } from '../../../shared/contracts/settings';\nimport { AUDITED_EVENTS } from '../constants';\
|
|
1
|
+
{"version":3,"file":"settings.js","sources":["../../../server/src/services/settings.ts"],"sourcesContent":["import { emitAudit } from '@strapi/utils';\nimport type { Core } from '@strapi/types';\n\nimport type { Settings } from '../../../shared/contracts/settings';\nimport { AUDITED_EVENTS } from '../constants';\n\nconst DEFAULT_SETTINGS = {\n defaultTimezone: null,\n} satisfies Settings;\n\nconst createSettingsService = ({ strapi }: { strapi: Core.Strapi }) => {\n const getStore = async () => strapi.store({ type: 'core', name: 'content-releases' });\n\n return {\n async update({ settings }: { settings: Settings }): Promise<Settings> {\n const store = await getStore();\n const previous = await this.find();\n\n await store.set({ key: 'settings', value: settings });\n\n // Compare all fields so new settings are audited automatically.\n // No audit event is emitted when nothing changed.\n const keys = [\n ...new Set([...Object.keys(previous), ...Object.keys(settings)]),\n ] as (keyof Settings)[];\n const changes: Partial<Record<keyof Settings, { before: unknown; after: unknown }>> = {};\n\n for (const key of keys) {\n if ((previous[key] ?? null) !== (settings[key] ?? null)) {\n changes[key] = { before: previous[key] ?? null, after: settings[key] ?? null };\n }\n }\n\n if (Object.keys(changes).length > 0) {\n await emitAudit({ strapi }, AUDITED_EVENTS.RELEASE_SETTINGS_UPDATE, { changes });\n }\n\n return settings;\n },\n async find(): Promise<Settings> {\n const store = await getStore();\n const settings = (await store.get({ key: 'settings' })) as Settings | undefined;\n\n return {\n ...DEFAULT_SETTINGS,\n ...settings,\n };\n },\n };\n};\n\nexport type SettingsService = ReturnType<typeof createSettingsService>;\n\nexport default createSettingsService;\n"],"names":["DEFAULT_SETTINGS","defaultTimezone","createSettingsService","strapi","getStore","store","type","name","update","settings","previous","find","set","key","value","keys","Set","Object","changes","before","after","length","emitAudit","AUDITED_EVENTS","RELEASE_SETTINGS_UPDATE","get"],"mappings":";;;;;AAMA,MAAMA,gBAAAA,GAAmB;IACvBC,eAAAA,EAAiB;AACnB,CAAA;AAEA,MAAMC,qBAAAA,GAAwB,CAAC,EAAEC,MAAM,EAA2B,GAAA;AAChE,IAAA,MAAMC,QAAAA,GAAW,UAAYD,MAAAA,CAAOE,KAAK,CAAC;YAAEC,IAAAA,EAAM,MAAA;YAAQC,IAAAA,EAAM;AAAmB,SAAA,CAAA;IAEnF,OAAO;QACL,MAAMC,MAAAA,CAAAA,CAAO,EAAEC,QAAQ,EAA0B,EAAA;AAC/C,YAAA,MAAMJ,QAAQ,MAAMD,QAAAA,EAAAA;AACpB,YAAA,MAAMM,QAAAA,GAAW,MAAM,IAAI,CAACC,IAAI,EAAA;YAEhC,MAAMN,KAAAA,CAAMO,GAAG,CAAC;gBAAEC,GAAAA,EAAK,UAAA;gBAAYC,KAAAA,EAAOL;AAAS,aAAA,CAAA;;;AAInD,YAAA,MAAMM,IAAAA,GAAO;AACR,gBAAA,GAAA,IAAIC,GAAAA,CAAI;AAAIC,oBAAAA,GAAAA,MAAAA,CAAOF,IAAI,CAACL,QAAAA,CAAAA;AAAcO,oBAAAA,GAAAA,MAAAA,CAAOF,IAAI,CAACN,QAAAA;AAAU,iBAAA;AAChE,aAAA;AACD,YAAA,MAAMS,UAAgF,EAAC;YAEvF,KAAK,MAAML,OAAOE,IAAAA,CAAM;AACtB,gBAAA,IAAI,CAACL,QAAQ,CAACG,IAAI,IAAI,IAAG,OAAQJ,QAAQ,CAACI,GAAAA,CAAI,IAAI,IAAG,CAAA,EAAI;oBACvDK,OAAO,CAACL,IAAI,GAAG;wBAAEM,MAAAA,EAAQT,QAAQ,CAACG,GAAAA,CAAI,IAAI,IAAA;wBAAMO,KAAAA,EAAOX,QAAQ,CAACI,GAAAA,CAAI,IAAI;AAAK,qBAAA;AAC/E,gBAAA;AACF,YAAA;AAEA,YAAA,IAAII,OAAOF,IAAI,CAACG,OAAAA,CAAAA,CAASG,MAAM,GAAG,CAAA,EAAG;AACnC,gBAAA,MAAMC,eAAAA,CAAU;AAAEnB,oBAAAA;iBAAO,EAAGoB,wBAAAA,CAAeC,uBAAuB,EAAE;AAAEN,oBAAAA;AAAQ,iBAAA,CAAA;AAChF,YAAA;YAEA,OAAOT,QAAAA;AACT,QAAA,CAAA;QACA,MAAME,IAAAA,CAAAA,GAAAA;AACJ,YAAA,MAAMN,QAAQ,MAAMD,QAAAA,EAAAA;AACpB,YAAA,MAAMK,QAAAA,GAAY,MAAMJ,KAAAA,CAAMoB,GAAG,CAAC;gBAAEZ,GAAAA,EAAK;AAAW,aAAA,CAAA;YAEpD,OAAO;AACL,gBAAA,GAAGb,gBAAgB;AACnB,gBAAA,GAAGS;AACL,aAAA;AACF,QAAA;AACF,KAAA;AACF;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings.mjs","sources":["../../../server/src/services/settings.ts"],"sourcesContent":["import type { Core } from '@strapi/types';\n\nimport type { Settings } from '../../../shared/contracts/settings';\nimport { AUDITED_EVENTS } from '../constants';\
|
|
1
|
+
{"version":3,"file":"settings.mjs","sources":["../../../server/src/services/settings.ts"],"sourcesContent":["import { emitAudit } from '@strapi/utils';\nimport type { Core } from '@strapi/types';\n\nimport type { Settings } from '../../../shared/contracts/settings';\nimport { AUDITED_EVENTS } from '../constants';\n\nconst DEFAULT_SETTINGS = {\n defaultTimezone: null,\n} satisfies Settings;\n\nconst createSettingsService = ({ strapi }: { strapi: Core.Strapi }) => {\n const getStore = async () => strapi.store({ type: 'core', name: 'content-releases' });\n\n return {\n async update({ settings }: { settings: Settings }): Promise<Settings> {\n const store = await getStore();\n const previous = await this.find();\n\n await store.set({ key: 'settings', value: settings });\n\n // Compare all fields so new settings are audited automatically.\n // No audit event is emitted when nothing changed.\n const keys = [\n ...new Set([...Object.keys(previous), ...Object.keys(settings)]),\n ] as (keyof Settings)[];\n const changes: Partial<Record<keyof Settings, { before: unknown; after: unknown }>> = {};\n\n for (const key of keys) {\n if ((previous[key] ?? null) !== (settings[key] ?? null)) {\n changes[key] = { before: previous[key] ?? null, after: settings[key] ?? null };\n }\n }\n\n if (Object.keys(changes).length > 0) {\n await emitAudit({ strapi }, AUDITED_EVENTS.RELEASE_SETTINGS_UPDATE, { changes });\n }\n\n return settings;\n },\n async find(): Promise<Settings> {\n const store = await getStore();\n const settings = (await store.get({ key: 'settings' })) as Settings | undefined;\n\n return {\n ...DEFAULT_SETTINGS,\n ...settings,\n };\n },\n };\n};\n\nexport type SettingsService = ReturnType<typeof createSettingsService>;\n\nexport default createSettingsService;\n"],"names":["DEFAULT_SETTINGS","defaultTimezone","createSettingsService","strapi","getStore","store","type","name","update","settings","previous","find","set","key","value","keys","Set","Object","changes","before","after","length","emitAudit","AUDITED_EVENTS","RELEASE_SETTINGS_UPDATE","get"],"mappings":";;;AAMA,MAAMA,gBAAAA,GAAmB;IACvBC,eAAAA,EAAiB;AACnB,CAAA;AAEA,MAAMC,qBAAAA,GAAwB,CAAC,EAAEC,MAAM,EAA2B,GAAA;AAChE,IAAA,MAAMC,QAAAA,GAAW,UAAYD,MAAAA,CAAOE,KAAK,CAAC;YAAEC,IAAAA,EAAM,MAAA;YAAQC,IAAAA,EAAM;AAAmB,SAAA,CAAA;IAEnF,OAAO;QACL,MAAMC,MAAAA,CAAAA,CAAO,EAAEC,QAAQ,EAA0B,EAAA;AAC/C,YAAA,MAAMJ,QAAQ,MAAMD,QAAAA,EAAAA;AACpB,YAAA,MAAMM,QAAAA,GAAW,MAAM,IAAI,CAACC,IAAI,EAAA;YAEhC,MAAMN,KAAAA,CAAMO,GAAG,CAAC;gBAAEC,GAAAA,EAAK,UAAA;gBAAYC,KAAAA,EAAOL;AAAS,aAAA,CAAA;;;AAInD,YAAA,MAAMM,IAAAA,GAAO;AACR,gBAAA,GAAA,IAAIC,GAAAA,CAAI;AAAIC,oBAAAA,GAAAA,MAAAA,CAAOF,IAAI,CAACL,QAAAA,CAAAA;AAAcO,oBAAAA,GAAAA,MAAAA,CAAOF,IAAI,CAACN,QAAAA;AAAU,iBAAA;AAChE,aAAA;AACD,YAAA,MAAMS,UAAgF,EAAC;YAEvF,KAAK,MAAML,OAAOE,IAAAA,CAAM;AACtB,gBAAA,IAAI,CAACL,QAAQ,CAACG,IAAI,IAAI,IAAG,OAAQJ,QAAQ,CAACI,GAAAA,CAAI,IAAI,IAAG,CAAA,EAAI;oBACvDK,OAAO,CAACL,IAAI,GAAG;wBAAEM,MAAAA,EAAQT,QAAQ,CAACG,GAAAA,CAAI,IAAI,IAAA;wBAAMO,KAAAA,EAAOX,QAAQ,CAACI,GAAAA,CAAI,IAAI;AAAK,qBAAA;AAC/E,gBAAA;AACF,YAAA;AAEA,YAAA,IAAII,OAAOF,IAAI,CAACG,OAAAA,CAAAA,CAASG,MAAM,GAAG,CAAA,EAAG;AACnC,gBAAA,MAAMC,SAAAA,CAAU;AAAEnB,oBAAAA;iBAAO,EAAGoB,cAAAA,CAAeC,uBAAuB,EAAE;AAAEN,oBAAAA;AAAQ,iBAAA,CAAA;AAChF,YAAA;YAEA,OAAOT,QAAAA;AACT,QAAA,CAAA;QACA,MAAME,IAAAA,CAAAA,GAAAA;AACJ,YAAA,MAAMN,QAAQ,MAAMD,QAAAA,EAAAA;AACpB,YAAA,MAAMK,QAAAA,GAAY,MAAMJ,KAAAA,CAAMoB,GAAG,CAAC;gBAAEZ,GAAAA,EAAK;AAAW,aAAA,CAAA;YAEpD,OAAO;AACL,gBAAA,GAAGb,gBAAgB;AACnB,gBAAA,GAAGS;AACL,aAAA;AACF,QAAA;AACF,KAAA;AACF;;;;"}
|
|
@@ -4,14 +4,10 @@ export interface CreateDetails {
|
|
|
4
4
|
scheduledAt?: string | null;
|
|
5
5
|
timezone?: string | null;
|
|
6
6
|
}
|
|
7
|
-
export interface FieldChange {
|
|
8
|
-
before: string | boolean | null;
|
|
9
|
-
after: string | boolean | null;
|
|
10
|
-
}
|
|
11
7
|
export declare const RELEASE_EDITABLE_FIELDS: readonly ["name", "scheduledAt", "timezone"];
|
|
12
8
|
type ReleaseChangedField = (typeof RELEASE_EDITABLE_FIELDS)[number] | 'isScheduled';
|
|
13
9
|
export interface UpdateDetails {
|
|
14
|
-
changes: Partial<Record<ReleaseChangedField, FieldChange>>;
|
|
10
|
+
changes: Partial<Record<ReleaseChangedField, Modules.AuditLogs.FieldChange>>;
|
|
15
11
|
}
|
|
16
12
|
/**
|
|
17
13
|
* Returns the fields that changed in a release update.
|
|
@@ -25,7 +21,7 @@ export declare const getReleaseChanges: (previous: {
|
|
|
25
21
|
name?: string;
|
|
26
22
|
scheduledAt?: string | null;
|
|
27
23
|
timezone?: string | null;
|
|
28
|
-
}) => Partial<Record<ReleaseChangedField, FieldChange
|
|
24
|
+
}) => Partial<Record<ReleaseChangedField, Modules.AuditLogs.FieldChange<string | number | boolean | null>>>;
|
|
29
25
|
export interface TriggerDetails {
|
|
30
26
|
published: number;
|
|
31
27
|
unpublished: number;
|
|
@@ -43,7 +39,7 @@ export interface TriggerFailureDetails {
|
|
|
43
39
|
failed?: number;
|
|
44
40
|
}
|
|
45
41
|
export interface SettingsUpdateDetails {
|
|
46
|
-
changes: Partial<Record<'defaultTimezone', FieldChange>>;
|
|
42
|
+
changes: Partial<Record<'defaultTimezone', Modules.AuditLogs.FieldChange>>;
|
|
47
43
|
}
|
|
48
44
|
interface EntryRef {
|
|
49
45
|
contentType: string;
|
|
@@ -64,14 +60,6 @@ export interface EntryUpdateDetails {
|
|
|
64
60
|
export interface EntryRemoveDetails {
|
|
65
61
|
entry: EntryRef;
|
|
66
62
|
}
|
|
67
|
-
/**
|
|
68
|
-
* Emits an audit event and waits for it to be processed.
|
|
69
|
-
* A failed audit write, or a failing listener, is logged here and doesn't affect the
|
|
70
|
-
* operation that emitted the event.
|
|
71
|
-
*/
|
|
72
|
-
export declare const emitAudit: ({ strapi }: {
|
|
73
|
-
strapi: Core.Strapi;
|
|
74
|
-
}, event: string, payload: unknown) => Promise<void>;
|
|
75
63
|
/**
|
|
76
64
|
* Emits an audit event for a write that may be part of a bulk transaction.
|
|
77
65
|
* Inside one, the event waits for its commit, so a rollback leaves no rows for
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit-logs.d.ts","sourceRoot":"","sources":["../../../server/src/audit-logs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"audit-logs.d.ts","sourceRoot":"","sources":["../../../server/src/audit-logs.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scheduling.d.ts","sourceRoot":"","sources":["../../../../server/src/services/scheduling.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAG1C,OAAO,EAAE,OAAO,EAAE,MAAM,oCAAoC,CAAC;AAK7D,QAAA,MAAM,uBAAuB,GAAI,YAAY;IAAE,MAAM,EAAE,IAAI,CAAC,MAAM,CAAA;CAAE;mBAI3C,OAAO,CAAC,IAAI,CAAC,gBAAgB,IAAI;
|
|
1
|
+
{"version":3,"file":"scheduling.d.ts","sourceRoot":"","sources":["../../../../server/src/services/scheduling.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAG1C,OAAO,EAAE,OAAO,EAAE,MAAM,oCAAoC,CAAC;AAK7D,QAAA,MAAM,uBAAuB,GAAI,YAAY;IAAE,MAAM,EAAE,IAAI,CAAC,MAAM,CAAA;CAAE;mBAI3C,OAAO,CAAC,IAAI,CAAC,gBAAgB,IAAI;sBAoCpC,OAAO,CAAC,IAAI,CAAC;;IAe/B;;;;OAIG;;CAkBN,CAAC;AAEF,eAAe,uBAAuB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../../../server/src/services/settings.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../../../server/src/services/settings.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAE1C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oCAAoC,CAAC;AAOnE,QAAA,MAAM,qBAAqB,GAAI,YAAY;IAAE,MAAM,EAAE,IAAI,CAAC,MAAM,CAAA;CAAE;yBAInC;QAAE,QAAQ,EAAE,QAAQ,CAAA;KAAE,GAAG,OAAO,CAAC,QAAQ,CAAC;YAyBvD,OAAO,CAAC,QAAQ,CAAC;CAUlC,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG,UAAU,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEvE,eAAe,qBAAqB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@strapi/content-releases",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.55.0",
|
|
4
4
|
"description": "Strapi plugin for organizing and releasing content",
|
|
5
5
|
"homepage": "https://strapi.io",
|
|
6
6
|
"bugs": {
|
|
@@ -64,11 +64,11 @@
|
|
|
64
64
|
},
|
|
65
65
|
"dependencies": {
|
|
66
66
|
"@reduxjs/toolkit": "1.9.7",
|
|
67
|
-
"@strapi/database": "5.
|
|
67
|
+
"@strapi/database": "5.55.0",
|
|
68
68
|
"@strapi/design-system": "2.2.4",
|
|
69
69
|
"@strapi/icons": "2.2.4",
|
|
70
|
-
"@strapi/types": "5.
|
|
71
|
-
"@strapi/utils": "5.
|
|
70
|
+
"@strapi/types": "5.55.0",
|
|
71
|
+
"@strapi/utils": "5.55.0",
|
|
72
72
|
"date-fns": "2.30.0",
|
|
73
73
|
"date-fns-tz": "2.0.1",
|
|
74
74
|
"formik": "2.4.9",
|
|
@@ -79,9 +79,9 @@
|
|
|
79
79
|
"yup": "0.32.9"
|
|
80
80
|
},
|
|
81
81
|
"devDependencies": {
|
|
82
|
-
"@strapi/admin": "5.
|
|
83
|
-
"@strapi/admin-test-utils": "5.
|
|
84
|
-
"@strapi/content-manager": "5.
|
|
82
|
+
"@strapi/admin": "5.55.0",
|
|
83
|
+
"@strapi/admin-test-utils": "5.55.0",
|
|
84
|
+
"@strapi/content-manager": "5.55.0",
|
|
85
85
|
"@testing-library/dom": "10.4.1",
|
|
86
86
|
"@testing-library/jest-dom": "6.9.1",
|
|
87
87
|
"@testing-library/react": "16.3.2",
|