@autofleet/shtinker 3.11.6 → 3.12.1

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.
@@ -0,0 +1,2 @@
1
+ import{t as e}from"./chunk-BO4cyBXU.js";import{isDeepStrictEqual as t}from"node:util";import{getCurrentPayload as n,getUser as r,outbreak as i}from"@autofleet/zehut";import a from"@autofleet/logger";import o from"@autofleet/network";let s=function(e){return e.CREATE=`create`,e.BULK_CREATE=`bulk-create`,e.BULK_EDIT=`bulk-edit`,e.DELETE=`delete`,e.UPDATE=`update`,e.CANCEL=`cancel`,e.FAIL=`fail`,e.UNASSIGN=`unassign`,e.BULK_ASSIGN=`bulk-assign`,e.REASSIGN=`reassign`,e.DISPATCH=`dispatch`,e.BULK_DISPATCH=`bulk-dispatch`,e.BULK_UPSERT=`bulk-upsert`,e.UPSERT=`upsert`,e.JOIN=`join`,e.MOVE=`move`,e.REOPTIMIZATION=`reoptimization`,e}({}),c=function(e){return e.RIDE=`Ride`,e.VEHICLE=`Vehicle`,e.DRIVER=`Driver`,e.PRICE_CALCULATION=`PriceCalculation`,e.ORDERING_FLEET_SPEC=`OrderingConfig`,e.ORDERING_VEHICLE=`OrderingVehicle`,e.ORDERING_ADDITIONAL_OPTION=`OrderingAdditionalOption`,e}({}),l=function(e){return e.USER=`user`,e.AUTOMATION=`automation`,e}({});const u=`audit-log-context`,d=`audit-log-rows`,f=`auditLogContext`,p=`x-af-automation-id`,m=a();m.addContextMiddleware(()=>({traceId:i.getCurrentContextTraceId()}));var h=m;const g=`customFields`,_=()=>n()?.nonHeaderContext?.get(f),v={[s.BULK_CREATE]:s.BULK_CREATE,[s.BULK_EDIT]:s.BULK_EDIT,[s.BULK_ASSIGN]:s.BULK_ASSIGN,[s.BULK_DISPATCH]:s.BULK_DISPATCH,[s.BULK_UPSERT]:s.BULK_UPSERT},y=e=>[null,void 0].includes(e)?!0:Array.isArray(e)?e.length===0:typeof e==`object`&&!(e instanceof Date)?Object.keys(e).length===0:!1,b=e=>!Object.hasOwn(v,e),x=(e,t)=>e.filter(e=>!y(t.get(e))||!y(t.previous(e))),S=(e,t)=>x((t.returning?t.fields:e.changed()||[]).filter(e=>e!==g),e).map(t=>({property:t,previousValue:e.previous(t),newValue:e.get(t)})),C=e=>{let n=e.changed();if(!(n&&n.includes(g)))return[];let r=e.get(g),i=e.previous(g);return Object.keys(r).filter(e=>{let n=r?.[e],a=i?.[e];return(!y(n)||!y(a))&&!t(n,a)}).map(e=>({property:`${g}.${e}`,previousValue:i?.[e],newValue:r?.[e]}))};var w=class{rabbit;sequelize;logger;excludeModels;waitForTransactionCommit;constructor(e){this.rabbit=e.rabbit,this.sequelize=e.sequelize,this.logger=e.logger||h,this.excludeModels=e.excludeModels??[],this.waitForTransactionCommit=e.waitForTransactionCommit??!1}async manualSendAuditLog({auditContext:e,...t},n=this.logAndThrowError){if(e??=_(),!e)return`NO_AUDIT_CONTEXT`;try{return e?.entityType?.toLowerCase()===t.entityType?.toLowerCase()&&b(e.action)&&(e.entityId=t.entityId),await this.rabbit.sendToQueue(d,t),`SUCCESS`}catch(e){return n(e,t),`FAILURE`}}registerHooks(){Object.entries(this.sequelize.models).forEach(([e,t])=>{this.excludeModels.includes(e)||t.addHook(`afterSave`,async(t,n)=>{let r=async()=>{let r=_();if(!r)return;let{id:i}=t;await this.manualSendAuditLog({entityId:i,auditContext:r,entityType:e,rows:[...S(t,n),...C(t)]},this.logError)};if(this.waitForTransactionCommit&&n.transaction){n.transaction.afterCommit(r);return}await r()})})}async sendAuditLogContext(e){try{await this.rabbit.sendToQueue(u,e)}catch(e){this.logger.error(`Failed to send audit log context`,e)}}logError=(e,t)=>{let{entityType:n,entityId:r}=t;this.logger.error(`Failed to send audit log rows`,{entityType:n,entityId:r,error:e})};logAndThrowError=(e,t)=>{throw this.logError(e,t),e}},T=e({getByEntityId:()=>D,query:()=>O,queryByEntityId:()=>k});const E=new o({serviceName:`AUDIT_MS`,timeout:6e4,logger:h}),D=async e=>{let{data:t}=await E.get(`api/v1/audit-logs/${e}`);return t},O=async(e,t=!1)=>{if(t){let t=await E.getAllPagesFromQueryEndpoint(`api/v1/audit-logs/query`,e);return{count:t.length,rows:t}}let{data:n}=await E.post(`api/v1/audit-logs/query`,e);return n},k=async(e,t,n=!1)=>{let r={...t,query:{...t?.query,entityId:e}};if(n){let e=await E.getAllPagesFromQueryEndpoint(`api/v1/audit-logs/query`,r);return{count:e.length,rows:e}}let{data:i}=await E.post(`api/v1/audit-logs/query`,r);return i};let A;const j=({registerHooks:e=!0,...t})=>process.env.DISABLE_AUDIT_LOGS===`true`?!1:(A=new w(t),e&&A.registerHooks(),A),M=({action:e,endpoint:t,entityType:r,method:i,automationId:a,userId:o})=>{let s={entityType:r,action:e,endpoint:t,method:i,performedBy:o||(a??null),actionOrigin:o&&l.USER||a&&l.AUTOMATION||null};return n().nonHeaderContext.set(f,s),s},N=async(e,t,n,i)=>{try{if(process.env.DISABLE_AUDIT_LOGS===`true`||!A)return;let a=M({action:i,endpoint:e.url,entityType:n,method:e.method,userId:r()?.id,automationId:e.headers[p]});if(!b(i)){await A.sendAuditLogContext(a);return}let o=async()=>{t.off(`finish`,o),t.off(`close`,o),t.off(`error`,o),await A?.sendAuditLogContext(a)};t.once(`finish`,o),t.once(`close`,o),t.once(`error`,o)}catch(e){h.error(`couldn't set audit context`,e)}},P=(e,t)=>async(n,{userId:r,automationId:i})=>{if(process.env.DISABLE_AUDIT_LOGS===`true`||!A)return;let a=M({action:t,endpoint:n,entityType:e,method:`rabbit`,userId:r,automationId:i});await A.sendAuditLogContext(a)},F=async(e,t,n,r)=>{if(process.env.DISABLE_AUDIT_LOGS===`true`||!A)return;let i=M({action:t,endpoint:n,entityType:e,method:`bull`,automationId:r});await A.sendAuditLogContext(i)};export{T as a,f as c,p as d,s as f,P as i,u as l,c as m,N as n,k as o,l as p,F as r,w as s,j as t,d as u};
2
+ //# sourceMappingURL=common-BWe107dZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"common-BWe107dZ.js","names":["logger: LoggerInstanceManager","getCurrentTrace","logger","query","auditLogger: AuditLogger | undefined","AuditLogger","auditLogContext: AuditLogContextData"],"sources":["../src/types.ts","../src/const.ts","../src/logger.ts","../src/audit-logger.ts","../src/audit-ms.ts","../src/common.ts"],"sourcesContent":["import type { ModelStatic, Model, Sequelize } from 'sequelize';\nimport type RabbitMq from '@autofleet/rabbit';\nimport type { LoggerInstanceManager } from '@autofleet/logger';\nimport type { IRouter } from 'express';\nimport type { FastifyInstance } from 'fastify';\n\nexport interface AuditLoggerOptions {\n rabbit: RabbitMq;\n sequelize: Sequelize;\n logger: LoggerInstanceManager;\n router?: IRouter;\n fastify?: FastifyInstance;\n excludeModels?: string[];\n entityScopedModelMap?: Record<string, ModelStatic<Model>>;\n /**\n * If true and the model was changed inside a transaction, the audit log will be sent only after the transaction is committed.\n * This is useful because sequelize afterSave hook doesn't include the previous values if the model was changed inside a transaction.\n * @default false\n */\n waitForTransactionCommit?: boolean;\n registerHooks?: boolean;\n}\n\nexport interface AuditLogContextData {\n entityType: string;\n entityId?: string;\n action: string;\n performedBy: string | null;\n endpoint: string;\n method: string;\n actionOrigin?: string | null;\n}\n\nexport interface AuditLogRowData {\n property: string;\n previousValue: unknown;\n newValue: unknown;\n}\n\nexport interface AuditLogContext extends AuditLogContextData {\n createdAt: Date;\n}\n\nexport interface AuditLogRow extends AuditLogRowData {\n id: string;\n context: AuditLogContext[];\n createdAt: Date;\n}\n\nexport interface AuditLogPayload {\n entityType: string;\n entityId: string;\n rows: readonly AuditLogRowData[];\n}\n\nexport enum Action {\n CREATE = 'create',\n BULK_CREATE = 'bulk-create',\n BULK_EDIT = 'bulk-edit',\n DELETE = 'delete',\n UPDATE = 'update',\n CANCEL = 'cancel',\n FAIL = 'fail',\n UNASSIGN = 'unassign',\n BULK_ASSIGN = 'bulk-assign',\n REASSIGN = 'reassign',\n DISPATCH = 'dispatch',\n BULK_DISPATCH = 'bulk-dispatch',\n BULK_UPSERT = 'bulk-upsert',\n UPSERT = 'upsert',\n JOIN = 'join',\n MOVE = 'move',\n REOPTIMIZATION = 'reoptimization',\n}\n\nexport enum EntityType {\n RIDE = 'Ride',\n VEHICLE = 'Vehicle',\n DRIVER = 'Driver',\n PRICE_CALCULATION = 'PriceCalculation',\n /** Due to some legacy reasons and bad product decisions, the fleet-spec model used to be called \"config\", and we are stuck with \"Config\" this as the name in the DB. */\n ORDERING_FLEET_SPEC = 'OrderingConfig',\n ORDERING_VEHICLE = 'OrderingVehicle',\n ORDERING_ADDITIONAL_OPTION = 'OrderingAdditionalOption',\n}\n\nexport const enum ActionOrigin {\n USER = 'user',\n AUTOMATION = 'automation',\n}\n","export const AUDIT_LOG_CONTEXT_QUEUE = 'audit-log-context';\nexport const AUDIT_LOG_ROWS_QUEUE = 'audit-log-rows';\nexport const AUDIT_LOG_CONTEXT_KEY = 'auditLogContext';\nexport const AUTOMATION_ID_HEADER = 'x-af-automation-id';\n","import { outbreak } from '@autofleet/zehut';\nimport Logger, { type LoggerInstanceManager } from '@autofleet/logger';\n\nconst logger: LoggerInstanceManager = Logger();\nlogger.addContextMiddleware(() => ({ traceId: outbreak.getCurrentContextTraceId() }));\nexport default logger;\n","import { isDeepStrictEqual } from 'node:util';\nimport type RabbitMq from '@autofleet/rabbit';\nimport type { LoggerInstanceManager } from '@autofleet/logger';\nimport { getCurrentPayload as getCurrentTrace } from '@autofleet/zehut';\nimport type {\n CreateOptions, InstanceUpdateOptions, Model, Sequelize,\n} from 'sequelize';\nimport {\n type AuditLogPayload, type AuditLoggerOptions, type AuditLogContextData, Action, type AuditLogRowData,\n} from './types';\nimport { AUDIT_LOG_CONTEXT_QUEUE, AUDIT_LOG_ROWS_QUEUE, AUDIT_LOG_CONTEXT_KEY } from './const';\nimport logger from './logger';\n\nconst CUSTOM_FIELDS_PROPERTY = 'customFields';\n\nconst getAuditContext = () => {\n const currentTrace = getCurrentTrace();\n return currentTrace?.nonHeaderContext?.get(AUDIT_LOG_CONTEXT_KEY) as AuditLogContextData | undefined;\n};\n\nconst ACTIONS_TO_OMIT_ENTITY_ID_FROM_CONTEXT = {\n [Action.BULK_CREATE]: Action.BULK_CREATE,\n [Action.BULK_EDIT]: Action.BULK_EDIT,\n [Action.BULK_ASSIGN]: Action.BULK_ASSIGN,\n [Action.BULK_DISPATCH]: Action.BULK_DISPATCH,\n [Action.BULK_UPSERT]: Action.BULK_UPSERT,\n};\n\nconst isEmpty = (field: unknown): boolean => {\n if ([null, undefined].includes(field as undefined)) {\n return true;\n }\n if (Array.isArray(field)) {\n return field.length === 0;\n }\n if (typeof field === 'object' && !(field instanceof Date)) {\n return Object.keys(field as Record<string, unknown>).length === 0;\n }\n return false;\n};\n\nexport const isEntityIdRequired = (action: string): boolean => !Object.hasOwn(ACTIONS_TO_OMIT_ENTITY_ID_FROM_CONTEXT, action);\n\nconst filterOutEmptyFields = (fields: string[], instance: Model) => fields.filter(field => !isEmpty(instance.get(field)) || !isEmpty(instance.previous(field)));\n\nconst getChangedFieldsRows = (instance: Model, options: CreateOptions | InstanceUpdateOptions): AuditLogRowData[] => {\n // When bulk updating in sequelize using the \"returning\" option, the instance.changed() stops working.\n // It's a known issue with sequelize.\n const changedFields: string[] = options.returning ? options.fields as string[] : (instance.changed() || []);\n // Filter customFields - we'll handle them later\n const filteredChangedFields = filterOutEmptyFields(changedFields.filter(field => field !== CUSTOM_FIELDS_PROPERTY), instance);\n return filteredChangedFields.map(property => ({\n property,\n previousValue: instance.previous(property),\n newValue: instance.get(property),\n }));\n};\n\nconst getChangedCustomFieldsRows = (instance: Model): AuditLogRowData[] => {\n // Based on the comment in getChangedFieldsRows, we cannot rely on instance.changed() when doing bulk updates with \"returning\".\n // So this seems like a possible bug.\n const changed = instance.changed();\n const customFieldsChanged = changed && changed.includes(CUSTOM_FIELDS_PROPERTY);\n if (!customFieldsChanged) {\n return [];\n }\n\n const customFields = instance.get(CUSTOM_FIELDS_PROPERTY) as Record<string, unknown>;\n const previousCustomFields = instance.previous(CUSTOM_FIELDS_PROPERTY);\n const changedCustomFields = Object.keys(customFields).filter((field) => {\n const newValue = customFields?.[field];\n const oldValue = previousCustomFields?.[field];\n return (!isEmpty(newValue) || !isEmpty(oldValue)) && !isDeepStrictEqual(newValue, oldValue);\n });\n return changedCustomFields.map(changedCustomField => ({\n property: `${CUSTOM_FIELDS_PROPERTY}.${changedCustomField}`,\n previousValue: previousCustomFields?.[changedCustomField],\n newValue: customFields?.[changedCustomField],\n }));\n};\n\ninterface AuditLogSendData extends AuditLogPayload {\n auditContext?: AuditLogContextData;\n}\n\ntype AuditLogRowSendingErrorHandler = (error: unknown, payload: AuditLogPayload) => void;\n\nclass AuditLogger {\n private readonly rabbit: RabbitMq;\n private readonly sequelize: Sequelize;\n private readonly logger: LoggerInstanceManager;\n private readonly excludeModels: string[];\n private readonly waitForTransactionCommit: boolean;\n\n constructor(options: AuditLoggerOptions) {\n this.rabbit = options.rabbit;\n this.sequelize = options.sequelize;\n this.logger = options.logger || logger;\n this.excludeModels = options.excludeModels ?? [];\n this.waitForTransactionCommit = options.waitForTransactionCommit ?? false;\n }\n\n public async manualSendAuditLog(\n { auditContext, ...payload }: AuditLogSendData,\n errorHandler: AuditLogRowSendingErrorHandler = this.logAndThrowError,\n ): Promise<string> {\n auditContext ??= getAuditContext();\n if (!auditContext) {\n return 'NO_AUDIT_CONTEXT';\n }\n try {\n // Make sure the current entity being changed it the \"main\" entity in the current audit context, and if so mark it as the \"entityId\".\n if (auditContext?.entityType?.toLowerCase() === payload.entityType?.toLowerCase() && isEntityIdRequired(auditContext.action)) {\n auditContext.entityId = payload.entityId;\n }\n await this.rabbit.sendToQueue(AUDIT_LOG_ROWS_QUEUE, payload);\n return 'SUCCESS';\n } catch (error) {\n errorHandler(error, payload);\n return 'FAILURE';\n }\n }\n\n public registerHooks(): void {\n Object.entries(this.sequelize.models).forEach(([modelName, modelType]) => {\n if (this.excludeModels.includes(modelName)) {\n return;\n }\n modelType.addHook('afterSave', async (instance, options) => {\n const sendAuditLogForModel = async () => {\n const auditContext = getAuditContext();\n if (!auditContext) return;\n\n const { id: entityId } = instance as unknown as { id: string; };\n await this.manualSendAuditLog(\n {\n entityId,\n auditContext,\n entityType: modelName,\n rows: [...getChangedFieldsRows(instance, options), ...getChangedCustomFieldsRows(instance)],\n },\n this.logError,\n );\n };\n /*\n By default sequelize doesn't keep the previous values when using a transaction\n So we want to be able to call this function after the transaction is committed\n to be able to get the previous values\n */\n if (this.waitForTransactionCommit && options.transaction) {\n options.transaction.afterCommit(sendAuditLogForModel);\n return;\n }\n await sendAuditLogForModel();\n });\n });\n }\n\n public async sendAuditLogContext(context: AuditLogContextData): Promise<void> {\n try {\n await this.rabbit.sendToQueue(AUDIT_LOG_CONTEXT_QUEUE, context);\n } catch (err) {\n this.logger.error('Failed to send audit log context', err);\n }\n }\n\n private logError: AuditLogRowSendingErrorHandler = (error: unknown, payload: AuditLogPayload) => {\n const { entityType, entityId } = payload;\n this.logger.error('Failed to send audit log rows', { entityType, entityId, error });\n };\n\n private logAndThrowError: AuditLogRowSendingErrorHandler = (error: unknown, payload: AuditLogPayload): never => {\n this.logError(error, payload);\n throw error;\n };\n}\n\nexport default AuditLogger;\n","import Network from '@autofleet/network';\nimport logger from './logger';\nimport type { FindOptions, Order, Includeable } from 'sequelize';\nimport type { AuditLogRow } from './types';\n\nconst auditMs = new Network({ serviceName: 'AUDIT_MS', timeout: 60_000, logger });\n\nconst getByEntityId = async (entityId: string): Promise<AuditLogRow[]> => {\n const { data } = await auditMs.get(`api/v1/audit-logs/${entityId}`);\n return data;\n};\n\nexport interface QueryRequestBody {\n query?: FindOptions['where'];\n order?: Order;\n attributes?: FindOptions['attributes'];\n page?: number;\n perPage?: number;\n include?: Includeable | Includeable[];\n}\n\ninterface QueryResponse {\n count: number;\n rows: AuditLogRow[];\n}\n\nconst query = async (queryRequestBody: QueryRequestBody, getAllPages = false): Promise<QueryResponse> => {\n if (getAllPages) {\n const allRows = await auditMs.getAllPagesFromQueryEndpoint<AuditLogRow>('api/v1/audit-logs/query', queryRequestBody);\n return { count: allRows.length, rows: allRows };\n }\n const { data } = await auditMs.post('api/v1/audit-logs/query', queryRequestBody);\n return data;\n};\n\nconst queryByEntityId = async (entityId: string, queryRequestBody: QueryRequestBody, getAllPages = false): Promise<QueryResponse> => {\n const query = { ...queryRequestBody, query: { ...queryRequestBody?.query, entityId } };\n if (getAllPages) {\n const allRows = await auditMs.getAllPagesFromQueryEndpoint<AuditLogRow>('api/v1/audit-logs/query', query);\n return { count: allRows.length, rows: allRows };\n }\n const { data } = await auditMs.post('api/v1/audit-logs/query', query);\n return data;\n};\n\nexport { getByEntityId, query, queryByEntityId };\n","import type { Writable } from 'node:stream';\nimport type { IncomingHttpHeaders } from 'node:http';\nimport { getCurrentPayload, getUser } from '@autofleet/zehut';\nimport logger from './logger';\nimport AuditLogger, { isEntityIdRequired } from './audit-logger';\nimport { AUDIT_LOG_CONTEXT_KEY, AUTOMATION_ID_HEADER } from './const';\nimport { ActionOrigin, type AuditLogContextData, type AuditLoggerOptions } from './types';\n\nlet auditLogger: AuditLogger | undefined;\n\nexport const innerEnableAuditing = ({ registerHooks = true, ...options }: AuditLoggerOptions): false | AuditLogger => {\n if (process.env.DISABLE_AUDIT_LOGS === 'true') {\n return false;\n }\n auditLogger = new AuditLogger(options);\n if (registerHooks) {\n auditLogger.registerHooks();\n }\n return auditLogger;\n};\n\ninterface GetAuditLogContextParams {\n action: string;\n endpoint: string;\n entityType: string;\n method: string;\n automationId?: string;\n userId?: string;\n}\nconst getAuditLogContextAndSetToContext = ({\n action, endpoint, entityType, method, automationId, userId,\n}: GetAuditLogContextParams): AuditLogContextData => {\n const performedBy = userId || (automationId ?? null);\n const actionOrigin = (userId && ActionOrigin.USER) || (automationId && ActionOrigin.AUTOMATION) || null;\n\n const auditLogContext: AuditLogContextData = {\n entityType,\n action,\n endpoint,\n method,\n performedBy,\n actionOrigin,\n };\n const currentTrace = getCurrentPayload();\n currentTrace.nonHeaderContext.set(AUDIT_LOG_CONTEXT_KEY, auditLogContext);\n\n return auditLogContext;\n};\n\ninterface PartialRequest {\n url: string;\n method: string;\n headers: IncomingHttpHeaders;\n}\n\nexport const innerSetAuditContext = async (req: PartialRequest, res: Writable, entityType: string, action: string): Promise<void> => {\n try {\n if (process.env.DISABLE_AUDIT_LOGS === 'true' || !auditLogger) {\n return;\n }\n const auditLogContext = getAuditLogContextAndSetToContext({\n action,\n endpoint: req.url,\n entityType,\n method: req.method,\n userId: getUser()?.id,\n automationId: req.headers[AUTOMATION_ID_HEADER] as string | undefined,\n });\n\n if (!isEntityIdRequired(action)) { // if it's a bulk action, we don't want to wait for the response to add the entity id\n await auditLogger.sendAuditLogContext(auditLogContext);\n return;\n }\n\n const sendAuditLogContextEvent = async () => {\n res.off('finish', sendAuditLogContextEvent);\n res.off('close', sendAuditLogContextEvent);\n res.off('error', sendAuditLogContextEvent);\n await auditLogger?.sendAuditLogContext(auditLogContext);\n };\n res.once('finish', sendAuditLogContextEvent);\n res.once('close', sendAuditLogContextEvent);\n res.once('error', sendAuditLogContextEvent);\n } catch (err) {\n logger.error('couldn\\'t set audit context', err);\n }\n};\n\nexport const setRabbitAuditContext = (entityType: string, action: string) => async (endpoint: string, { userId, automationId }: { userId: string; automationId: string; }): Promise<void> => {\n if (process.env.DISABLE_AUDIT_LOGS === 'true' || !auditLogger) {\n return;\n }\n const auditLogContext = getAuditLogContextAndSetToContext({\n action,\n endpoint,\n entityType,\n method: 'rabbit',\n userId,\n automationId,\n });\n\n await auditLogger.sendAuditLogContext(auditLogContext);\n};\n\nexport const setBullMqAuditContext = async (entityType: string, action: string, workerName: string, jobId: string): Promise<void> => {\n if (process.env.DISABLE_AUDIT_LOGS === 'true' || !auditLogger) {\n return;\n }\n const auditLogContext = getAuditLogContextAndSetToContext({\n action,\n endpoint: workerName,\n entityType,\n method: 'bull',\n automationId: jobId,\n });\n\n await auditLogger.sendAuditLogContext(auditLogContext);\n};\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,n)=>{let r={};for(var i in e)t(r,i,{get:e[i],enumerable:!0});return n&&t(r,Symbol.toStringTag,{value:`Module`}),r},s=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},c=(n,r,a)=>(a=n==null?{}:e(i(n)),s(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let l=require(`node:util`),u=require(`@autofleet/zehut`),d=require(`@autofleet/logger`);d=c(d);let f=require(`@autofleet/network`);f=c(f);let p=function(e){return e.CREATE=`create`,e.BULK_CREATE=`bulk-create`,e.BULK_EDIT=`bulk-edit`,e.DELETE=`delete`,e.UPDATE=`update`,e.CANCEL=`cancel`,e.FAIL=`fail`,e.UNASSIGN=`unassign`,e.BULK_ASSIGN=`bulk-assign`,e.REASSIGN=`reassign`,e.DISPATCH=`dispatch`,e.BULK_DISPATCH=`bulk-dispatch`,e.BULK_UPSERT=`bulk-upsert`,e.UPSERT=`upsert`,e.JOIN=`join`,e.MOVE=`move`,e.REOPTIMIZATION=`reoptimization`,e}({}),m=function(e){return e.RIDE=`Ride`,e.VEHICLE=`Vehicle`,e.DRIVER=`Driver`,e.PRICE_CALCULATION=`PriceCalculation`,e.ORDERING_FLEET_SPEC=`OrderingConfig`,e.ORDERING_VEHICLE=`OrderingVehicle`,e.ORDERING_ADDITIONAL_OPTION=`OrderingAdditionalOption`,e}({}),h=function(e){return e.USER=`user`,e.AUTOMATION=`automation`,e}({});const g=`audit-log-context`,_=`audit-log-rows`,v=`auditLogContext`,y=`x-af-automation-id`,b=(0,d.default)();b.addContextMiddleware(()=>({traceId:u.outbreak.getCurrentContextTraceId()}));var x=b;const S=`customFields`,C=()=>(0,u.getCurrentPayload)()?.nonHeaderContext?.get(v),w={[p.BULK_CREATE]:p.BULK_CREATE,[p.BULK_EDIT]:p.BULK_EDIT,[p.BULK_ASSIGN]:p.BULK_ASSIGN,[p.BULK_DISPATCH]:p.BULK_DISPATCH,[p.BULK_UPSERT]:p.BULK_UPSERT},T=e=>[null,void 0].includes(e)?!0:Array.isArray(e)?e.length===0:typeof e==`object`&&!(e instanceof Date)?Object.keys(e).length===0:!1,E=e=>!Object.hasOwn(w,e),D=(e,t)=>e.filter(e=>!T(t.get(e))||!T(t.previous(e))),O=(e,t)=>D((t.returning?t.fields:e.changed()||[]).filter(e=>e!==S),e).map(t=>({property:t,previousValue:e.previous(t),newValue:e.get(t)})),k=e=>{let t=e.changed();if(!(t&&t.includes(S)))return[];let n=e.get(S),r=e.previous(S);return Object.keys(n).filter(e=>{let t=n?.[e],i=r?.[e];return(!T(t)||!T(i))&&!(0,l.isDeepStrictEqual)(t,i)}).map(e=>({property:`${S}.${e}`,previousValue:r?.[e],newValue:n?.[e]}))};var A=class{rabbit;sequelize;logger;excludeModels;waitForTransactionCommit;constructor(e){this.rabbit=e.rabbit,this.sequelize=e.sequelize,this.logger=e.logger||x,this.excludeModels=e.excludeModels??[],this.waitForTransactionCommit=e.waitForTransactionCommit??!1}async manualSendAuditLog({auditContext:e,...t},n=this.logAndThrowError){if(e??=C(),!e)return`NO_AUDIT_CONTEXT`;try{return e?.entityType?.toLowerCase()===t.entityType?.toLowerCase()&&E(e.action)&&(e.entityId=t.entityId),await this.rabbit.sendToQueue(_,t),`SUCCESS`}catch(e){return n(e,t),`FAILURE`}}registerHooks(){Object.entries(this.sequelize.models).forEach(([e,t])=>{this.excludeModels.includes(e)||t.addHook(`afterSave`,async(t,n)=>{let r=async()=>{let r=C();if(!r)return;let{id:i}=t;await this.manualSendAuditLog({entityId:i,auditContext:r,entityType:e,rows:[...O(t,n),...k(t)]},this.logError)};if(this.waitForTransactionCommit&&n.transaction){n.transaction.afterCommit(r);return}await r()})})}async sendAuditLogContext(e){try{await this.rabbit.sendToQueue(g,e)}catch(e){this.logger.error(`Failed to send audit log context`,e)}}logError=(e,t)=>{let{entityType:n,entityId:r}=t;this.logger.error(`Failed to send audit log rows`,{entityType:n,entityId:r,error:e})};logAndThrowError=(e,t)=>{throw this.logError(e,t),e}},j=A,M=o({getByEntityId:()=>P,query:()=>F,queryByEntityId:()=>I});const N=new f.default({serviceName:`AUDIT_MS`,timeout:6e4,logger:x}),P=async e=>{let{data:t}=await N.get(`api/v1/audit-logs/${e}`);return t},F=async(e,t=!1)=>{if(t){let t=await N.getAllPagesFromQueryEndpoint(`api/v1/audit-logs/query`,e);return{count:t.length,rows:t}}let{data:n}=await N.post(`api/v1/audit-logs/query`,e);return n},I=async(e,t,n=!1)=>{let r={...t,query:{...t?.query,entityId:e}};if(n){let e=await N.getAllPagesFromQueryEndpoint(`api/v1/audit-logs/query`,r);return{count:e.length,rows:e}}let{data:i}=await N.post(`api/v1/audit-logs/query`,r);return i};let L;const R=({registerHooks:e=!0,...t})=>process.env.DISABLE_AUDIT_LOGS===`true`?!1:(L=new j(t),e&&L.registerHooks(),L),z=({action:e,endpoint:t,entityType:n,method:r,automationId:i,userId:a})=>{let o={entityType:n,action:e,endpoint:t,method:r,performedBy:a||(i??null),actionOrigin:a&&h.USER||i&&h.AUTOMATION||null};return(0,u.getCurrentPayload)().nonHeaderContext.set(v,o),o},B=async(e,t,n,r)=>{try{if(process.env.DISABLE_AUDIT_LOGS===`true`||!L)return;let i=z({action:r,endpoint:e.url,entityType:n,method:e.method,userId:(0,u.getUser)()?.id,automationId:e.headers[y]});if(!E(r)){await L.sendAuditLogContext(i);return}let a=async()=>{t.off(`finish`,a),t.off(`close`,a),t.off(`error`,a),await L?.sendAuditLogContext(i)};t.once(`finish`,a),t.once(`close`,a),t.once(`error`,a)}catch(e){x.error(`couldn't set audit context`,e)}},V=(e,t)=>async(n,{userId:r,automationId:i})=>{if(process.env.DISABLE_AUDIT_LOGS===`true`||!L)return;let a=z({action:t,endpoint:n,entityType:e,method:`rabbit`,userId:r,automationId:i});await L.sendAuditLogContext(a)},H=async(e,t,n,r)=>{if(process.env.DISABLE_AUDIT_LOGS===`true`||!L)return;let i=z({action:t,endpoint:n,entityType:e,method:`bull`,automationId:r});await L.sendAuditLogContext(i)};Object.defineProperty(exports,`a`,{enumerable:!0,get:function(){return M}}),Object.defineProperty(exports,`c`,{enumerable:!0,get:function(){return v}}),Object.defineProperty(exports,`d`,{enumerable:!0,get:function(){return y}}),Object.defineProperty(exports,`f`,{enumerable:!0,get:function(){return p}}),Object.defineProperty(exports,`h`,{enumerable:!0,get:function(){return c}}),Object.defineProperty(exports,`i`,{enumerable:!0,get:function(){return V}}),Object.defineProperty(exports,`l`,{enumerable:!0,get:function(){return g}}),Object.defineProperty(exports,`m`,{enumerable:!0,get:function(){return m}}),Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return B}}),Object.defineProperty(exports,`o`,{enumerable:!0,get:function(){return I}}),Object.defineProperty(exports,`p`,{enumerable:!0,get:function(){return h}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return H}}),Object.defineProperty(exports,`s`,{enumerable:!0,get:function(){return j}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return R}}),Object.defineProperty(exports,`u`,{enumerable:!0,get:function(){return _}});
2
+ //# sourceMappingURL=common-CYMTaZKn.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"common-BGDD7JIS.js","names":["logger: LoggerInstanceManager","getCurrentTrace","logger","query","auditLogger: AuditLogger | undefined","AuditLogger","auditLogContext: AuditLogContextData"],"sources":["../src/types.ts","../src/const.ts","../src/logger.ts","../src/audit-logger.ts","../src/audit-ms.ts","../src/common.ts"],"sourcesContent":["import type { ModelStatic, Model, Sequelize } from 'sequelize';\nimport type RabbitMq from '@autofleet/rabbit';\nimport type { LoggerInstanceManager } from '@autofleet/logger';\nimport type { IRouter } from 'express';\nimport type { FastifyInstance } from 'fastify';\n\nexport interface AuditLoggerOptions {\n rabbit: RabbitMq;\n sequelize: Sequelize;\n logger: LoggerInstanceManager;\n router?: IRouter;\n fastify?: FastifyInstance;\n excludeModels?: string[];\n entityScopedModelMap?: Record<string, ModelStatic<Model>>;\n /**\n * If true and the model was changed inside a transaction, the audit log will be sent only after the transaction is committed.\n * This is useful because sequelize afterSave hook doesn't include the previous values if the model was changed inside a transaction.\n * @default false\n */\n waitForTransactionCommit?: boolean;\n registerHooks?: boolean;\n}\n\nexport interface AuditLogContextData {\n entityType: string;\n entityId?: string;\n action: string;\n performedBy: string | null;\n endpoint: string;\n method: string;\n actionOrigin?: string | null;\n}\n\nexport interface AuditLogRowData {\n property: string;\n previousValue: unknown;\n newValue: unknown;\n}\n\nexport interface AuditLogContext extends AuditLogContextData {\n createdAt: Date;\n}\n\nexport interface AuditLogRow extends AuditLogRowData {\n id: string;\n context: AuditLogContext[];\n createdAt: Date;\n}\n\nexport interface AuditLogPayload {\n entityType: string;\n entityId: string;\n rows: readonly AuditLogRowData[];\n}\n\nexport enum Action {\n CREATE = 'create',\n BULK_CREATE = 'bulk-create',\n BULK_EDIT = 'bulk-edit',\n DELETE = 'delete',\n UPDATE = 'update',\n CANCEL = 'cancel',\n FAIL = 'fail',\n UNASSIGN = 'unassign',\n BULK_ASSIGN = 'bulk-assign',\n REASSIGN = 'reassign',\n DISPATCH = 'dispatch',\n BULK_DISPATCH = 'bulk-dispatch',\n BULK_UPSERT = 'bulk-upsert',\n UPSERT = 'upsert',\n JOIN = 'join',\n MOVE = 'move',\n REOPTIMIZATION = 'reoptimization',\n}\n\nexport enum EntityType {\n RIDE = 'Ride',\n VEHICLE = 'Vehicle',\n DRIVER = 'Driver',\n PRICE_CALCULATION = 'PriceCalculation',\n /** Due to some legacy reasons and bad product decisions, the fleet-spec model used to be called \"config\", and we are stuck with \"Config\" this as the name in the DB. */\n ORDERING_FLEET_SPEC = 'OrderingConfig',\n ORDERING_VEHICLE = 'OrderingVehicle',\n}\n\nexport const enum ActionOrigin {\n USER = 'user',\n AUTOMATION = 'automation',\n}\n","export const AUDIT_LOG_CONTEXT_QUEUE = 'audit-log-context';\nexport const AUDIT_LOG_ROWS_QUEUE = 'audit-log-rows';\nexport const AUDIT_LOG_CONTEXT_KEY = 'auditLogContext';\nexport const AUTOMATION_ID_HEADER = 'x-af-automation-id';\n","import { outbreak } from '@autofleet/zehut';\nimport Logger, { type LoggerInstanceManager } from '@autofleet/logger';\n\nconst logger: LoggerInstanceManager = Logger();\nlogger.addContextMiddleware(() => ({ traceId: outbreak.getCurrentContextTraceId() }));\nexport default logger;\n","import { isDeepStrictEqual } from 'node:util';\nimport type RabbitMq from '@autofleet/rabbit';\nimport type { LoggerInstanceManager } from '@autofleet/logger';\nimport { getCurrentPayload as getCurrentTrace } from '@autofleet/zehut';\nimport type {\n CreateOptions, InstanceUpdateOptions, Model, Sequelize,\n} from 'sequelize';\nimport {\n type AuditLogPayload, type AuditLoggerOptions, type AuditLogContextData, Action, type AuditLogRowData,\n} from './types';\nimport { AUDIT_LOG_CONTEXT_QUEUE, AUDIT_LOG_ROWS_QUEUE, AUDIT_LOG_CONTEXT_KEY } from './const';\nimport logger from './logger';\n\nconst CUSTOM_FIELDS_PROPERTY = 'customFields';\n\nconst getAuditContext = () => {\n const currentTrace = getCurrentTrace();\n return currentTrace?.nonHeaderContext?.get(AUDIT_LOG_CONTEXT_KEY) as AuditLogContextData | undefined;\n};\n\nconst ACTIONS_TO_OMIT_ENTITY_ID_FROM_CONTEXT = {\n [Action.BULK_CREATE]: Action.BULK_CREATE,\n [Action.BULK_EDIT]: Action.BULK_EDIT,\n [Action.BULK_ASSIGN]: Action.BULK_ASSIGN,\n [Action.BULK_DISPATCH]: Action.BULK_DISPATCH,\n [Action.BULK_UPSERT]: Action.BULK_UPSERT,\n};\n\nconst isEmpty = (field: unknown): boolean => {\n if ([null, undefined].includes(field as undefined)) {\n return true;\n }\n if (Array.isArray(field)) {\n return field.length === 0;\n }\n if (typeof field === 'object' && !(field instanceof Date)) {\n return Object.keys(field as Record<string, unknown>).length === 0;\n }\n return false;\n};\n\nexport const isEntityIdRequired = (action: string): boolean => !Object.hasOwn(ACTIONS_TO_OMIT_ENTITY_ID_FROM_CONTEXT, action);\n\nconst filterOutEmptyFields = (fields: string[], instance: Model) => fields.filter(field => !isEmpty(instance.get(field)) || !isEmpty(instance.previous(field)));\n\nconst getChangedFieldsRows = (instance: Model, options: CreateOptions | InstanceUpdateOptions): AuditLogRowData[] => {\n // When bulk updating in sequelize using the \"returning\" option, the instance.changed() stops working.\n // It's a known issue with sequelize.\n const changedFields: string[] = options.returning ? options.fields as string[] : (instance.changed() || []);\n // Filter customFields - we'll handle them later\n const filteredChangedFields = filterOutEmptyFields(changedFields.filter(field => field !== CUSTOM_FIELDS_PROPERTY), instance);\n return filteredChangedFields.map(property => ({\n property,\n previousValue: instance.previous(property),\n newValue: instance.get(property),\n }));\n};\n\nconst getChangedCustomFieldsRows = (instance: Model): AuditLogRowData[] => {\n // Based on the comment in getChangedFieldsRows, we cannot rely on instance.changed() when doing bulk updates with \"returning\".\n // So this seems like a possible bug.\n const changed = instance.changed();\n const customFieldsChanged = changed && changed.includes(CUSTOM_FIELDS_PROPERTY);\n if (!customFieldsChanged) {\n return [];\n }\n\n const customFields = instance.get(CUSTOM_FIELDS_PROPERTY) as Record<string, unknown>;\n const previousCustomFields = instance.previous(CUSTOM_FIELDS_PROPERTY);\n const changedCustomFields = Object.keys(customFields).filter((field) => {\n const newValue = customFields?.[field];\n const oldValue = previousCustomFields?.[field];\n return (!isEmpty(newValue) || !isEmpty(oldValue)) && !isDeepStrictEqual(newValue, oldValue);\n });\n return changedCustomFields.map(changedCustomField => ({\n property: `${CUSTOM_FIELDS_PROPERTY}.${changedCustomField}`,\n previousValue: previousCustomFields?.[changedCustomField],\n newValue: customFields?.[changedCustomField],\n }));\n};\n\ninterface AuditLogSendData extends AuditLogPayload {\n auditContext?: AuditLogContextData;\n}\n\ntype AuditLogRowSendingErrorHandler = (error: unknown, payload: AuditLogPayload) => void;\n\nclass AuditLogger {\n private readonly rabbit: RabbitMq;\n private readonly sequelize: Sequelize;\n private readonly logger: LoggerInstanceManager;\n private readonly excludeModels: string[];\n private readonly waitForTransactionCommit: boolean;\n\n constructor(options: AuditLoggerOptions) {\n this.rabbit = options.rabbit;\n this.sequelize = options.sequelize;\n this.logger = options.logger || logger;\n this.excludeModels = options.excludeModels ?? [];\n this.waitForTransactionCommit = options.waitForTransactionCommit ?? false;\n }\n\n public async manualSendAuditLog(\n { auditContext, ...payload }: AuditLogSendData,\n errorHandler: AuditLogRowSendingErrorHandler = this.logAndThrowError,\n ): Promise<string> {\n auditContext ??= getAuditContext();\n if (!auditContext) {\n return 'NO_AUDIT_CONTEXT';\n }\n try {\n // Make sure the current entity being changed it the \"main\" entity in the current audit context, and if so mark it as the \"entityId\".\n if (auditContext?.entityType?.toLowerCase() === payload.entityType?.toLowerCase() && isEntityIdRequired(auditContext.action)) {\n auditContext.entityId = payload.entityId;\n }\n await this.rabbit.sendToQueue(AUDIT_LOG_ROWS_QUEUE, payload);\n return 'SUCCESS';\n } catch (error) {\n errorHandler(error, payload);\n return 'FAILURE';\n }\n }\n\n public registerHooks(): void {\n Object.entries(this.sequelize.models).forEach(([modelName, modelType]) => {\n if (this.excludeModels.includes(modelName)) {\n return;\n }\n modelType.addHook('afterSave', async (instance, options) => {\n const sendAuditLogForModel = async () => {\n const auditContext = getAuditContext();\n if (!auditContext) return;\n\n const { id: entityId } = instance as unknown as { id: string; };\n await this.manualSendAuditLog(\n {\n entityId,\n auditContext,\n entityType: modelName,\n rows: [...getChangedFieldsRows(instance, options), ...getChangedCustomFieldsRows(instance)],\n },\n this.logError,\n );\n };\n /*\n By default sequelize doesn't keep the previous values when using a transaction\n So we want to be able to call this function after the transaction is committed\n to be able to get the previous values\n */\n if (this.waitForTransactionCommit && options.transaction) {\n options.transaction.afterCommit(sendAuditLogForModel);\n return;\n }\n await sendAuditLogForModel();\n });\n });\n }\n\n public async sendAuditLogContext(context: AuditLogContextData): Promise<void> {\n try {\n await this.rabbit.sendToQueue(AUDIT_LOG_CONTEXT_QUEUE, context);\n } catch (err) {\n this.logger.error('Failed to send audit log context', err);\n }\n }\n\n private logError: AuditLogRowSendingErrorHandler = (error: unknown, payload: AuditLogPayload) => {\n const { entityType, entityId } = payload;\n this.logger.error('Failed to send audit log rows', { entityType, entityId, error });\n };\n\n private logAndThrowError: AuditLogRowSendingErrorHandler = (error: unknown, payload: AuditLogPayload): never => {\n this.logError(error, payload);\n throw error;\n };\n}\n\nexport default AuditLogger;\n","import Network from '@autofleet/network';\nimport logger from './logger';\nimport type { FindOptions, Order, Includeable } from 'sequelize';\nimport type { AuditLogRow } from './types';\n\nconst auditMs = new Network({ serviceName: 'AUDIT_MS', timeout: 60_000, logger });\n\nconst getByEntityId = async (entityId: string): Promise<AuditLogRow[]> => {\n const { data } = await auditMs.get(`api/v1/audit-logs/${entityId}`);\n return data;\n};\n\nexport interface QueryRequestBody {\n query?: FindOptions['where'];\n order?: Order;\n attributes?: FindOptions['attributes'];\n page?: number;\n perPage?: number;\n include?: Includeable | Includeable[];\n}\n\ninterface QueryResponse {\n count: number;\n rows: AuditLogRow[];\n}\n\nconst query = async (queryRequestBody: QueryRequestBody, getAllPages = false): Promise<QueryResponse> => {\n if (getAllPages) {\n const allRows = await auditMs.getAllPagesFromQueryEndpoint<AuditLogRow>('api/v1/audit-logs/query', queryRequestBody);\n return { count: allRows.length, rows: allRows };\n }\n const { data } = await auditMs.post('api/v1/audit-logs/query', queryRequestBody);\n return data;\n};\n\nconst queryByEntityId = async (entityId: string, queryRequestBody: QueryRequestBody, getAllPages = false): Promise<QueryResponse> => {\n const query = { ...queryRequestBody, query: { ...queryRequestBody?.query, entityId } };\n if (getAllPages) {\n const allRows = await auditMs.getAllPagesFromQueryEndpoint<AuditLogRow>('api/v1/audit-logs/query', query);\n return { count: allRows.length, rows: allRows };\n }\n const { data } = await auditMs.post('api/v1/audit-logs/query', query);\n return data;\n};\n\nexport { getByEntityId, query, queryByEntityId };\n","import type { Writable } from 'node:stream';\nimport type { IncomingHttpHeaders } from 'node:http';\nimport { getCurrentPayload, getUser } from '@autofleet/zehut';\nimport logger from './logger';\nimport AuditLogger, { isEntityIdRequired } from './audit-logger';\nimport { AUDIT_LOG_CONTEXT_KEY, AUTOMATION_ID_HEADER } from './const';\nimport { ActionOrigin, type AuditLogContextData, type AuditLoggerOptions } from './types';\n\nlet auditLogger: AuditLogger | undefined;\n\nexport const innerEnableAuditing = ({ registerHooks = true, ...options }: AuditLoggerOptions): false | AuditLogger => {\n if (process.env.DISABLE_AUDIT_LOGS === 'true') {\n return false;\n }\n auditLogger = new AuditLogger(options);\n if (registerHooks) {\n auditLogger.registerHooks();\n }\n return auditLogger;\n};\n\ninterface GetAuditLogContextParams {\n action: string;\n endpoint: string;\n entityType: string;\n method: string;\n automationId?: string;\n userId?: string;\n}\nconst getAuditLogContextAndSetToContext = ({\n action, endpoint, entityType, method, automationId, userId,\n}: GetAuditLogContextParams): AuditLogContextData => {\n const performedBy = userId || (automationId ?? null);\n const actionOrigin = (userId && ActionOrigin.USER) || (automationId && ActionOrigin.AUTOMATION) || null;\n\n const auditLogContext: AuditLogContextData = {\n entityType,\n action,\n endpoint,\n method,\n performedBy,\n actionOrigin,\n };\n const currentTrace = getCurrentPayload();\n currentTrace.nonHeaderContext.set(AUDIT_LOG_CONTEXT_KEY, auditLogContext);\n\n return auditLogContext;\n};\n\ninterface PartialRequest {\n url: string;\n method: string;\n headers: IncomingHttpHeaders;\n}\n\nexport const innerSetAuditContext = async (req: PartialRequest, res: Writable, entityType: string, action: string): Promise<void> => {\n try {\n if (process.env.DISABLE_AUDIT_LOGS === 'true' || !auditLogger) {\n return;\n }\n const auditLogContext = getAuditLogContextAndSetToContext({\n action,\n endpoint: req.url,\n entityType,\n method: req.method,\n userId: getUser()?.id,\n automationId: req.headers[AUTOMATION_ID_HEADER] as string | undefined,\n });\n\n if (!isEntityIdRequired(action)) { // if it's a bulk action, we don't want to wait for the response to add the entity id\n await auditLogger.sendAuditLogContext(auditLogContext);\n return;\n }\n\n const sendAuditLogContextEvent = async () => {\n res.off('finish', sendAuditLogContextEvent);\n res.off('close', sendAuditLogContextEvent);\n res.off('error', sendAuditLogContextEvent);\n await auditLogger?.sendAuditLogContext(auditLogContext);\n };\n res.once('finish', sendAuditLogContextEvent);\n res.once('close', sendAuditLogContextEvent);\n res.once('error', sendAuditLogContextEvent);\n } catch (err) {\n logger.error('couldn\\'t set audit context', err);\n }\n};\n\nexport const setRabbitAuditContext = (entityType: string, action: string) => async (endpoint: string, { userId, automationId }: { userId: string; automationId: string; }): Promise<void> => {\n if (process.env.DISABLE_AUDIT_LOGS === 'true' || !auditLogger) {\n return;\n }\n const auditLogContext = getAuditLogContextAndSetToContext({\n action,\n endpoint,\n entityType,\n method: 'rabbit',\n userId,\n automationId,\n });\n\n await auditLogger.sendAuditLogContext(auditLogContext);\n};\n\nexport const setBullMqAuditContext = async (entityType: string, action: string, workerName: string, jobId: string): Promise<void> => {\n if (process.env.DISABLE_AUDIT_LOGS === 'true' || !auditLogger) {\n return;\n }\n const auditLogContext = getAuditLogContextAndSetToContext({\n action,\n endpoint: workerName,\n entityType,\n method: 'bull',\n automationId: jobId,\n });\n\n await auditLogger.sendAuditLogContext(auditLogContext);\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"common-CYMTaZKn.cjs","names":["logger: LoggerInstanceManager","outbreak","logger","Network","query","auditLogger: AuditLogger | undefined","AuditLogger","auditLogContext: AuditLogContextData"],"sources":["../src/types.ts","../src/const.ts","../src/logger.ts","../src/audit-logger.ts","../src/audit-ms.ts","../src/common.ts"],"sourcesContent":["import type { ModelStatic, Model, Sequelize } from 'sequelize';\nimport type RabbitMq from '@autofleet/rabbit';\nimport type { LoggerInstanceManager } from '@autofleet/logger';\nimport type { IRouter } from 'express';\nimport type { FastifyInstance } from 'fastify';\n\nexport interface AuditLoggerOptions {\n rabbit: RabbitMq;\n sequelize: Sequelize;\n logger: LoggerInstanceManager;\n router?: IRouter;\n fastify?: FastifyInstance;\n excludeModels?: string[];\n entityScopedModelMap?: Record<string, ModelStatic<Model>>;\n /**\n * If true and the model was changed inside a transaction, the audit log will be sent only after the transaction is committed.\n * This is useful because sequelize afterSave hook doesn't include the previous values if the model was changed inside a transaction.\n * @default false\n */\n waitForTransactionCommit?: boolean;\n registerHooks?: boolean;\n}\n\nexport interface AuditLogContextData {\n entityType: string;\n entityId?: string;\n action: string;\n performedBy: string | null;\n endpoint: string;\n method: string;\n actionOrigin?: string | null;\n}\n\nexport interface AuditLogRowData {\n property: string;\n previousValue: unknown;\n newValue: unknown;\n}\n\nexport interface AuditLogContext extends AuditLogContextData {\n createdAt: Date;\n}\n\nexport interface AuditLogRow extends AuditLogRowData {\n id: string;\n context: AuditLogContext[];\n createdAt: Date;\n}\n\nexport interface AuditLogPayload {\n entityType: string;\n entityId: string;\n rows: readonly AuditLogRowData[];\n}\n\nexport enum Action {\n CREATE = 'create',\n BULK_CREATE = 'bulk-create',\n BULK_EDIT = 'bulk-edit',\n DELETE = 'delete',\n UPDATE = 'update',\n CANCEL = 'cancel',\n FAIL = 'fail',\n UNASSIGN = 'unassign',\n BULK_ASSIGN = 'bulk-assign',\n REASSIGN = 'reassign',\n DISPATCH = 'dispatch',\n BULK_DISPATCH = 'bulk-dispatch',\n BULK_UPSERT = 'bulk-upsert',\n UPSERT = 'upsert',\n JOIN = 'join',\n MOVE = 'move',\n REOPTIMIZATION = 'reoptimization',\n}\n\nexport enum EntityType {\n RIDE = 'Ride',\n VEHICLE = 'Vehicle',\n DRIVER = 'Driver',\n PRICE_CALCULATION = 'PriceCalculation',\n /** Due to some legacy reasons and bad product decisions, the fleet-spec model used to be called \"config\", and we are stuck with \"Config\" this as the name in the DB. */\n ORDERING_FLEET_SPEC = 'OrderingConfig',\n ORDERING_VEHICLE = 'OrderingVehicle',\n ORDERING_ADDITIONAL_OPTION = 'OrderingAdditionalOption',\n}\n\nexport const enum ActionOrigin {\n USER = 'user',\n AUTOMATION = 'automation',\n}\n","export const AUDIT_LOG_CONTEXT_QUEUE = 'audit-log-context';\nexport const AUDIT_LOG_ROWS_QUEUE = 'audit-log-rows';\nexport const AUDIT_LOG_CONTEXT_KEY = 'auditLogContext';\nexport const AUTOMATION_ID_HEADER = 'x-af-automation-id';\n","import { outbreak } from '@autofleet/zehut';\nimport Logger, { type LoggerInstanceManager } from '@autofleet/logger';\n\nconst logger: LoggerInstanceManager = Logger();\nlogger.addContextMiddleware(() => ({ traceId: outbreak.getCurrentContextTraceId() }));\nexport default logger;\n","import { isDeepStrictEqual } from 'node:util';\nimport type RabbitMq from '@autofleet/rabbit';\nimport type { LoggerInstanceManager } from '@autofleet/logger';\nimport { getCurrentPayload as getCurrentTrace } from '@autofleet/zehut';\nimport type {\n CreateOptions, InstanceUpdateOptions, Model, Sequelize,\n} from 'sequelize';\nimport {\n type AuditLogPayload, type AuditLoggerOptions, type AuditLogContextData, Action, type AuditLogRowData,\n} from './types';\nimport { AUDIT_LOG_CONTEXT_QUEUE, AUDIT_LOG_ROWS_QUEUE, AUDIT_LOG_CONTEXT_KEY } from './const';\nimport logger from './logger';\n\nconst CUSTOM_FIELDS_PROPERTY = 'customFields';\n\nconst getAuditContext = () => {\n const currentTrace = getCurrentTrace();\n return currentTrace?.nonHeaderContext?.get(AUDIT_LOG_CONTEXT_KEY) as AuditLogContextData | undefined;\n};\n\nconst ACTIONS_TO_OMIT_ENTITY_ID_FROM_CONTEXT = {\n [Action.BULK_CREATE]: Action.BULK_CREATE,\n [Action.BULK_EDIT]: Action.BULK_EDIT,\n [Action.BULK_ASSIGN]: Action.BULK_ASSIGN,\n [Action.BULK_DISPATCH]: Action.BULK_DISPATCH,\n [Action.BULK_UPSERT]: Action.BULK_UPSERT,\n};\n\nconst isEmpty = (field: unknown): boolean => {\n if ([null, undefined].includes(field as undefined)) {\n return true;\n }\n if (Array.isArray(field)) {\n return field.length === 0;\n }\n if (typeof field === 'object' && !(field instanceof Date)) {\n return Object.keys(field as Record<string, unknown>).length === 0;\n }\n return false;\n};\n\nexport const isEntityIdRequired = (action: string): boolean => !Object.hasOwn(ACTIONS_TO_OMIT_ENTITY_ID_FROM_CONTEXT, action);\n\nconst filterOutEmptyFields = (fields: string[], instance: Model) => fields.filter(field => !isEmpty(instance.get(field)) || !isEmpty(instance.previous(field)));\n\nconst getChangedFieldsRows = (instance: Model, options: CreateOptions | InstanceUpdateOptions): AuditLogRowData[] => {\n // When bulk updating in sequelize using the \"returning\" option, the instance.changed() stops working.\n // It's a known issue with sequelize.\n const changedFields: string[] = options.returning ? options.fields as string[] : (instance.changed() || []);\n // Filter customFields - we'll handle them later\n const filteredChangedFields = filterOutEmptyFields(changedFields.filter(field => field !== CUSTOM_FIELDS_PROPERTY), instance);\n return filteredChangedFields.map(property => ({\n property,\n previousValue: instance.previous(property),\n newValue: instance.get(property),\n }));\n};\n\nconst getChangedCustomFieldsRows = (instance: Model): AuditLogRowData[] => {\n // Based on the comment in getChangedFieldsRows, we cannot rely on instance.changed() when doing bulk updates with \"returning\".\n // So this seems like a possible bug.\n const changed = instance.changed();\n const customFieldsChanged = changed && changed.includes(CUSTOM_FIELDS_PROPERTY);\n if (!customFieldsChanged) {\n return [];\n }\n\n const customFields = instance.get(CUSTOM_FIELDS_PROPERTY) as Record<string, unknown>;\n const previousCustomFields = instance.previous(CUSTOM_FIELDS_PROPERTY);\n const changedCustomFields = Object.keys(customFields).filter((field) => {\n const newValue = customFields?.[field];\n const oldValue = previousCustomFields?.[field];\n return (!isEmpty(newValue) || !isEmpty(oldValue)) && !isDeepStrictEqual(newValue, oldValue);\n });\n return changedCustomFields.map(changedCustomField => ({\n property: `${CUSTOM_FIELDS_PROPERTY}.${changedCustomField}`,\n previousValue: previousCustomFields?.[changedCustomField],\n newValue: customFields?.[changedCustomField],\n }));\n};\n\ninterface AuditLogSendData extends AuditLogPayload {\n auditContext?: AuditLogContextData;\n}\n\ntype AuditLogRowSendingErrorHandler = (error: unknown, payload: AuditLogPayload) => void;\n\nclass AuditLogger {\n private readonly rabbit: RabbitMq;\n private readonly sequelize: Sequelize;\n private readonly logger: LoggerInstanceManager;\n private readonly excludeModels: string[];\n private readonly waitForTransactionCommit: boolean;\n\n constructor(options: AuditLoggerOptions) {\n this.rabbit = options.rabbit;\n this.sequelize = options.sequelize;\n this.logger = options.logger || logger;\n this.excludeModels = options.excludeModels ?? [];\n this.waitForTransactionCommit = options.waitForTransactionCommit ?? false;\n }\n\n public async manualSendAuditLog(\n { auditContext, ...payload }: AuditLogSendData,\n errorHandler: AuditLogRowSendingErrorHandler = this.logAndThrowError,\n ): Promise<string> {\n auditContext ??= getAuditContext();\n if (!auditContext) {\n return 'NO_AUDIT_CONTEXT';\n }\n try {\n // Make sure the current entity being changed it the \"main\" entity in the current audit context, and if so mark it as the \"entityId\".\n if (auditContext?.entityType?.toLowerCase() === payload.entityType?.toLowerCase() && isEntityIdRequired(auditContext.action)) {\n auditContext.entityId = payload.entityId;\n }\n await this.rabbit.sendToQueue(AUDIT_LOG_ROWS_QUEUE, payload);\n return 'SUCCESS';\n } catch (error) {\n errorHandler(error, payload);\n return 'FAILURE';\n }\n }\n\n public registerHooks(): void {\n Object.entries(this.sequelize.models).forEach(([modelName, modelType]) => {\n if (this.excludeModels.includes(modelName)) {\n return;\n }\n modelType.addHook('afterSave', async (instance, options) => {\n const sendAuditLogForModel = async () => {\n const auditContext = getAuditContext();\n if (!auditContext) return;\n\n const { id: entityId } = instance as unknown as { id: string; };\n await this.manualSendAuditLog(\n {\n entityId,\n auditContext,\n entityType: modelName,\n rows: [...getChangedFieldsRows(instance, options), ...getChangedCustomFieldsRows(instance)],\n },\n this.logError,\n );\n };\n /*\n By default sequelize doesn't keep the previous values when using a transaction\n So we want to be able to call this function after the transaction is committed\n to be able to get the previous values\n */\n if (this.waitForTransactionCommit && options.transaction) {\n options.transaction.afterCommit(sendAuditLogForModel);\n return;\n }\n await sendAuditLogForModel();\n });\n });\n }\n\n public async sendAuditLogContext(context: AuditLogContextData): Promise<void> {\n try {\n await this.rabbit.sendToQueue(AUDIT_LOG_CONTEXT_QUEUE, context);\n } catch (err) {\n this.logger.error('Failed to send audit log context', err);\n }\n }\n\n private logError: AuditLogRowSendingErrorHandler = (error: unknown, payload: AuditLogPayload) => {\n const { entityType, entityId } = payload;\n this.logger.error('Failed to send audit log rows', { entityType, entityId, error });\n };\n\n private logAndThrowError: AuditLogRowSendingErrorHandler = (error: unknown, payload: AuditLogPayload): never => {\n this.logError(error, payload);\n throw error;\n };\n}\n\nexport default AuditLogger;\n","import Network from '@autofleet/network';\nimport logger from './logger';\nimport type { FindOptions, Order, Includeable } from 'sequelize';\nimport type { AuditLogRow } from './types';\n\nconst auditMs = new Network({ serviceName: 'AUDIT_MS', timeout: 60_000, logger });\n\nconst getByEntityId = async (entityId: string): Promise<AuditLogRow[]> => {\n const { data } = await auditMs.get(`api/v1/audit-logs/${entityId}`);\n return data;\n};\n\nexport interface QueryRequestBody {\n query?: FindOptions['where'];\n order?: Order;\n attributes?: FindOptions['attributes'];\n page?: number;\n perPage?: number;\n include?: Includeable | Includeable[];\n}\n\ninterface QueryResponse {\n count: number;\n rows: AuditLogRow[];\n}\n\nconst query = async (queryRequestBody: QueryRequestBody, getAllPages = false): Promise<QueryResponse> => {\n if (getAllPages) {\n const allRows = await auditMs.getAllPagesFromQueryEndpoint<AuditLogRow>('api/v1/audit-logs/query', queryRequestBody);\n return { count: allRows.length, rows: allRows };\n }\n const { data } = await auditMs.post('api/v1/audit-logs/query', queryRequestBody);\n return data;\n};\n\nconst queryByEntityId = async (entityId: string, queryRequestBody: QueryRequestBody, getAllPages = false): Promise<QueryResponse> => {\n const query = { ...queryRequestBody, query: { ...queryRequestBody?.query, entityId } };\n if (getAllPages) {\n const allRows = await auditMs.getAllPagesFromQueryEndpoint<AuditLogRow>('api/v1/audit-logs/query', query);\n return { count: allRows.length, rows: allRows };\n }\n const { data } = await auditMs.post('api/v1/audit-logs/query', query);\n return data;\n};\n\nexport { getByEntityId, query, queryByEntityId };\n","import type { Writable } from 'node:stream';\nimport type { IncomingHttpHeaders } from 'node:http';\nimport { getCurrentPayload, getUser } from '@autofleet/zehut';\nimport logger from './logger';\nimport AuditLogger, { isEntityIdRequired } from './audit-logger';\nimport { AUDIT_LOG_CONTEXT_KEY, AUTOMATION_ID_HEADER } from './const';\nimport { ActionOrigin, type AuditLogContextData, type AuditLoggerOptions } from './types';\n\nlet auditLogger: AuditLogger | undefined;\n\nexport const innerEnableAuditing = ({ registerHooks = true, ...options }: AuditLoggerOptions): false | AuditLogger => {\n if (process.env.DISABLE_AUDIT_LOGS === 'true') {\n return false;\n }\n auditLogger = new AuditLogger(options);\n if (registerHooks) {\n auditLogger.registerHooks();\n }\n return auditLogger;\n};\n\ninterface GetAuditLogContextParams {\n action: string;\n endpoint: string;\n entityType: string;\n method: string;\n automationId?: string;\n userId?: string;\n}\nconst getAuditLogContextAndSetToContext = ({\n action, endpoint, entityType, method, automationId, userId,\n}: GetAuditLogContextParams): AuditLogContextData => {\n const performedBy = userId || (automationId ?? null);\n const actionOrigin = (userId && ActionOrigin.USER) || (automationId && ActionOrigin.AUTOMATION) || null;\n\n const auditLogContext: AuditLogContextData = {\n entityType,\n action,\n endpoint,\n method,\n performedBy,\n actionOrigin,\n };\n const currentTrace = getCurrentPayload();\n currentTrace.nonHeaderContext.set(AUDIT_LOG_CONTEXT_KEY, auditLogContext);\n\n return auditLogContext;\n};\n\ninterface PartialRequest {\n url: string;\n method: string;\n headers: IncomingHttpHeaders;\n}\n\nexport const innerSetAuditContext = async (req: PartialRequest, res: Writable, entityType: string, action: string): Promise<void> => {\n try {\n if (process.env.DISABLE_AUDIT_LOGS === 'true' || !auditLogger) {\n return;\n }\n const auditLogContext = getAuditLogContextAndSetToContext({\n action,\n endpoint: req.url,\n entityType,\n method: req.method,\n userId: getUser()?.id,\n automationId: req.headers[AUTOMATION_ID_HEADER] as string | undefined,\n });\n\n if (!isEntityIdRequired(action)) { // if it's a bulk action, we don't want to wait for the response to add the entity id\n await auditLogger.sendAuditLogContext(auditLogContext);\n return;\n }\n\n const sendAuditLogContextEvent = async () => {\n res.off('finish', sendAuditLogContextEvent);\n res.off('close', sendAuditLogContextEvent);\n res.off('error', sendAuditLogContextEvent);\n await auditLogger?.sendAuditLogContext(auditLogContext);\n };\n res.once('finish', sendAuditLogContextEvent);\n res.once('close', sendAuditLogContextEvent);\n res.once('error', sendAuditLogContextEvent);\n } catch (err) {\n logger.error('couldn\\'t set audit context', err);\n }\n};\n\nexport const setRabbitAuditContext = (entityType: string, action: string) => async (endpoint: string, { userId, automationId }: { userId: string; automationId: string; }): Promise<void> => {\n if (process.env.DISABLE_AUDIT_LOGS === 'true' || !auditLogger) {\n return;\n }\n const auditLogContext = getAuditLogContextAndSetToContext({\n action,\n endpoint,\n entityType,\n method: 'rabbit',\n userId,\n automationId,\n });\n\n await auditLogger.sendAuditLogContext(auditLogContext);\n};\n\nexport const setBullMqAuditContext = async (entityType: string, action: string, workerName: string, jobId: string): Promise<void> => {\n if (process.env.DISABLE_AUDIT_LOGS === 'true' || !auditLogger) {\n return;\n }\n const auditLogContext = getAuditLogContextAndSetToContext({\n action,\n endpoint: workerName,\n entityType,\n method: 'bull',\n automationId: jobId,\n });\n\n await auditLogger.sendAuditLogContext(auditLogContext);\n};\n"],"mappings":"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"}
@@ -75,6 +75,7 @@ declare enum EntityType {
75
75
  /** Due to some legacy reasons and bad product decisions, the fleet-spec model used to be called "config", and we are stuck with "Config" this as the name in the DB. */
76
76
  ORDERING_FLEET_SPEC = "OrderingConfig",
77
77
  ORDERING_VEHICLE = "OrderingVehicle",
78
+ ORDERING_ADDITIONAL_OPTION = "OrderingAdditionalOption",
78
79
  }
