From e1c11bd0c70d93b44127f3b61d3b10f672fc358b Mon Sep 17 00:00:00 2001 From: yaroslav8765 Date: Tue, 6 Jan 2026 11:19:32 +0200 Subject: [PATCH 1/2] feat: add possobolity to set cookies from the before/after save hooks --- adminforth/index.ts | 21 ++++++++++++++------- adminforth/modules/restApi.ts | 18 +++++++++--------- adminforth/types/Back.ts | 14 ++++++++++---- 3 files changed, 33 insertions(+), 20 deletions(-) diff --git a/adminforth/index.ts b/adminforth/index.ts index 25b48752c..5d748b338 100644 --- a/adminforth/index.ts +++ b/adminforth/index.ts @@ -20,6 +20,7 @@ import { HttpExtra, BeforeCreateSaveFunction, AdminForthInputConfig, + IAdminForthHttpResponse, } from './types/Back.js'; import { @@ -538,8 +539,8 @@ class AdminForth implements IAdminForth { } async createResourceRecord( - { resource, record, adminUser, extra }: - { resource: AdminForthResource, record: any, adminUser: AdminUser, extra?: HttpExtra } + { resource, record, adminUser, extra, response }: + { resource: AdminForthResource, record: any, adminUser: AdminUser, extra?: HttpExtra, response: IAdminForthHttpResponse } ): Promise<{ error?: string, createdRecord?: any, newRecordId?: any }> { const err = this.validateRecordValues(resource, record, 'create'); @@ -557,6 +558,7 @@ class AdminForth implements IAdminForth { record, adminUser, adminforth: this, + response, extra, }); if (!resp || (typeof resp.ok !== 'boolean' && (!resp.error && !resp.newRecordId))) { @@ -602,6 +604,7 @@ class AdminForth implements IAdminForth { adminUser, adminforth: this, recordWithVirtualColumns, + response, extra, }); @@ -621,9 +624,9 @@ class AdminForth implements IAdminForth { * record is partial record with only changed fields */ async updateResourceRecord( - { resource, recordId, record, oldRecord, adminUser, extra, updates }: - | { resource: AdminForthResource, recordId: any, record: any, oldRecord: any, adminUser: AdminUser, extra?: HttpExtra, updates?: never } - | { resource: AdminForthResource, recordId: any, record?: never, oldRecord: any, adminUser: AdminUser, extra?: HttpExtra, updates: any } + { resource, recordId, record, oldRecord, adminUser, response, extra, updates }: + | { resource: AdminForthResource, recordId: any, record: any, oldRecord: any, adminUser: AdminUser, response: IAdminForthHttpResponse, extra?: HttpExtra, updates?: never } + | { resource: AdminForthResource, recordId: any, record?: never, oldRecord: any, adminUser: AdminUser, response: IAdminForthHttpResponse, extra?: HttpExtra, updates: any } ): Promise<{ error?: string }> { const dataToUse = updates || record; const err = this.validateRecordValues(resource, dataToUse, 'edit'); @@ -651,6 +654,7 @@ class AdminForth implements IAdminForth { oldRecord, adminUser, adminforth: this, + response, extra, }); if (!resp || typeof resp.ok !== 'boolean') { @@ -695,6 +699,7 @@ class AdminForth implements IAdminForth { oldRecord, recordId, adminforth: this, + response, extra, }); if (!resp || (!resp.ok && !resp.error)) { @@ -709,8 +714,8 @@ class AdminForth implements IAdminForth { } async deleteResourceRecord( - { resource, recordId, adminUser, record, extra }: - { resource: AdminForthResource, recordId: any, adminUser: AdminUser, record: any, extra?: HttpExtra } + { resource, recordId, adminUser, record, response, extra }: + { resource: AdminForthResource, recordId: any, adminUser: AdminUser, record: any, response: IAdminForthHttpResponse, extra?: HttpExtra } ): Promise<{ error?: string }> { // execute hook if needed for (const hook of listify(resource.hooks?.delete?.beforeSave)) { @@ -720,6 +725,7 @@ class AdminForth implements IAdminForth { adminUser, recordId, adminforth: this, + response, extra, }); if (!resp || (!resp.ok && !resp.error)) { @@ -742,6 +748,7 @@ class AdminForth implements IAdminForth { adminUser, recordId, adminforth: this, + response, extra, }); if (!resp || (!resp.ok && !resp.error)) { diff --git a/adminforth/modules/restApi.ts b/adminforth/modules/restApi.ts index 7b90eb58b..b5b9f143e 100644 --- a/adminforth/modules/restApi.ts +++ b/adminforth/modules/restApi.ts @@ -1162,7 +1162,7 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI { server.endpoint({ method: 'POST', path: '/create_record', - handler: async ({ body, adminUser, query, headers, cookies, requestUrl }) => { + handler: async ({ body, adminUser, query, headers, cookies, requestUrl, response }) => { const resource = this.adminforth.config.resources.find((res) => res.resourceId == body['resourceId']); if (!resource) { return { error: `Resource '${body['resourceId']}' not found` }; @@ -1276,14 +1276,14 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI { } } - const response = await this.adminforth.createResourceRecord({ resource, record, adminUser, extra: { body, query, headers, cookies, requestUrl } }); - if (response.error) { - return { error: response.error, ok: false, newRecordId: response.newRecordId }; + const createRecordResponse = await this.adminforth.createResourceRecord({ resource, record, adminUser, response, extra: { body, query, headers, cookies, requestUrl } }); + if (createRecordResponse.error) { + return { error: createRecordResponse.error, ok: false, newRecordId: createRecordResponse.newRecordId }; } const connector = this.adminforth.connectors[resource.dataSource]; return { - newRecordId: response.createdRecord[connector.getPrimaryKey(resource)], + newRecordId: createRecordResponse.createdRecord[connector.getPrimaryKey(resource)], ok: true } } @@ -1291,7 +1291,7 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI { server.endpoint({ method: 'POST', path: '/update_record', - handler: async ({ body, adminUser, query, headers, cookies, requestUrl }) => { + handler: async ({ body, adminUser, query, headers, cookies, requestUrl, response }) => { const resource = this.adminforth.config.resources.find((res) => res.resourceId == body['resourceId']); if (!resource) { return { error: `Resource '${body['resourceId']}' not found` }; @@ -1396,7 +1396,7 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI { } } - const { error } = await this.adminforth.updateResourceRecord({ resource, record, adminUser, oldRecord, recordId, extra: { body, query, headers, cookies, requestUrl} }); + const { error } = await this.adminforth.updateResourceRecord({ resource, record, adminUser, oldRecord, recordId, response, extra: { body, query, headers, cookies, requestUrl} }); if (error) { return { error }; } @@ -1409,7 +1409,7 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI { server.endpoint({ method: 'POST', path: '/delete_record', - handler: async ({ body, adminUser, query, headers, cookies, requestUrl }) => { + handler: async ({ body, adminUser, query, headers, cookies, requestUrl, response }) => { const resource = this.adminforth.config.resources.find((res) => res.resourceId == body['resourceId']); const record = await this.adminforth.connectors[resource.dataSource].getRecordByPrimaryKey(resource, body['primaryKey']); if (!resource) { @@ -1435,7 +1435,7 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI { return { error }; } - const { error: deleteError } = await this.adminforth.deleteResourceRecord({ resource, record, adminUser, recordId: body['primaryKey'], extra: { body, query, headers, cookies, requestUrl } }); + const { error: deleteError } = await this.adminforth.deleteResourceRecord({ resource, record, adminUser, recordId: body['primaryKey'], response, extra: { body, query, headers, cookies, requestUrl } }); if (deleteError) { return { error: deleteError }; } diff --git a/adminforth/types/Back.ts b/adminforth/types/Back.ts index 116150ed1..0e8cf56f0 100644 --- a/adminforth/types/Back.ts +++ b/adminforth/types/Back.ts @@ -360,16 +360,16 @@ export interface IAdminForth { tr(msg: string, category: string, lang: string, params: any, pluralizationNumber?: number): Promise; createResourceRecord( - params: { resource: AdminForthResource, record: any, adminUser: AdminUser, extra?: HttpExtra } + params: { resource: AdminForthResource, record: any, response: IAdminForthHttpResponse, adminUser: AdminUser, extra?: HttpExtra } ): Promise<{ error?: string, createdRecord?: any, newRecordId?: any }>; updateResourceRecord( - params: { resource: AdminForthResource, recordId: any, record: any, oldRecord: any, adminUser: AdminUser, extra?: HttpExtra, updates?: never } - | { resource: AdminForthResource, recordId: any, record?: never, oldRecord: any, adminUser: AdminUser, extra?: HttpExtra, updates: any } + params: { resource: AdminForthResource, recordId: any, record: any, oldRecord: any, adminUser: AdminUser, response: IAdminForthHttpResponse, extra?: HttpExtra, updates?: never } + | { resource: AdminForthResource, recordId: any, record?: never, oldRecord: any, adminUser: AdminUser, response: IAdminForthHttpResponse, extra?: HttpExtra, updates: any } ): Promise<{ error?: string }>; deleteResourceRecord( - params: { resource: AdminForthResource, recordId: string, adminUser: AdminUser, record: any, extra?: HttpExtra } + params: { resource: AdminForthResource, recordId: string, adminUser: AdminUser, record: any, response: IAdminForthHttpResponse, extra?: HttpExtra } ): Promise<{ error?: string }>; auth: IAdminForthAuth; @@ -539,6 +539,7 @@ export type BeforeDeleteSaveFunction = (params: { adminUser: AdminUser, record: any, adminforth: IAdminForth, + response: IAdminForthHttpResponse, extra?: HttpExtra, }) => Promise<{ok: boolean, error?: string}>; @@ -551,6 +552,7 @@ export type BeforeEditSaveFunction = (params: { record: any, // legacy, 'updates' should be used instead oldRecord: any, adminforth: IAdminForth, + response: IAdminForthHttpResponse, extra?: HttpExtra, }) => Promise<{ok: boolean, error?: string | null}>; @@ -561,6 +563,7 @@ export type BeforeCreateSaveFunction = (params: { adminUser: AdminUser, record: any, adminforth: IAdminForth, + response: IAdminForthHttpResponse, extra?: HttpExtra, }) => Promise<{ok: boolean, error?: string | null, newRecordId?: string}>; @@ -571,6 +574,7 @@ export type AfterCreateSaveFunction = (params: { record: any, adminforth: IAdminForth, recordWithVirtualColumns?: any, + response: IAdminForthHttpResponse, extra?: HttpExtra, }) => Promise<{ok: boolean, error?: string}>; @@ -584,6 +588,7 @@ export type AfterDeleteSaveFunction = (params: { adminUser: AdminUser, record: any, adminforth: IAdminForth, + response: IAdminForthHttpResponse, extra?: HttpExtra, }) => Promise<{ok: boolean, error?: string}>; @@ -596,6 +601,7 @@ export type AfterEditSaveFunction = (params: { record: any, // legacy, 'updates' should be used instead oldRecord: any, adminforth: IAdminForth, + response: IAdminForthHttpResponse, extra?: HttpExtra, }) => Promise<{ok: boolean, error?: string}>; From 7cb340c223bdec8ebe768ae7f6a5c3cd0568e8b1 Mon Sep 17 00:00:00 2001 From: yaroslav8765 Date: Tue, 6 Jan 2026 11:27:24 +0200 Subject: [PATCH 2/2] feat: allow to set cookie from the bulkActions --- adminforth/modules/configValidator.ts | 4 +++- adminforth/modules/restApi.ts | 6 +++--- adminforth/types/Back.ts | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/adminforth/modules/configValidator.ts b/adminforth/modules/configValidator.ts index 8aa830ebc..c3a761044 100644 --- a/adminforth/modules/configValidator.ts +++ b/adminforth/modules/configValidator.ts @@ -249,7 +249,7 @@ export default class ConfigValidator implements IConfigValidator { icon: 'flowbite:trash-bin-outline', confirm: 'Are you sure you want to delete selected items?', allowed: async ({ resource, adminUser, allowedActions }) => { return allowedActions.delete }, - action: async ({ selectedIds, adminUser }) => { + action: async ({ selectedIds, adminUser, response }) => { const connector = this.adminforth.connectors[res.dataSource]; // for now if at least one error, stop and return error @@ -267,6 +267,7 @@ export default class ConfigValidator implements IConfigValidator { resource: res as AdminForthResource, record, adminUser, + response, adminforth: this.adminforth }); if (!error && resp.error) { @@ -290,6 +291,7 @@ export default class ConfigValidator implements IConfigValidator { record, adminUser, recordId: recordId, + response, adminforth: this.adminforth, }); } diff --git a/adminforth/modules/restApi.ts b/adminforth/modules/restApi.ts index b5b9f143e..8197b2a11 100644 --- a/adminforth/modules/restApi.ts +++ b/adminforth/modules/restApi.ts @@ -1448,7 +1448,7 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI { server.endpoint({ method: 'POST', path: '/start_bulk_action', - handler: async ({ body, adminUser, tr }) => { + handler: async ({ body, adminUser, tr, response }) => { const { resourceId, actionId, recordIds } = body; const resource = this.adminforth.config.resources.find((res) => res.resourceId == resourceId); if (!resource) { @@ -1473,13 +1473,13 @@ export default class AdminForthRestAPI implements IAdminForthRestAPI { return { error: await tr(`Action "{actionId}" not allowed`, 'errors', { actionId: action.label }) }; } } - const response = await action.action({selectedIds: recordIds, adminUser, resource, tr}); + const bulkActionResponse = await action.action({selectedIds: recordIds, adminUser, resource, response, tr}); return { actionId, recordIds, resourceId, - ...response + ...bulkActionResponse } } }) diff --git a/adminforth/types/Back.ts b/adminforth/types/Back.ts index 0e8cf56f0..6b926759f 100644 --- a/adminforth/types/Back.ts +++ b/adminforth/types/Back.ts @@ -1535,8 +1535,8 @@ export interface AdminForthBulkAction extends AdminForthBulkActionCommon { * Callback which will be called on backend when user clicks on action button. * It should return Promise which will be resolved when action is done. */ - action: ({ resource, selectedIds, adminUser, tr }: { - resource: AdminForthResource, selectedIds: Array, adminUser: AdminUser, tr: (key: string, category?: string, params?: any) => string + action: ({ resource, selectedIds, adminUser, response, tr }: { + resource: AdminForthResource, selectedIds: Array, adminUser: AdminUser, response: IAdminForthHttpResponse, tr: (key: string, category?: string, params?: any) => string }) => Promise<{ ok: boolean, error?: string, successMessage?: string }>, /**