diff --git a/.secretlintrc.json b/.secretlintrc.json index fcef33c49ab..abec5ba6fd3 100644 --- a/.secretlintrc.json +++ b/.secretlintrc.json @@ -16,7 +16,7 @@ { "name": "credential in URL query string", "patterns": [ - "/[?&](?:token|api[_-]?key|access[_-]?token|auth[_-]?token|client[_-]?secret|secret|password)=(?(?!p\\.)[^&\\s\"'<>]{16,})/i" + "/[?&](?:token|api[_-]?key|access[_-]?token|auth[_-]?token|client[_-]?secret|secret|password)=(?(?!p\\.|\\$\\{)[^&\\s\"'<>]{16,})/i" ] }, { diff --git a/ghost/core/core/server/api/endpoints/automations.js b/ghost/core/core/server/api/endpoints/automations.js index 71b56e7ca3f..30c6cff71b8 100644 --- a/ghost/core/core/server/api/endpoints/automations.js +++ b/ghost/core/core/server/api/endpoints/automations.js @@ -24,6 +24,49 @@ const controller = { } }, + read: { + headers: { + cacheInvalidate: false + }, + data: [ + 'id' + ], + permissions: true, + query(frame) { + // TODO: NY-1265 - replace this static payload with persisted automation data. + return { + id: frame.data.id, + slug: 'member-welcome-email-free', + name: 'Welcome email', + status: 'active', + created_at: '2026-05-05T00:00:00.000Z', + updated_at: '2026-05-05T00:00:00.000Z', + actions: [{ + id: '67f3f3f3f3f3f3f3f3f3f3f4', + type: 'delay', + data: { + delay_hours: 24 + } + }, { + id: '67f3f3f3f3f3f3f3f3f3f3f5', + type: 'send email', + data: { + email_subject: 'Welcome!', + email_lexical: '{"root":{"children":[]}}', + email_sender_name: null, + email_sender_email: null, + email_sender_reply_to: null, + email_design_setting_id: '680000000000000000000001' + } + }], + edges: [{ + source_action_id: '67f3f3f3f3f3f3f3f3f3f3f4', + target_action_id: '67f3f3f3f3f3f3f3f3f3f3f5' + }] + }; + } + }, + poll: { statusCode: 204, headers: { diff --git a/ghost/core/core/server/web/api/endpoints/admin/routes.js b/ghost/core/core/server/web/api/endpoints/admin/routes.js index e3a45181dab..2c796cc10f2 100644 --- a/ghost/core/core/server/web/api/endpoints/admin/routes.js +++ b/ghost/core/core/server/web/api/endpoints/admin/routes.js @@ -187,6 +187,7 @@ module.exports = function apiRoutes() { // ## Automations router.get('/automations', mw.authAdminApi, http(api.automations.browse)); + router.get('/automations/:id', mw.authAdminApi, http(api.automations.read)); router.put('/automations/poll', mw.authAdminApiWithUrl, http(api.automations.poll)); // ## Automated Emails diff --git a/ghost/core/test/e2e-api/admin/__snapshots__/automations.test.js.snap b/ghost/core/test/e2e-api/admin/__snapshots__/automations.test.js.snap index c3e686e3ae2..8b0f0b7ffb0 100644 --- a/ghost/core/test/e2e-api/admin/__snapshots__/automations.test.js.snap +++ b/ghost/core/test/e2e-api/admin/__snapshots__/automations.test.js.snap @@ -72,3 +72,58 @@ Object { "x-powered-by": "Express", } `; + +exports[`Automations API read returns a placeholder automation for the requested id 1: [body] 1`] = ` +Object { + "automations": Array [ + Object { + "actions": Array [ + Object { + "data": Object { + "delay_hours": 24, + }, + "id": "67f3f3f3f3f3f3f3f3f3f3f4", + "type": "delay", + }, + Object { + "data": Object { + "email_design_setting_id": "680000000000000000000001", + "email_lexical": "{\\"root\\":{\\"children\\":[]}}", + "email_sender_email": null, + "email_sender_name": null, + "email_sender_reply_to": null, + "email_subject": "Welcome!", + }, + "id": "67f3f3f3f3f3f3f3f3f3f3f5", + "type": "send email", + }, + ], + "created_at": "2026-05-05T00:00:00.000Z", + "edges": Array [ + Object { + "source_action_id": "67f3f3f3f3f3f3f3f3f3f3f4", + "target_action_id": "67f3f3f3f3f3f3f3f3f3f3f5", + }, + ], + "id": "67f3f3f3f3f3f3f3f3f3f3f3", + "name": "Welcome email", + "slug": "member-welcome-email-free", + "status": "active", + "updated_at": "2026-05-05T00:00:00.000Z", + }, + ], +} +`; + +exports[`Automations API read returns a placeholder automation for the requested id 2: [headers] 1`] = ` +Object { + "access-control-allow-origin": "http://127.0.0.1:2369", + "cache-control": "no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0", + "content-length": "668", + "content-type": "application/json; charset=utf-8", + "content-version": StringMatching /v\\\\d\\+\\\\\\.\\\\d\\+/, + "etag": StringMatching /\\(\\?:W\\\\/\\)\\?"\\(\\?:\\[ !#-\\\\x7E\\\\x80-\\\\xFF\\]\\*\\|\\\\r\\\\n\\[\\\\t \\]\\|\\\\\\\\\\.\\)\\*"/, + "vary": "Accept-Version, Origin, Accept-Encoding", + "x-powered-by": "Express", +} +`; diff --git a/ghost/core/test/e2e-api/admin/automations.test.js b/ghost/core/test/e2e-api/admin/automations.test.js index 063069e0b68..240d3162de9 100644 --- a/ghost/core/test/e2e-api/admin/automations.test.js +++ b/ghost/core/test/e2e-api/admin/automations.test.js @@ -73,6 +73,22 @@ describe('Automations API', function () { }); }); + describe('read', function () { + it('returns a placeholder automation for the requested id', async function () { + const automationId = '67f3f3f3f3f3f3f3f3f3f3f3'; + + await agent + .get(`automations/${automationId}`) + .expectStatus(200) + .expect(cacheInvalidateHeaderNotSet()) + .matchBodySnapshot() + .matchHeaderSnapshot({ + 'content-version': anyContentVersion, + etag: anyEtag + }); + }); + }); + describe('poll', function () { /** @type {sinon.SinonStub} */ let dispatchStub; diff --git a/ghost/core/test/unit/api/endpoints/automations.test.js b/ghost/core/test/unit/api/endpoints/automations.test.js index 2d4fa98304f..f06b69a4aff 100644 --- a/ghost/core/test/unit/api/endpoints/automations.test.js +++ b/ghost/core/test/unit/api/endpoints/automations.test.js @@ -62,6 +62,47 @@ describe('Automations controller', function () { }); }); + describe('read', function () { + it('returns a placeholder automation for the requested id', function () { + const result = automationsController.read.query({ + data: { + id: '67f3f3f3f3f3f3f3f3f3f3f3' + } + }); + + assert.deepEqual(result, { + id: '67f3f3f3f3f3f3f3f3f3f3f3', + slug: 'member-welcome-email-free', + name: 'Welcome email', + status: 'active', + created_at: '2026-05-05T00:00:00.000Z', + updated_at: '2026-05-05T00:00:00.000Z', + actions: [{ + id: '67f3f3f3f3f3f3f3f3f3f3f4', + type: 'delay', + data: { + delay_hours: 24 + } + }, { + id: '67f3f3f3f3f3f3f3f3f3f3f5', + type: 'send email', + data: { + email_subject: 'Welcome!', + email_lexical: '{"root":{"children":[]}}', + email_sender_name: null, + email_sender_email: null, + email_sender_reply_to: null, + email_design_setting_id: '680000000000000000000001' + } + }], + edges: [{ + source_action_id: '67f3f3f3f3f3f3f3f3f3f3f4', + target_action_id: '67f3f3f3f3f3f3f3f3f3f3f5' + }] + }); + }); + }); + describe('poll', function () { it('dispatches a StartAutomationsPollEvent', function () { const result = automationsController.poll.query({});