79
80
  declare const enum ActionOrigin {
80
81
  USER = "user",
@@ -120,4 +121,4 @@ declare const AUDIT_LOG_CONTEXT_KEY = "auditLogContext";
120
121
  declare const AUTOMATION_ID_HEADER = "x-af-automation-id";
121
122
  //#endregion
122
123
  export { setBullMqAuditContext as a, Action as c, AuditLogContextData as d, AuditLogPayload as f, EntityType as g, AuditLoggerOptions as h, AUTOMATION_ID_HEADER as i, ActionOrigin as l, AuditLogRowData as m, AUDIT_LOG_CONTEXT_QUEUE as n, setRabbitAuditContext as o, AuditLogRow as p, AUDIT_LOG_ROWS_QUEUE as r, AuditLogger as s, AUDIT_LOG_CONTEXT_KEY as t, AuditLogContext as u };
123
- //# sourceMappingURL=const-BQszE8qk.d.ts.map
124
+ //# sourceMappingURL=const-B4zFkdjq.d.ts.map
@@ -75,6 +75,7 @@ declare enum EntityType {
75
75
  /** Due to some legacy reasons and bad product decisions, the fleet-spec model used to be called "config", and we are stuck with "Config" this as the name in the DB. */
76
76
  ORDERING_FLEET_SPEC = "OrderingConfig",
77
77
  ORDERING_VEHICLE = "OrderingVehicle",
78
+ ORDERING_ADDITIONAL_OPTION = "OrderingAdditionalOption",
78
79
  }
79
80
  declare const enum ActionOrigin {
80
81
  USER = "user",
@@ -120,4 +121,4 @@ declare const AUDIT_LOG_CONTEXT_KEY = "auditLogContext";
120
121
  declare const AUTOMATION_ID_HEADER = "x-af-automation-id";
121
122
  //#endregion
122
123
  export { setBullMqAuditContext as a, Action as c, AuditLogContextData as d, AuditLogPayload as f, EntityType as g, AuditLoggerOptions as h, AUTOMATION_ID_HEADER as i, ActionOrigin as l, AuditLogRowData as m, AUDIT_LOG_CONTEXT_QUEUE as n, setRabbitAuditContext as o, AuditLogRow as p, AUDIT_LOG_ROWS_QUEUE as r, AuditLogger as s, AUDIT_LOG_CONTEXT_KEY as t, AuditLogContext as u };
123
- //# sourceMappingURL=const-LUiTa-JG.d.cts.map
124
+ //# sourceMappingURL=const-DJuLl54g.d.cts.map
package/dist/fastify.cjs CHANGED
@@ -1,2 +1,2 @@
1
- Object.defineProperty(exports,`__esModule`,{value:!0});const e=require(`./common-BERxNP0v.cjs`);let t=require(`@autofleet/errors`);const n={name:`Audit`,description:`Audit logs API`};function r({fastify:r,logger:i,entityScopedModelMap:a}){!a||!r||Object.entries(a).forEach(([a,o])=>{r.post(`/api/v1/${a}/:id/audit`,{schema:{tags:[n.name],description:`Query audit logs for ${a} by ID`,params:{type:`object`,properties:{id:{type:`string`}},required:[`id`]}}},async n=>{let{id:r}=n.params;if(!await o.findByPk(r)){let e=new t.ResourceNotFoundError;throw i.error(`Entity ${a} with id ${r} not found`,{err:e}),e}return e.o(r,n.body)})})}const i=t=>{let n=e.t(t);if(n)return r(t),n},a=(t,n)=>async(r,i)=>{await e.n(r,i.raw,t,n)};var o=e.s;exports.AUDIT_LOG_CONTEXT_KEY=e.c,exports.AUDIT_LOG_CONTEXT_QUEUE=e.l,exports.AUDIT_LOG_ROWS_QUEUE=e.u,exports.AUTOMATION_ID_HEADER=e.d,exports.Action=e.f,exports.ActionOrigin=e.p,exports.EntityType=e.m,exports.SWAGGER_TAG=n,exports.default=o,exports.enableAuditing=i,exports.setAuditContext=a,exports.setBullMqAuditContext=e.r,exports.setRabbitAuditContext=e.i;
1
+ Object.defineProperty(exports,`__esModule`,{value:!0});const e=require(`./common-CYMTaZKn.cjs`);let t=require(`@autofleet/errors`);const n={name:`Audit`,description:`Audit logs API`};function r({fastify:r,logger:i,entityScopedModelMap:a}){!a||!r||Object.entries(a).forEach(([a,o])=>{r.post(`/api/v1/${a}/:id/audit`,{schema:{tags:[n.name],description:`Query audit logs for ${a} by ID`,params:{type:`object`,properties:{id:{type:`string`}},required:[`id`]}}},async n=>{let{id:r}=n.params;if(!await o.findByPk(r)){let e=new t.ResourceNotFoundError;throw i.error(`Entity ${a} with id ${r} not found`,{err:e}),e}return e.o(r,n.body)})})}const i=t=>{let n=e.t(t);if(n)return r(t),n},a=(t,n)=>async(r,i)=>{await e.n(r,i.raw,t,n)};var o=e.s;exports.AUDIT_LOG_CONTEXT_KEY=e.c,exports.AUDIT_LOG_CONTEXT_QUEUE=e.l,exports.AUDIT_LOG_ROWS_QUEUE=e.u,exports.AUTOMATION_ID_HEADER=e.d,exports.Action=e.f,exports.ActionOrigin=e.p,exports.EntityType=e.m,exports.SWAGGER_TAG=n,exports.default=o,exports.enableAuditing=i,exports.setAuditContext=a,exports.setBullMqAuditContext=e.r,exports.setRabbitAuditContext=e.i;
2
2
  //# sourceMappingURL=fastify.cjs.map
@@ -1,4 +1,4 @@
1
- import { a as setBullMqAuditContext, c as Action, d as AuditLogContextData, f as AuditLogPayload, g as EntityType, h as AuditLoggerOptions, i as AUTOMATION_ID_HEADER, l as ActionOrigin, m as AuditLogRowData, n as AUDIT_LOG_CONTEXT_QUEUE, o as setRabbitAuditContext, p as AuditLogRow, r as AUDIT_LOG_ROWS_QUEUE, s as AuditLogger, t as AUDIT_LOG_CONTEXT_KEY, u as AuditLogContext } from "./const-LUiTa-JG.cjs";
1
+ import { a as setBullMqAuditContext, c as Action, d as AuditLogContextData, f as AuditLogPayload, g as EntityType, h as AuditLoggerOptions, i as AUTOMATION_ID_HEADER, l as ActionOrigin, m as AuditLogRowData, n as AUDIT_LOG_CONTEXT_QUEUE, o as setRabbitAuditContext, p as AuditLogRow, r as AUDIT_LOG_ROWS_QUEUE, s as AuditLogger, t as AUDIT_LOG_CONTEXT_KEY, u as AuditLogContext } from "./const-DJuLl54g.cjs";
2
2
  import { preHandlerAsyncHookHandler } from "fastify";
3
3
 
4
4
  //#region src/audit-api/fastify.d.ts
package/dist/fastify.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as setBullMqAuditContext, c as Action, d as AuditLogContextData, f as AuditLogPayload, g as EntityType, h as AuditLoggerOptions, i as AUTOMATION_ID_HEADER, l as ActionOrigin, m as AuditLogRowData, n as AUDIT_LOG_CONTEXT_QUEUE, o as setRabbitAuditContext, p as AuditLogRow, r as AUDIT_LOG_ROWS_QUEUE, s as AuditLogger, t as AUDIT_LOG_CONTEXT_KEY, u as AuditLogContext } from "./const-BQszE8qk.js";
1
+ import { a as setBullMqAuditContext, c as Action, d as AuditLogContextData, f as AuditLogPayload, g as EntityType, h as AuditLoggerOptions, i as AUTOMATION_ID_HEADER, l as ActionOrigin, m as AuditLogRowData, n as AUDIT_LOG_CONTEXT_QUEUE, o as setRabbitAuditContext, p as AuditLogRow, r as AUDIT_LOG_ROWS_QUEUE, s as AuditLogger, t as AUDIT_LOG_CONTEXT_KEY, u as AuditLogContext } from "./const-B4zFkdjq.js";
2
2
  import { preHandlerAsyncHookHandler } from "fastify";
3
3
 
4
4
  //#region src/audit-api/fastify.d.ts
package/dist/fastify.js CHANGED
@@ -1,2 +1,2 @@
1
- import{c as e,d as t,f as n,i as r,l as i,m as a,n as o,o as s,p as c,r as l,s as u,t as d,u as f}from"./common-BGDD7JIS.js";import{ResourceNotFoundError as p}from"@autofleet/errors";const m={name:`Audit`,description:`Audit logs API`};function h({fastify:e,logger:t,entityScopedModelMap:n}){!n||!e||Object.entries(n).forEach(([n,r])=>{e.post(`/api/v1/${n}/:id/audit`,{schema:{tags:[m.name],description:`Query audit logs for ${n} by ID`,params:{type:`object`,properties:{id:{type:`string`}},required:[`id`]}}},async e=>{let{id:i}=e.params;if(!await r.findByPk(i)){let e=new p;throw t.error(`Entity ${n} with id ${i} not found`,{err:e}),e}return s(i,e.body)})})}const g=e=>{let t=d(e);if(t)return h(e),t},_=(e,t)=>async(n,r)=>{await o(n,r.raw,e,t)};var v=u;export{e as AUDIT_LOG_CONTEXT_KEY,i as AUDIT_LOG_CONTEXT_QUEUE,f as AUDIT_LOG_ROWS_QUEUE,t as AUTOMATION_ID_HEADER,n as Action,c as ActionOrigin,a as EntityType,m as SWAGGER_TAG,v as default,g as enableAuditing,_ as setAuditContext,l as setBullMqAuditContext,r as setRabbitAuditContext};
1
+ import{c as e,d as t,f as n,i as r,l as i,m as a,n as o,o as s,p as c,r as l,s as u,t as d,u as f}from"./common-BWe107dZ.js";import{ResourceNotFoundError as p}from"@autofleet/errors";const m={name:`Audit`,description:`Audit logs API`};function h({fastify:e,logger:t,entityScopedModelMap:n}){!n||!e||Object.entries(n).forEach(([n,r])=>{e.post(`/api/v1/${n}/:id/audit`,{schema:{tags:[m.name],description:`Query audit logs for ${n} by ID`,params:{type:`object`,properties:{id:{type:`string`}},required:[`id`]}}},async e=>{let{id:i}=e.params;if(!await r.findByPk(i)){let e=new p;throw t.error(`Entity ${n} with id ${i} not found`,{err:e}),e}return s(i,e.body)})})}const g=e=>{let t=d(e);if(t)return h(e),t},_=(e,t)=>async(n,r)=>{await o(n,r.raw,e,t)};var v=u;export{e as AUDIT_LOG_CONTEXT_KEY,i as AUDIT_LOG_CONTEXT_QUEUE,f as AUDIT_LOG_ROWS_QUEUE,t as AUTOMATION_ID_HEADER,n as Action,c as ActionOrigin,a as EntityType,m as SWAGGER_TAG,v as default,g as enableAuditing,_ as setAuditContext,l as setBullMqAuditContext,r as setRabbitAuditContext};
2
2
  //# sourceMappingURL=fastify.js.map
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- Object.defineProperty(exports,`__esModule`,{value:!0});const e=require(`./common-BERxNP0v.cjs`);let t=require(`@autofleet/errors`);function n({router:n,logger:r,entityScopedModelMap:i}){!i||!n||Object.entries(i).forEach(([i,a])=>{n.post(`/${i}/:id/audit`,async(n,o)=>{try{let{id:s}=n.params;if(!await a.findByPk(s))return(0,t.handleError)(new t.ResourceNotFoundError,o,{logger:r,message:`Entity ${i} with id ${s} not found`});let c=await e.o(s,n.body);return o.json(c)}catch(e){return(0,t.handleError)(new t.UnexpectedError(e),o,{logger:r})}})})}const r=t=>{let r=e.t(t);if(r)return n(t),r},i=(t,n)=>async(r,i,a)=>(await e.n(r,i,t,n),a());var a=e.s;exports.AUDIT_LOG_CONTEXT_KEY=e.c,exports.AUDIT_LOG_CONTEXT_QUEUE=e.l,exports.AUDIT_LOG_ROWS_QUEUE=e.u,exports.AUTOMATION_ID_HEADER=e.d,exports.Action=e.f,exports.ActionOrigin=e.p,Object.defineProperty(exports,`AuditMs`,{enumerable:!0,get:function(){return e.a}}),exports.EntityType=e.m,exports.default=a,exports.enableAuditing=r,exports.setAuditContext=i,exports.setBullMqAuditContext=e.r,exports.setRabbitAuditContext=e.i;
1
+ Object.defineProperty(exports,`__esModule`,{value:!0});const e=require(`./common-CYMTaZKn.cjs`);let t=require(`@autofleet/errors`);function n({router:n,logger:r,entityScopedModelMap:i}){!i||!n||Object.entries(i).forEach(([i,a])=>{n.post(`/${i}/:id/audit`,async(n,o)=>{try{let{id:s}=n.params;if(!await a.findByPk(s))return(0,t.handleError)(new t.ResourceNotFoundError,o,{logger:r,message:`Entity ${i} with id ${s} not found`});let c=await e.o(s,n.body);return o.json(c)}catch(e){return(0,t.handleError)(new t.UnexpectedError(e),o,{logger:r})}})})}const r=t=>{let r=e.t(t);if(r)return n(t),r},i=(t,n)=>async(r,i,a)=>(await e.n(r,i,t,n),a());var a=e.s;exports.AUDIT_LOG_CONTEXT_KEY=e.c,exports.AUDIT_LOG_CONTEXT_QUEUE=e.l,exports.AUDIT_LOG_ROWS_QUEUE=e.u,exports.AUTOMATION_ID_HEADER=e.d,exports.Action=e.f,exports.ActionOrigin=e.p,Object.defineProperty(exports,`AuditMs`,{enumerable:!0,get:function(){return e.a}}),exports.EntityType=e.m,exports.default=a,exports.enableAuditing=r,exports.setAuditContext=i,exports.setBullMqAuditContext=e.r,exports.setRabbitAuditContext=e.i;
2
2
  //# sourceMappingURL=index.cjs.map
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as setBullMqAuditContext, c as Action, d as AuditLogContextData, f as AuditLogPayload, g as EntityType, h as AuditLoggerOptions, i as AUTOMATION_ID_HEADER, l as ActionOrigin, m as AuditLogRowData, n as AUDIT_LOG_CONTEXT_QUEUE, o as setRabbitAuditContext, p as AuditLogRow, r as AUDIT_LOG_ROWS_QUEUE, s as AuditLogger, t as AUDIT_LOG_CONTEXT_KEY, u as AuditLogContext } from "./const-LUiTa-JG.cjs";
1
+ import { a as setBullMqAuditContext, c as Action, d as AuditLogContextData, f as AuditLogPayload, g as EntityType, h as AuditLoggerOptions, i as AUTOMATION_ID_HEADER, l as ActionOrigin, m as AuditLogRowData, n as AUDIT_LOG_CONTEXT_QUEUE, o as setRabbitAuditContext, p as AuditLogRow, r as AUDIT_LOG_ROWS_QUEUE, s as AuditLogger, t as AUDIT_LOG_CONTEXT_KEY, u as AuditLogContext } from "./const-DJuLl54g.cjs";
2
2
  import { FindOptions, Includeable, Order } from "sequelize";
3
3
  import { NextFunction, Request, Response } from "express";
4
4
 
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as setBullMqAuditContext, c as Action, d as AuditLogContextData, f as AuditLogPayload, g as EntityType, h as AuditLoggerOptions, i as AUTOMATION_ID_HEADER, l as ActionOrigin, m as AuditLogRowData, n as AUDIT_LOG_CONTEXT_QUEUE, o as setRabbitAuditContext, p as AuditLogRow, r as AUDIT_LOG_ROWS_QUEUE, s as AuditLogger, t as AUDIT_LOG_CONTEXT_KEY, u as AuditLogContext } from "./const-BQszE8qk.js";
1
+ import { a as setBullMqAuditContext, c as Action, d as AuditLogContextData, f as AuditLogPayload, g as EntityType, h as AuditLoggerOptions, i as AUTOMATION_ID_HEADER, l as ActionOrigin, m as AuditLogRowData, n as AUDIT_LOG_CONTEXT_QUEUE, o as setRabbitAuditContext, p as AuditLogRow, r as AUDIT_LOG_ROWS_QUEUE, s as AuditLogger, t as AUDIT_LOG_CONTEXT_KEY, u as AuditLogContext } from "./const-B4zFkdjq.js";
2
2
  import { FindOptions, Includeable, Order } from "sequelize";
3
3
  import { NextFunction, Request, Response } from "express";
4
4
 
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import{a as e,c as t,d as n,f as r,i,l as a,m as o,n as s,o as c,p as l,r as u,s as d,t as f,u as p}from"./common-BGDD7JIS.js";import{ResourceNotFoundError as m,UnexpectedError as h,handleError as g}from"@autofleet/errors";function _({router:e,logger:t,entityScopedModelMap:n}){!n||!e||Object.entries(n).forEach(([n,r])=>{e.post(`/${n}/:id/audit`,async(e,i)=>{try{let{id:a}=e.params;if(!await r.findByPk(a))return g(new m,i,{logger:t,message:`Entity ${n} with id ${a} not found`});let o=await c(a,e.body);return i.json(o)}catch(e){return g(new h(e),i,{logger:t})}})})}const v=e=>{let t=f(e);if(t)return _(e),t},y=(e,t)=>async(n,r,i)=>(await s(n,r,e,t),i());var b=d;export{t as AUDIT_LOG_CONTEXT_KEY,a as AUDIT_LOG_CONTEXT_QUEUE,p as AUDIT_LOG_ROWS_QUEUE,n as AUTOMATION_ID_HEADER,r as Action,l as ActionOrigin,e as AuditMs,o as EntityType,b as default,v as enableAuditing,y as setAuditContext,u as setBullMqAuditContext,i as setRabbitAuditContext};
1
+ import{a as e,c as t,d as n,f as r,i,l as a,m as o,n as s,o as c,p as l,r as u,s as d,t as f,u as p}from"./common-BWe107dZ.js";import{ResourceNotFoundError as m,UnexpectedError as h,handleError as g}from"@autofleet/errors";function _({router:e,logger:t,entityScopedModelMap:n}){!n||!e||Object.entries(n).forEach(([n,r])=>{e.post(`/${n}/:id/audit`,async(e,i)=>{try{let{id:a}=e.params;if(!await r.findByPk(a))return g(new m,i,{logger:t,message:`Entity ${n} with id ${a} not found`});let o=await c(a,e.body);return i.json(o)}catch(e){return g(new h(e),i,{logger:t})}})})}const v=e=>{let t=f(e);if(t)return _(e),t},y=(e,t)=>async(n,r,i)=>(await s(n,r,e,t),i());var b=d;export{t as AUDIT_LOG_CONTEXT_KEY,a as AUDIT_LOG_CONTEXT_QUEUE,p as AUDIT_LOG_ROWS_QUEUE,n as AUTOMATION_ID_HEADER,r as Action,l as ActionOrigin,e as AuditMs,o as EntityType,b as default,v as enableAuditing,y as setAuditContext,u as setBullMqAuditContext,i as setRabbitAuditContext};
2
2
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autofleet/shtinker",
3
- "version": "3.11.6",
3
+ "version": "3.12.1",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -31,7 +31,7 @@
31
31
  "node": ">=18"
32
32
  },
33
33
  "dependencies": {
34
- "@autofleet/network": "^1.12.3"
34
+ "@autofleet/network": "^1.12.4"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@types/express": "^4.17.17",
@@ -41,8 +41,8 @@
41
41
  "sequelize": "^6.37.7",
42
42
  "supertest": "^7.0.0",
43
43
  "ts-node": "^10.9.2",
44
- "@autofleet/zehut": "^4.12.6",
45
- "@autofleet/errors": "^3.1.51"
44
+ "@autofleet/errors": "^3.1.52",
45
+ "@autofleet/zehut": "^4.12.7"
46
46
  },
47
47
  "peerDependencies": {
48
48
  "@autofleet/errors": "^3",
@@ -1,2 +0,0 @@
1
- var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,n)=>{let r={};for(var i in e)t(r,i,{get:e[i],enumerable:!0});return n&&t(r,Symbol.toStringTag,{value:`Module`}),r},s=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},c=(n,r,a)=>(a=n==null?{}:e(i(n)),s(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let l=require(`node:util`),u=require(`@autofleet/zehut`),d=require(`@autofleet/logger`);d=c(d);let f=require(`@autofleet/network`);f=c(f);let p=function(e){return e.CREATE=`create`,e.BULK_CREATE=`bulk-create`,e.BULK_EDIT=`bulk-edit`,e.DELETE=`delete`,e.UPDATE=`update`,e.CANCEL=`cancel`,e.FAIL=`fail`,e.UNASSIGN=`unassign`,e.BULK_ASSIGN=`bulk-assign`,e.REASSIGN=`reassign`,e.DISPATCH=`dispatch`,e.BULK_DISPATCH=`bulk-dispatch`,e.BULK_UPSERT=`bulk-upsert`,e.UPSERT=`upsert`,e.JOIN=`join`,e.MOVE=`move`,e.REOPTIMIZATION=`reoptimization`,e}({}),m=function(e){return e.RIDE=`Ride`,e.VEHICLE=`Vehicle`,e.DRIVER=`Driver`,e.PRICE_CALCULATION=`PriceCalculation`,e.ORDERING_FLEET_SPEC=`OrderingConfig`,e.ORDERING_VEHICLE=`OrderingVehicle`,e}({}),h=function(e){return e.USER=`user`,e.AUTOMATION=`automation`,e}({});const g=`audit-log-context`,_=`audit-log-rows`,v=`auditLogContext`,y=`x-af-automation-id`,b=(0,d.default)();b.addContextMiddleware(()=>({traceId:u.outbreak.getCurrentContextTraceId()}));var x=b;const S=`customFields`,C=()=>(0,u.getCurrentPayload)()?.nonHeaderContext?.get(v),w={[p.BULK_CREATE]:p.BULK_CREATE,[p.BULK_EDIT]:p.BULK_EDIT,[p.BULK_ASSIGN]:p.BULK_ASSIGN,[p.BULK_DISPATCH]:p.BULK_DISPATCH,[p.BULK_UPSERT]:p.BULK_UPSERT},T=e=>[null,void 0].includes(e)?!0:Array.isArray(e)?e.length===0:typeof e==`object`&&!(e instanceof Date)?Object.keys(e).length===0:!1,E=e=>!Object.hasOwn(w,e),D=(e,t)=>e.filter(e=>!T(t.get(e))||!T(t.previous(e))),O=(e,t)=>D((t.returning?t.fields:e.changed()||[]).filter(e=>e!==S),e).map(t=>({property:t,previousValue:e.previous(t),newValue:e.get(t)})),k=e=>{let t=e.changed();if(!(t&&t.includes(S)))return[];let n=e.get(S),r=e.previous(S);return Object.keys(n).filter(e=>{let t=n?.[e],i=r?.[e];return(!T(t)||!T(i))&&!(0,l.isDeepStrictEqual)(t,i)}).map(e=>({property:`${S}.${e}`,previousValue:r?.[e],newValue:n?.[e]}))};var A=class{rabbit;sequelize;logger;excludeModels;waitForTransactionCommit;constructor(e){this.rabbit=e.rabbit,this.sequelize=e.sequelize,this.logger=e.logger||x,this.excludeModels=e.excludeModels??[],this.waitForTransactionCommit=e.waitForTransactionCommit??!1}async manualSendAuditLog({auditContext:e,...t},n=this.logAndThrowError){if(e??=C(),!e)return`NO_AUDIT_CONTEXT`;try{return e?.entityType?.toLowerCase()===t.entityType?.toLowerCase()&&E(e.action)&&(e.entityId=t.entityId),await this.rabbit.sendToQueue(_,t),`SUCCESS`}catch(e){return n(e,t),`FAILURE`}}registerHooks(){Object.entries(this.sequelize.models).forEach(([e,t])=>{this.excludeModels.includes(e)||t.addHook(`afterSave`,async(t,n)=>{let r=async()=>{let r=C();if(!r)return;let{id:i}=t;await this.manualSendAuditLog({entityId:i,auditContext:r,entityType:e,rows:[...O(t,n),...k(t)]},this.logError)};if(this.waitForTransactionCommit&&n.transaction){n.transaction.afterCommit(r);return}await r()})})}async sendAuditLogContext(e){try{await this.rabbit.sendToQueue(g,e)}catch(e){this.logger.error(`Failed to send audit log context`,e)}}logError=(e,t)=>{let{entityType:n,entityId:r}=t;this.logger.error(`Failed to send audit log rows`,{entityType:n,entityId:r,error:e})};logAndThrowError=(e,t)=>{throw this.logError(e,t),e}},j=A,M=o({getByEntityId:()=>P,query:()=>F,queryByEntityId:()=>I});const N=new f.default({serviceName:`AUDIT_MS`,timeout:6e4,logger:x}),P=async e=>{let{data:t}=await N.get(`api/v1/audit-logs/${e}`);return t},F=async(e,t=!1)=>{if(t){let t=await N.getAllPagesFromQueryEndpoint(`api/v1/audit-logs/query`,e);return{count:t.length,rows:t}}let{data:n}=await N.post(`api/v1/audit-logs/query`,e);return n},I=async(e,t,n=!1)=>{let r={...t,query:{...t?.query,entityId:e}};if(n){let e=await N.getAllPagesFromQueryEndpoint(`api/v1/audit-logs/query`,r);return{count:e.length,rows:e}}let{data:i}=await N.post(`api/v1/audit-logs/query`,r);return i};let L;const R=({registerHooks:e=!0,...t})=>process.env.DISABLE_AUDIT_LOGS===`true`?!1:(L=new j(t),e&&L.registerHooks(),L),z=({action:e,endpoint:t,entityType:n,method:r,automationId:i,userId:a})=>{let o={entityType:n,action:e,endpoint:t,method:r,performedBy:a||(i??null),actionOrigin:a&&h.USER||i&&h.AUTOMATION||null};return(0,u.getCurrentPayload)().nonHeaderContext.set(v,o),o},B=async(e,t,n,r)=>{try{if(process.env.DISABLE_AUDIT_LOGS===`true`||!L)return;let i=z({action:r,endpoint:e.url,entityType:n,method:e.method,userId:(0,u.getUser)()?.id,automationId:e.headers[y]});if(!E(r)){await L.sendAuditLogContext(i);return}let a=async()=>{t.off(`finish`,a),t.off(`close`,a),t.off(`error`,a),await L?.sendAuditLogContext(i)};t.once(`finish`,a),t.once(`close`,a),t.once(`error`,a)}catch(e){x.error(`couldn't set audit context`,e)}},V=(e,t)=>async(n,{userId:r,automationId:i})=>{if(process.env.DISABLE_AUDIT_LOGS===`true`||!L)return;let a=z({action:t,endpoint:n,entityType:e,method:`rabbit`,userId:r,automationId:i});await L.sendAuditLogContext(a)},H=async(e,t,n,r)=>{if(process.env.DISABLE_AUDIT_LOGS===`true`||!L)return;let i=z({action:t,endpoint:n,entityType:e,method:`bull`,automationId:r});await L.sendAuditLogContext(i)};Object.defineProperty(exports,`a`,{enumerable:!0,get:function(){return M}}),Object.defineProperty(exports,`c`,{enumerable:!0,get:function(){return v}}),Object.defineProperty(exports,`d`,{enumerable:!0,get:function(){return y}}),Object.defineProperty(exports,`f`,{enumerable:!0,get:function(){return p}}),Object.defineProperty(exports,`h`,{enumerable:!0,get:function(){return c}}),Object.defineProperty(exports,`i`,{enumerable:!0,get:function(){return V}}),Object.defineProperty(exports,`l`,{enumerable:!0,get:function(){return g}}),Object.defineProperty(exports,`m`,{enumerable:!0,get:function(){return m}}),Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return B}}),Object.defineProperty(exports,`o`,{enumerable:!0,get:function(){return I}}),Object.defineProperty(exports,`p`,{enumerable:!0,get:function(){return h}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return H}}),Object.defineProperty(exports,`s`,{enumerable:!0,get:function(){return j}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return R}}),Object.defineProperty(exports,`u`,{enumerable:!0,get:function(){return _}});
2
- //# sourceMappingURL=common-BERxNP0v.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"common-BERxNP0v.cjs","names":["logger: LoggerInstanceManager","outbreak","logger","Network","query","auditLogger: AuditLogger | undefined","AuditLogger","auditLogContext: AuditLogContextData"],"sources":["../src/types.ts","../src/const.ts","../src/logger.ts","../src/audit-logger.ts","../src/audit-ms.ts","../src/common.ts"],"sourcesContent":["import type { ModelStatic, Model, Sequelize } from 'sequelize';\nimport type RabbitMq from '@autofleet/rabbit';\nimport type { LoggerInstanceManager } from '@autofleet/logger';\nimport type { IRouter } from 'express';\nimport type { FastifyInstance } from 'fastify';\n\nexport interface AuditLoggerOptions {\n rabbit: RabbitMq;\n sequelize: Sequelize;\n logger: LoggerInstanceManager;\n router?: IRouter;\n fastify?: FastifyInstance;\n excludeModels?: string[];\n entityScopedModelMap?: Record<string, ModelStatic<Model>>;\n /**\n * If true and the model was changed inside a transaction, the audit log will be sent only after the transaction is committed.\n * This is useful because sequelize afterSave hook doesn't include the previous values if the model was changed inside a transaction.\n * @default false\n */\n waitForTransactionCommit?: boolean;\n registerHooks?: boolean;\n}\n\nexport interface AuditLogContextData {\n entityType: string;\n entityId?: string;\n action: string;\n performedBy: string | null;\n endpoint: string;\n method: string;\n actionOrigin?: string | null;\n}\n\nexport interface AuditLogRowData {\n property: string;\n previousValue: unknown;\n newValue: unknown;\n}\n\nexport interface AuditLogContext extends AuditLogContextData {\n createdAt: Date;\n}\n\nexport interface AuditLogRow extends AuditLogRowData {\n id: string;\n context: AuditLogContext[];\n createdAt: Date;\n}\n\nexport interface AuditLogPayload {\n entityType: string;\n entityId: string;\n rows: readonly AuditLogRowData[];\n}\n\nexport enum Action {\n CREATE = 'create',\n BULK_CREATE = 'bulk-create',\n BULK_EDIT = 'bulk-edit',\n DELETE = 'delete',\n UPDATE = 'update',\n CANCEL = 'cancel',\n FAIL = 'fail',\n UNASSIGN = 'unassign',\n BULK_ASSIGN = 'bulk-assign',\n REASSIGN = 'reassign',\n DISPATCH = 'dispatch',\n BULK_DISPATCH = 'bulk-dispatch',\n BULK_UPSERT = 'bulk-upsert',\n UPSERT = 'upsert',\n JOIN = 'join',\n MOVE = 'move',\n REOPTIMIZATION = 'reoptimization',\n}\n\nexport enum EntityType {\n RIDE = 'Ride',\n VEHICLE = 'Vehicle',\n DRIVER = 'Driver',\n PRICE_CALCULATION = 'PriceCalculation',\n /** Due to some legacy reasons and bad product decisions, the fleet-spec model used to be called \"config\", and we are stuck with \"Config\" this as the name in the DB. */\n ORDERING_FLEET_SPEC = 'OrderingConfig',\n ORDERING_VEHICLE = 'OrderingVehicle',\n}\n\nexport const enum ActionOrigin {\n USER = 'user',\n AUTOMATION = 'automation',\n}\n","export const AUDIT_LOG_CONTEXT_QUEUE = 'audit-log-context';\nexport const AUDIT_LOG_ROWS_QUEUE = 'audit-log-rows';\nexport const AUDIT_LOG_CONTEXT_KEY = 'auditLogContext';\nexport const AUTOMATION_ID_HEADER = 'x-af-automation-id';\n","import { outbreak } from '@autofleet/zehut';\nimport Logger, { type LoggerInstanceManager } from '@autofleet/logger';\n\nconst logger: LoggerInstanceManager = Logger();\nlogger.addContextMiddleware(() => ({ traceId: outbreak.getCurrentContextTraceId() }));\nexport default logger;\n","import { isDeepStrictEqual } from 'node:util';\nimport type RabbitMq from '@autofleet/rabbit';\nimport type { LoggerInstanceManager } from '@autofleet/logger';\nimport { getCurrentPayload as getCurrentTrace } from '@autofleet/zehut';\nimport type {\n CreateOptions, InstanceUpdateOptions, Model, Sequelize,\n} from 'sequelize';\nimport {\n type AuditLogPayload, type AuditLoggerOptions, type AuditLogContextData, Action, type AuditLogRowData,\n} from './types';\nimport { AUDIT_LOG_CONTEXT_QUEUE, AUDIT_LOG_ROWS_QUEUE, AUDIT_LOG_CONTEXT_KEY } from './const';\nimport logger from './logger';\n\nconst CUSTOM_FIELDS_PROPERTY = 'customFields';\n\nconst getAuditContext = () => {\n const currentTrace = getCurrentTrace();\n return currentTrace?.nonHeaderContext?.get(AUDIT_LOG_CONTEXT_KEY) as AuditLogContextData | undefined;\n};\n\nconst ACTIONS_TO_OMIT_ENTITY_ID_FROM_CONTEXT = {\n [Action.BULK_CREATE]: Action.BULK_CREATE,\n [Action.BULK_EDIT]: Action.BULK_EDIT,\n [Action.BULK_ASSIGN]: Action.BULK_ASSIGN,\n [Action.BULK_DISPATCH]: Action.BULK_DISPATCH,\n [Action.BULK_UPSERT]: Action.BULK_UPSERT,\n};\n\nconst isEmpty = (field: unknown): boolean => {\n if ([null, undefined].includes(field as undefined)) {\n return true;\n }\n if (Array.isArray(field)) {\n return field.length === 0;\n }\n if (typeof field === 'object' && !(field instanceof Date)) {\n return Object.keys(field as Record<string, unknown>).length === 0;\n }\n return false;\n};\n\nexport const isEntityIdRequired = (action: string): boolean => !Object.hasOwn(ACTIONS_TO_OMIT_ENTITY_ID_FROM_CONTEXT, action);\n\nconst filterOutEmptyFields = (fields: string[], instance: Model) => fields.filter(field => !isEmpty(instance.get(field)) || !isEmpty(instance.previous(field)));\n\nconst getChangedFieldsRows = (instance: Model, options: CreateOptions | InstanceUpdateOptions): AuditLogRowData[] => {\n // When bulk updating in sequelize using the \"returning\" option, the instance.changed() stops working.\n // It's a known issue with sequelize.\n const changedFields: string[] = options.returning ? options.fields as string[] : (instance.changed() || []);\n // Filter customFields - we'll handle them later\n const filteredChangedFields = filterOutEmptyFields(changedFields.filter(field => field !== CUSTOM_FIELDS_PROPERTY), instance);\n return filteredChangedFields.map(property => ({\n property,\n previousValue: instance.previous(property),\n newValue: instance.get(property),\n }));\n};\n\nconst getChangedCustomFieldsRows = (instance: Model): AuditLogRowData[] => {\n // Based on the comment in getChangedFieldsRows, we cannot rely on instance.changed() when doing bulk updates with \"returning\".\n // So this seems like a possible bug.\n const changed = instance.changed();\n const customFieldsChanged = changed && changed.includes(CUSTOM_FIELDS_PROPERTY);\n if (!customFieldsChanged) {\n return [];\n }\n\n const customFields = instance.get(CUSTOM_FIELDS_PROPERTY) as Record<string, unknown>;\n const previousCustomFields = instance.previous(CUSTOM_FIELDS_PROPERTY);\n const changedCustomFields = Object.keys(customFields).filter((field) => {\n const newValue = customFields?.[field];\n const oldValue = previousCustomFields?.[field];\n return (!isEmpty(newValue) || !isEmpty(oldValue)) && !isDeepStrictEqual(newValue, oldValue);\n });\n return changedCustomFields.map(changedCustomField => ({\n property: `${CUSTOM_FIELDS_PROPERTY}.${changedCustomField}`,\n previousValue: previousCustomFields?.[changedCustomField],\n newValue: customFields?.[changedCustomField],\n }));\n};\n\ninterface AuditLogSendData extends AuditLogPayload {\n auditContext?: AuditLogContextData;\n}\n\ntype AuditLogRowSendingErrorHandler = (error: unknown, payload: AuditLogPayload) => void;\n\nclass AuditLogger {\n private readonly rabbit: RabbitMq;\n private readonly sequelize: Sequelize;\n private readonly logger: LoggerInstanceManager;\n private readonly excludeModels: string[];\n private readonly waitForTransactionCommit: boolean;\n\n constructor(options: AuditLoggerOptions) {\n this.rabbit = options.rabbit;\n this.sequelize = options.sequelize;\n this.logger = options.logger || logger;\n this.excludeModels = options.excludeModels ?? [];\n this.waitForTransactionCommit = options.waitForTransactionCommit ?? false;\n }\n\n public async manualSendAuditLog(\n { auditContext, ...payload }: AuditLogSendData,\n errorHandler: AuditLogRowSendingErrorHandler = this.logAndThrowError,\n ): Promise<string> {\n auditContext ??= getAuditContext();\n if (!auditContext) {\n return 'NO_AUDIT_CONTEXT';\n }\n try {\n // Make sure the current entity being changed it the \"main\" entity in the current audit context, and if so mark it as the \"entityId\".\n if (auditContext?.entityType?.toLowerCase() === payload.entityType?.toLowerCase() && isEntityIdRequired(auditContext.action)) {\n auditContext.entityId = payload.entityId;\n }\n await this.rabbit.sendToQueue(AUDIT_LOG_ROWS_QUEUE, payload);\n return 'SUCCESS';\n } catch (error) {\n errorHandler(error, payload);\n return 'FAILURE';\n }\n }\n\n public registerHooks(): void {\n Object.entries(this.sequelize.models).forEach(([modelName, modelType]) => {\n if (this.excludeModels.includes(modelName)) {\n return;\n }\n modelType.addHook('afterSave', async (instance, options) => {\n const sendAuditLogForModel = async () => {\n const auditContext = getAuditContext();\n if (!auditContext) return;\n\n const { id: entityId } = instance as unknown as { id: string; };\n await this.manualSendAuditLog(\n {\n entityId,\n auditContext,\n entityType: modelName,\n rows: [...getChangedFieldsRows(instance, options), ...getChangedCustomFieldsRows(instance)],\n },\n this.logError,\n );\n };\n /*\n By default sequelize doesn't keep the previous values when using a transaction\n So we want to be able to call this function after the transaction is committed\n to be able to get the previous values\n */\n if (this.waitForTransactionCommit && options.transaction) {\n options.transaction.afterCommit(sendAuditLogForModel);\n return;\n }\n await sendAuditLogForModel();\n });\n });\n }\n\n public async sendAuditLogContext(context: AuditLogContextData): Promise<void> {\n try {\n await this.rabbit.sendToQueue(AUDIT_LOG_CONTEXT_QUEUE, context);\n } catch (err) {\n this.logger.error('Failed to send audit log context', err);\n }\n }\n\n private logError: AuditLogRowSendingErrorHandler = (error: unknown, payload: AuditLogPayload) => {\n const { entityType, entityId } = payload;\n this.logger.error('Failed to send audit log rows', { entityType, entityId, error });\n };\n\n private logAndThrowError: AuditLogRowSendingErrorHandler = (error: unknown, payload: AuditLogPayload): never => {\n this.logError(error, payload);\n throw error;\n };\n}\n\nexport default AuditLogger;\n","import Network from '@autofleet/network';\nimport logger from './logger';\nimport type { FindOptions, Order, Includeable } from 'sequelize';\nimport type { AuditLogRow } from './types';\n\nconst auditMs = new Network({ serviceName: 'AUDIT_MS', timeout: 60_000, logger });\n\nconst getByEntityId = async (entityId: string): Promise<AuditLogRow[]> => {\n const { data } = await auditMs.get(`api/v1/audit-logs/${entityId}`);\n return data;\n};\n\nexport interface QueryRequestBody {\n query?: FindOptions['where'];\n order?: Order;\n attributes?: FindOptions['attributes'];\n page?: number;\n perPage?: number;\n include?: Includeable | Includeable[];\n}\n\ninterface QueryResponse {\n count: number;\n rows: AuditLogRow[];\n}\n\nconst query = async (queryRequestBody: QueryRequestBody, getAllPages = false): Promise<QueryResponse> => {\n if (getAllPages) {\n const allRows = await auditMs.getAllPagesFromQueryEndpoint<AuditLogRow>('api/v1/audit-logs/query', queryRequestBody);\n return { count: allRows.length, rows: allRows };\n }\n const { data } = await auditMs.post('api/v1/audit-logs/query', queryRequestBody);\n return data;\n};\n\nconst queryByEntityId = async (entityId: string, queryRequestBody: QueryRequestBody, getAllPages = false): Promise<QueryResponse> => {\n const query = { ...queryRequestBody, query: { ...queryRequestBody?.query, entityId } };\n if (getAllPages) {\n const allRows = await auditMs.getAllPagesFromQueryEndpoint<AuditLogRow>('api/v1/audit-logs/query', query);\n return { count: allRows.length, rows: allRows };\n }\n const { data } = await auditMs.post('api/v1/audit-logs/query', query);\n return data;\n};\n\nexport { getByEntityId, query, queryByEntityId };\n","import type { Writable } from 'node:stream';\nimport type { IncomingHttpHeaders } from 'node:http';\nimport { getCurrentPayload, getUser } from '@autofleet/zehut';\nimport logger from './logger';\nimport AuditLogger, { isEntityIdRequired } from './audit-logger';\nimport { AUDIT_LOG_CONTEXT_KEY, AUTOMATION_ID_HEADER } from './const';\nimport { ActionOrigin, type AuditLogContextData, type AuditLoggerOptions } from './types';\n\nlet auditLogger: AuditLogger | undefined;\n\nexport const innerEnableAuditing = ({ registerHooks = true, ...options }: AuditLoggerOptions): false | AuditLogger => {\n if (process.env.DISABLE_AUDIT_LOGS === 'true') {\n return false;\n }\n auditLogger = new AuditLogger(options);\n if (registerHooks) {\n auditLogger.registerHooks();\n }\n return auditLogger;\n};\n\ninterface GetAuditLogContextParams {\n action: string;\n endpoint: string;\n entityType: string;\n method: string;\n automationId?: string;\n userId?: string;\n}\nconst getAuditLogContextAndSetToContext = ({\n action, endpoint, entityType, method, automationId, userId,\n}: GetAuditLogContextParams): AuditLogContextData => {\n const performedBy = userId || (automationId ?? null);\n const actionOrigin = (userId && ActionOrigin.USER) || (automationId && ActionOrigin.AUTOMATION) || null;\n\n const auditLogContext: AuditLogContextData = {\n entityType,\n action,\n endpoint,\n method,\n performedBy,\n actionOrigin,\n };\n const currentTrace = getCurrentPayload();\n currentTrace.nonHeaderContext.set(AUDIT_LOG_CONTEXT_KEY, auditLogContext);\n\n return auditLogContext;\n};\n\ninterface PartialRequest {\n url: string;\n method: string;\n headers: IncomingHttpHeaders;\n}\n\nexport const innerSetAuditContext = async (req: PartialRequest, res: Writable, entityType: string, action: string): Promise<void> => {\n try {\n if (process.env.DISABLE_AUDIT_LOGS === 'true' || !auditLogger) {\n return;\n }\n const auditLogContext = getAuditLogContextAndSetToContext({\n action,\n endpoint: req.url,\n entityType,\n method: req.method,\n userId: getUser()?.id,\n automationId: req.headers[AUTOMATION_ID_HEADER] as string | undefined,\n });\n\n if (!isEntityIdRequired(action)) { // if it's a bulk action, we don't want to wait for the response to add the entity id\n await auditLogger.sendAuditLogContext(auditLogContext);\n return;\n }\n\n const sendAuditLogContextEvent = async () => {\n res.off('finish', sendAuditLogContextEvent);\n res.off('close', sendAuditLogContextEvent);\n res.off('error', sendAuditLogContextEvent);\n await auditLogger?.sendAuditLogContext(auditLogContext);\n };\n res.once('finish', sendAuditLogContextEvent);\n res.once('close', sendAuditLogContextEvent);\n res.once('error', sendAuditLogContextEvent);\n } catch (err) {\n logger.error('couldn\\'t set audit context', err);\n }\n};\n\nexport const setRabbitAuditContext = (entityType: string, action: string) => async (endpoint: string, { userId, automationId }: { userId: string; automationId: string; }): Promise<void> => {\n if (process.env.DISABLE_AUDIT_LOGS === 'true' || !auditLogger) {\n return;\n }\n const auditLogContext = getAuditLogContextAndSetToContext({\n action,\n endpoint,\n entityType,\n method: 'rabbit',\n userId,\n automationId,\n });\n\n await auditLogger.sendAuditLogContext(auditLogContext);\n};\n\nexport const setBullMqAuditContext = async (entityType: string, action: string, workerName: string, jobId: string): Promise<void> => {\n if (process.env.DISABLE_AUDIT_LOGS === 'true' || !auditLogger) {\n return;\n }\n const auditLogContext = getAuditLogContextAndSetToContext({\n action,\n endpoint: workerName,\n entityType,\n method: 'bull',\n automationId: jobId,\n });\n\n await auditLogger.sendAuditLogContext(auditLogContext);\n};\n"],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- import{t as e}from"./chunk-BO4cyBXU.js";import{isDeepStrictEqual as t}from"node:util";import{getCurrentPayload as n,getUser as r,outbreak as i}from"@autofleet/zehut";import a from"@autofleet/logger";import o from"@autofleet/network";let s=function(e){return e.CREATE=`create`,e.BULK_CREATE=`bulk-create`,e.BULK_EDIT=`bulk-edit`,e.DELETE=`delete`,e.UPDATE=`update`,e.CANCEL=`cancel`,e.FAIL=`fail`,e.UNASSIGN=`unassign`,e.BULK_ASSIGN=`bulk-assign`,e.REASSIGN=`reassign`,e.DISPATCH=`dispatch`,e.BULK_DISPATCH=`bulk-dispatch`,e.BULK_UPSERT=`bulk-upsert`,e.UPSERT=`upsert`,e.JOIN=`join`,e.MOVE=`move`,e.REOPTIMIZATION=`reoptimization`,e}({}),c=function(e){return e.RIDE=`Ride`,e.VEHICLE=`Vehicle`,e.DRIVER=`Driver`,e.PRICE_CALCULATION=`PriceCalculation`,e.ORDERING_FLEET_SPEC=`OrderingConfig`,e.ORDERING_VEHICLE=`OrderingVehicle`,e}({}),l=function(e){return e.USER=`user`,e.AUTOMATION=`automation`,e}({});const u=`audit-log-context`,d=`audit-log-rows`,f=`auditLogContext`,p=`x-af-automation-id`,m=a();m.addContextMiddleware(()=>({traceId:i.getCurrentContextTraceId()}));var h=m;const g=`customFields`,_=()=>n()?.nonHeaderContext?.get(f),v={[s.BULK_CREATE]:s.BULK_CREATE,[s.BULK_EDIT]:s.BULK_EDIT,[s.BULK_ASSIGN]:s.BULK_ASSIGN,[s.BULK_DISPATCH]:s.BULK_DISPATCH,[s.BULK_UPSERT]:s.BULK_UPSERT},y=e=>[null,void 0].includes(e)?!0:Array.isArray(e)?e.length===0:typeof e==`object`&&!(e instanceof Date)?Object.keys(e).length===0:!1,b=e=>!Object.hasOwn(v,e),x=(e,t)=>e.filter(e=>!y(t.get(e))||!y(t.previous(e))),S=(e,t)=>x((t.returning?t.fields:e.changed()||[]).filter(e=>e!==g),e).map(t=>({property:t,previousValue:e.previous(t),newValue:e.get(t)})),C=e=>{let n=e.changed();if(!(n&&n.includes(g)))return[];let r=e.get(g),i=e.previous(g);return Object.keys(r).filter(e=>{let n=r?.[e],a=i?.[e];return(!y(n)||!y(a))&&!t(n,a)}).map(e=>({property:`${g}.${e}`,previousValue:i?.[e],newValue:r?.[e]}))};var w=class{rabbit;sequelize;logger;excludeModels;waitForTransactionCommit;constructor(e){this.rabbit=e.rabbit,this.sequelize=e.sequelize,this.logger=e.logger||h,this.excludeModels=e.excludeModels??[],this.waitForTransactionCommit=e.waitForTransactionCommit??!1}async manualSendAuditLog({auditContext:e,...t},n=this.logAndThrowError){if(e??=_(),!e)return`NO_AUDIT_CONTEXT`;try{return e?.entityType?.toLowerCase()===t.entityType?.toLowerCase()&&b(e.action)&&(e.entityId=t.entityId),await this.rabbit.sendToQueue(d,t),`SUCCESS`}catch(e){return n(e,t),`FAILURE`}}registerHooks(){Object.entries(this.sequelize.models).forEach(([e,t])=>{this.excludeModels.includes(e)||t.addHook(`afterSave`,async(t,n)=>{let r=async()=>{let r=_();if(!r)return;let{id:i}=t;await this.manualSendAuditLog({entityId:i,auditContext:r,entityType:e,rows:[...S(t,n),...C(t)]},this.logError)};if(this.waitForTransactionCommit&&n.transaction){n.transaction.afterCommit(r);return}await r()})})}async sendAuditLogContext(e){try{await this.rabbit.sendToQueue(u,e)}catch(e){this.logger.error(`Failed to send audit log context`,e)}}logError=(e,t)=>{let{entityType:n,entityId:r}=t;this.logger.error(`Failed to send audit log rows`,{entityType:n,entityId:r,error:e})};logAndThrowError=(e,t)=>{throw this.logError(e,t),e}},T=e({getByEntityId:()=>D,query:()=>O,queryByEntityId:()=>k});const E=new o({serviceName:`AUDIT_MS`,timeout:6e4,logger:h}),D=async e=>{let{data:t}=await E.get(`api/v1/audit-logs/${e}`);return t},O=async(e,t=!1)=>{if(t){let t=await E.getAllPagesFromQueryEndpoint(`api/v1/audit-logs/query`,e);return{count:t.length,rows:t}}let{data:n}=await E.post(`api/v1/audit-logs/query`,e);return n},k=async(e,t,n=!1)=>{let r={...t,query:{...t?.query,entityId:e}};if(n){let e=await E.getAllPagesFromQueryEndpoint(`api/v1/audit-logs/query`,r);return{count:e.length,rows:e}}let{data:i}=await E.post(`api/v1/audit-logs/query`,r);return i};let A;const j=({registerHooks:e=!0,...t})=>process.env.DISABLE_AUDIT_LOGS===`true`?!1:(A=new w(t),e&&A.registerHooks(),A),M=({action:e,endpoint:t,entityType:r,method:i,automationId:a,userId:o})=>{let s={entityType:r,action:e,endpoint:t,method:i,performedBy:o||(a??null),actionOrigin:o&&l.USER||a&&l.AUTOMATION||null};return n().nonHeaderContext.set(f,s),s},N=async(e,t,n,i)=>{try{if(process.env.DISABLE_AUDIT_LOGS===`true`||!A)return;let a=M({action:i,endpoint:e.url,entityType:n,method:e.method,userId:r()?.id,automationId:e.headers[p]});if(!b(i)){await A.sendAuditLogContext(a);return}let o=async()=>{t.off(`finish`,o),t.off(`close`,o),t.off(`error`,o),await A?.sendAuditLogContext(a)};t.once(`finish`,o),t.once(`close`,o),t.once(`error`,o)}catch(e){h.error(`couldn't set audit context`,e)}},P=(e,t)=>async(n,{userId:r,automationId:i})=>{if(process.env.DISABLE_AUDIT_LOGS===`true`||!A)return;let a=M({action:t,endpoint:n,entityType:e,method:`rabbit`,userId:r,automationId:i});await A.sendAuditLogContext(a)},F=async(e,t,n,r)=>{if(process.env.DISABLE_AUDIT_LOGS===`true`||!A)return;let i=M({action:t,endpoint:n,entityType:e,method:`bull`,automationId:r});await A.sendAuditLogContext(i)};export{T as a,f as c,p as d,s as f,P as i,u as l,c as m,N as n,k as o,l as p,F as r,w as s,j as t,d as u};
2
- //# sourceMappingURL=common-BGDD7JIS.js.map