Enhance Workflow API Call Handling and Authorization Logic
- Introduced a new proxy endpoint for API calls during workflow execution to handle CORS issues and streamline API interactions. - Updated the authorization logic to support Basic Auth with both token and username/password options, improving flexibility in API authentication. - Enhanced the API request building process to accommodate new node data structures, including dynamic handling of headers, parameters, and body content. - Improved error handling and response management in the workflow execution process, ensuring better feedback and control over API call outcomes. - Refactored the workflow page to utilize the new API call structure, enhancing overall workflow execution reliability and user experience.
This commit is contained in:
parent
dfea8e7f47
commit
bae98c2b17
@ -655,11 +655,18 @@ function buildApiRequest(nodeData, variables) {
|
||||
// 4. Authorization
|
||||
if (nodeData.authorization && nodeData.authorization.type && nodeData.authorization.type !== 'none') {
|
||||
const auth = nodeData.authorization;
|
||||
|
||||
if (auth.type === 'bearer' && auth.token) {
|
||||
headers['Authorization'] = `Bearer ${substituteVariables(auth.token, variables)}`;
|
||||
} else if (auth.type === 'basic' && auth.username && auth.password) {
|
||||
const token = btoa(`${substituteVariables(auth.username, variables)}:${substituteVariables(auth.password, variables)}`);
|
||||
headers['Authorization'] = `Basic ${token}`;
|
||||
} else if (auth.type === 'basic') {
|
||||
if (auth.token) {
|
||||
// Basic Auth with token (JWT or other token)
|
||||
headers['Authorization'] = `Basic ${substituteVariables(auth.token, variables)}`;
|
||||
} else if (auth.username && auth.password) {
|
||||
// Basic Auth with username/password
|
||||
const token = btoa(`${substituteVariables(auth.username, variables)}:${substituteVariables(auth.password, variables)}`);
|
||||
headers['Authorization'] = `Basic ${token}`;
|
||||
}
|
||||
} else if (auth.type === 'apiKey' && auth.key && auth.value) {
|
||||
if (auth.in === 'header') {
|
||||
headers[substituteVariables(auth.key, variables)] = substituteVariables(auth.value, variables);
|
||||
@ -712,26 +719,45 @@ const executeCurrentStep = async () => {
|
||||
if (currentNode.value?.type === 'api') {
|
||||
console.log(`[Workflow] Executing API node: ${currentNode.value.data?.label || currentNode.value.label}`);
|
||||
const nodeData = currentNode.value.data || {};
|
||||
// Use new structure if present
|
||||
if (nodeData.body || nodeData.headers || nodeData.params || nodeData.authorization) {
|
||||
const { url, headers, body } = buildApiRequest(nodeData, processVariables.value);
|
||||
const apiMethod = nodeData.apiMethod || 'GET';
|
||||
|
||||
// Use new structure if present (check for any new structure properties)
|
||||
if (nodeData.body !== undefined || nodeData.headers !== undefined || nodeData.params !== undefined || nodeData.authorization !== undefined) {
|
||||
const outputVariable = nodeData.outputVariable || 'apiResponse';
|
||||
const errorVariable = nodeData.errorVariable || 'apiError';
|
||||
const continueOnError = nodeData.continueOnError || false;
|
||||
|
||||
try {
|
||||
const response = await $fetch(url, {
|
||||
method: apiMethod,
|
||||
headers,
|
||||
body: ['GET', 'HEAD'].includes(apiMethod) ? undefined : body,
|
||||
// Use proxy endpoint to avoid CORS issues
|
||||
const response = await $fetch('/api/process/workflow-api-call', {
|
||||
method: 'POST',
|
||||
body: {
|
||||
nodeData,
|
||||
processVariables: processVariables.value
|
||||
}
|
||||
});
|
||||
processVariables.value[outputVariable] = response;
|
||||
processVariables.value[errorVariable] = null;
|
||||
console.log('[Workflow] API call success. Output variable set:', outputVariable, response);
|
||||
if (canAutoProgress(currentNode.value)) {
|
||||
moveToNextStep();
|
||||
|
||||
if (response.success) {
|
||||
processVariables.value[outputVariable] = response.data;
|
||||
processVariables.value[errorVariable] = null;
|
||||
console.log('[Workflow] API call success. Output variable set:', outputVariable, response.data);
|
||||
if (canAutoProgress(currentNode.value)) {
|
||||
moveToNextStep();
|
||||
} else {
|
||||
console.log('[Workflow] API completed, multiple paths available - waiting for user choice');
|
||||
}
|
||||
} else {
|
||||
console.log('[Workflow] API completed, multiple paths available - waiting for user choice');
|
||||
processVariables.value[errorVariable] = response.error;
|
||||
console.error('[Workflow] API call failed:', response.error);
|
||||
if (continueOnError) {
|
||||
if (canAutoProgress(currentNode.value)) {
|
||||
moveToNextStep();
|
||||
} else {
|
||||
console.log('[Workflow] API failed but continuing, multiple paths available - waiting for user choice');
|
||||
}
|
||||
} else {
|
||||
error.value = 'API call failed: ' + (response.error.message || response.error);
|
||||
notifyParentOfError(error.value);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
processVariables.value[errorVariable] = err;
|
||||
@ -749,6 +775,7 @@ const executeCurrentStep = async () => {
|
||||
}
|
||||
} else {
|
||||
// Fallback: old structure
|
||||
|
||||
const {
|
||||
apiUrl,
|
||||
apiMethod = 'GET',
|
||||
|
@ -1,73 +1,82 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"definitions": {
|
||||
"user": {
|
||||
"caseInstance": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"userID": {
|
||||
"caseID": {
|
||||
"type": "integer"
|
||||
},
|
||||
"userSecretKey": {
|
||||
"caseUUID": {
|
||||
"type": "string"
|
||||
},
|
||||
"caseName": {
|
||||
"type": "string"
|
||||
},
|
||||
"caseStatus": {
|
||||
"type": "string",
|
||||
"default": "active"
|
||||
},
|
||||
"caseVariables": {
|
||||
"type": [
|
||||
"number",
|
||||
"string",
|
||||
"boolean",
|
||||
"object",
|
||||
"array",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"userUsername": {
|
||||
"caseSettings": {
|
||||
"type": [
|
||||
"number",
|
||||
"string",
|
||||
"boolean",
|
||||
"object",
|
||||
"array",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"userPassword": {
|
||||
"caseDefinition": {
|
||||
"type": [
|
||||
"number",
|
||||
"string",
|
||||
"boolean",
|
||||
"object",
|
||||
"array",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"userFullName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
"caseCreatedDate": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"userEmail": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"userPhone": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"userStatus": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"userCreatedDate": {
|
||||
"caseModifiedDate": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"format": "date-time"
|
||||
},
|
||||
"userModifiedDate": {
|
||||
"caseCompletedDate": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"format": "date-time"
|
||||
},
|
||||
"caseInstance": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/caseInstance"
|
||||
}
|
||||
"startedBy": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/user"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"process": {
|
||||
"$ref": "#/definitions/process"
|
||||
},
|
||||
"caseTimeline": {
|
||||
"type": "array",
|
||||
@ -75,123 +84,45 @@
|
||||
"$ref": "#/definitions/caseTimeline"
|
||||
}
|
||||
},
|
||||
"forms": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/form"
|
||||
}
|
||||
},
|
||||
"formHistoryEntries": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/formHistory"
|
||||
}
|
||||
},
|
||||
"processes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/process"
|
||||
}
|
||||
},
|
||||
"processHistoryEntries": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/processHistory"
|
||||
}
|
||||
},
|
||||
"task": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/task"
|
||||
}
|
||||
},
|
||||
"userrole": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/userrole"
|
||||
}
|
||||
},
|
||||
"startedCases": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/caseInstance"
|
||||
}
|
||||
},
|
||||
"assignedTasks": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/task"
|
||||
}
|
||||
},
|
||||
"caseTimelineEntries": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/caseTimeline"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"role": {
|
||||
"caseTimeline": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"roleID": {
|
||||
"timelineID": {
|
||||
"type": "integer"
|
||||
},
|
||||
"roleName": {
|
||||
"timelineType": {
|
||||
"type": "string"
|
||||
},
|
||||
"timelineDescription": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"roleDescription": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"roleStatus": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"roleCreatedDate": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"format": "date-time"
|
||||
},
|
||||
"roleModifiedDate": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"format": "date-time"
|
||||
},
|
||||
"userrole": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/userrole"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"userrole": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"userRoleID": {
|
||||
"type": "integer"
|
||||
},
|
||||
"userRoleCreatedDate": {
|
||||
"timelineDate": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"role": {
|
||||
"$ref": "#/definitions/role"
|
||||
"caseInstance": {
|
||||
"$ref": "#/definitions/caseInstance"
|
||||
},
|
||||
"user": {
|
||||
"$ref": "#/definitions/user"
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/user"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -277,13 +208,13 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"history": {
|
||||
"formHistory": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/formHistory"
|
||||
}
|
||||
},
|
||||
"tasks": {
|
||||
"task": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/task"
|
||||
@ -366,7 +297,7 @@
|
||||
"form": {
|
||||
"$ref": "#/definitions/form"
|
||||
},
|
||||
"savedByUser": {
|
||||
"user": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/user"
|
||||
@ -507,17 +438,11 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"history": {
|
||||
"processHistory": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/processHistory"
|
||||
}
|
||||
},
|
||||
"cases": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/caseInstance"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -625,7 +550,7 @@
|
||||
"process": {
|
||||
"$ref": "#/definitions/process"
|
||||
},
|
||||
"savedByUser": {
|
||||
"user": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/user"
|
||||
@ -637,93 +562,48 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"caseInstance": {
|
||||
"role": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"caseID": {
|
||||
"roleID": {
|
||||
"type": "integer"
|
||||
},
|
||||
"caseUUID": {
|
||||
"type": "string"
|
||||
},
|
||||
"caseName": {
|
||||
"type": "string"
|
||||
},
|
||||
"caseStatus": {
|
||||
"type": "string",
|
||||
"default": "active"
|
||||
},
|
||||
"caseVariables": {
|
||||
"roleName": {
|
||||
"type": [
|
||||
"number",
|
||||
"string",
|
||||
"boolean",
|
||||
"object",
|
||||
"array",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"caseSettings": {
|
||||
"roleDescription": {
|
||||
"type": [
|
||||
"number",
|
||||
"string",
|
||||
"boolean",
|
||||
"object",
|
||||
"array",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"caseDefinition": {
|
||||
"roleStatus": {
|
||||
"type": [
|
||||
"number",
|
||||
"string",
|
||||
"boolean",
|
||||
"object",
|
||||
"array",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"caseCreatedDate": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"caseModifiedDate": {
|
||||
"roleCreatedDate": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"format": "date-time"
|
||||
},
|
||||
"caseCompletedDate": {
|
||||
"roleModifiedDate": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"format": "date-time"
|
||||
},
|
||||
"process": {
|
||||
"$ref": "#/definitions/process"
|
||||
},
|
||||
"startedBy": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/user"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"tasks": {
|
||||
"userrole": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/task"
|
||||
}
|
||||
},
|
||||
"timeline": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/caseTimeline"
|
||||
"$ref": "#/definitions/userrole"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -775,10 +655,10 @@
|
||||
],
|
||||
"format": "date-time"
|
||||
},
|
||||
"case": {
|
||||
"caseInstance": {
|
||||
"$ref": "#/definitions/caseInstance"
|
||||
},
|
||||
"assignedTo": {
|
||||
"user": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/user"
|
||||
@ -800,51 +680,144 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"caseTimeline": {
|
||||
"user": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"timelineID": {
|
||||
"userID": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelineType": {
|
||||
"type": "string"
|
||||
},
|
||||
"timelineDescription": {
|
||||
"userSecretKey": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"timelineDate": {
|
||||
"userUsername": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"userPassword": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"userFullName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"userEmail": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"userPhone": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"userStatus": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"userCreatedDate": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"format": "date-time"
|
||||
},
|
||||
"userModifiedDate": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
],
|
||||
"format": "date-time"
|
||||
},
|
||||
"caseInstance": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/caseInstance"
|
||||
}
|
||||
},
|
||||
"caseTimeline": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/caseTimeline"
|
||||
}
|
||||
},
|
||||
"form": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/form"
|
||||
}
|
||||
},
|
||||
"formHistory": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/formHistory"
|
||||
}
|
||||
},
|
||||
"process": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/process"
|
||||
}
|
||||
},
|
||||
"processHistory": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/processHistory"
|
||||
}
|
||||
},
|
||||
"task": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/task"
|
||||
}
|
||||
},
|
||||
"userrole": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/userrole"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"userrole": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"userRoleID": {
|
||||
"type": "integer"
|
||||
},
|
||||
"userRoleCreatedDate": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"case": {
|
||||
"$ref": "#/definitions/caseInstance"
|
||||
"role": {
|
||||
"$ref": "#/definitions/role"
|
||||
},
|
||||
"createdBy": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/user"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
"user": {
|
||||
"$ref": "#/definitions/user"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"user": {
|
||||
"$ref": "#/definitions/user"
|
||||
"caseInstance": {
|
||||
"$ref": "#/definitions/caseInstance"
|
||||
},
|
||||
"role": {
|
||||
"$ref": "#/definitions/role"
|
||||
},
|
||||
"userrole": {
|
||||
"$ref": "#/definitions/userrole"
|
||||
"caseTimeline": {
|
||||
"$ref": "#/definitions/caseTimeline"
|
||||
},
|
||||
"form": {
|
||||
"$ref": "#/definitions/form"
|
||||
@ -858,14 +831,17 @@
|
||||
"processHistory": {
|
||||
"$ref": "#/definitions/processHistory"
|
||||
},
|
||||
"caseInstance": {
|
||||
"$ref": "#/definitions/caseInstance"
|
||||
"role": {
|
||||
"$ref": "#/definitions/role"
|
||||
},
|
||||
"task": {
|
||||
"$ref": "#/definitions/task"
|
||||
},
|
||||
"caseTimeline": {
|
||||
"$ref": "#/definitions/caseTimeline"
|
||||
"user": {
|
||||
"$ref": "#/definitions/user"
|
||||
},
|
||||
"userrole": {
|
||||
"$ref": "#/definitions/userrole"
|
||||
}
|
||||
}
|
||||
}
|
@ -12,50 +12,42 @@ datasource db {
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
model user {
|
||||
userID Int @id @default(autoincrement())
|
||||
userSecretKey String? @db.VarChar(255)
|
||||
userUsername String? @db.VarChar(255)
|
||||
userPassword String? @db.VarChar(255)
|
||||
userFullName String? @db.VarChar(255)
|
||||
userEmail String? @db.VarChar(255)
|
||||
userPhone String? @db.VarChar(255)
|
||||
userStatus String? @db.VarChar(255)
|
||||
userCreatedDate DateTime? @db.DateTime(0)
|
||||
userModifiedDate DateTime? @db.DateTime(0)
|
||||
caseInstance caseInstance[]
|
||||
caseTimeline caseTimeline[]
|
||||
forms form[] @relation("FormCreator")
|
||||
formHistoryEntries formHistory[]
|
||||
processes process[] @relation("ProcessCreator")
|
||||
processHistoryEntries processHistory[]
|
||||
task task[]
|
||||
userrole userrole[]
|
||||
startedCases caseInstance[] @relation("CaseStartedBy")
|
||||
assignedTasks task[] @relation("TaskAssignedTo")
|
||||
caseTimelineEntries caseTimeline[]
|
||||
model caseInstance {
|
||||
caseID Int @id @default(autoincrement())
|
||||
caseUUID String @unique @db.VarChar(36)
|
||||
processID Int
|
||||
caseName String @db.VarChar(255)
|
||||
caseStatus String @default("active") @db.VarChar(50)
|
||||
caseStartedBy Int?
|
||||
caseVariables Json?
|
||||
caseSettings Json?
|
||||
caseDefinition Json?
|
||||
caseCreatedDate DateTime @default(now()) @db.DateTime(0)
|
||||
caseModifiedDate DateTime? @db.DateTime(0)
|
||||
caseCompletedDate DateTime? @db.DateTime(0)
|
||||
startedBy user? @relation(fields: [caseStartedBy], references: [userID])
|
||||
process process @relation(fields: [processID], references: [processID])
|
||||
caseTimeline caseTimeline[]
|
||||
task task[]
|
||||
|
||||
@@index([processID], map: "FK_case_process")
|
||||
@@index([caseStartedBy], map: "FK_case_startedBy")
|
||||
@@index([caseStatus], map: "IDX_case_status")
|
||||
}
|
||||
|
||||
model role {
|
||||
roleID Int @id @default(autoincrement())
|
||||
roleName String? @db.VarChar(255)
|
||||
roleDescription String? @db.VarChar(255)
|
||||
roleStatus String? @db.VarChar(255)
|
||||
roleCreatedDate DateTime? @db.DateTime(0)
|
||||
roleModifiedDate DateTime? @db.DateTime(0)
|
||||
userrole userrole[]
|
||||
}
|
||||
model caseTimeline {
|
||||
timelineID Int @id @default(autoincrement())
|
||||
caseID Int
|
||||
timelineType String @db.VarChar(50)
|
||||
timelineDescription String? @db.Text
|
||||
timelineDate DateTime @default(now()) @db.DateTime(0)
|
||||
timelineCreatedBy Int?
|
||||
caseInstance caseInstance @relation(fields: [caseID], references: [caseID])
|
||||
user user? @relation(fields: [timelineCreatedBy], references: [userID])
|
||||
|
||||
model userrole {
|
||||
userRoleID Int @id @default(autoincrement())
|
||||
userRoleUserID Int @default(0)
|
||||
userRoleRoleID Int @default(0)
|
||||
userRoleCreatedDate DateTime @db.DateTime(0)
|
||||
role role @relation(fields: [userRoleRoleID], references: [roleID], onDelete: NoAction, onUpdate: NoAction, map: "FK_userrole_role")
|
||||
user user @relation(fields: [userRoleUserID], references: [userID], onDelete: NoAction, onUpdate: NoAction, map: "FK_userrole_user")
|
||||
|
||||
@@index([userRoleRoleID], map: "FK_userrole_role")
|
||||
@@index([userRoleUserID], map: "FK_userrole_user")
|
||||
@@index([caseID], map: "FK_caseTimeline_case")
|
||||
@@index([timelineCreatedBy], map: "FK_caseTimeline_createdBy")
|
||||
@@index([timelineDate], map: "IDX_caseTimeline_date")
|
||||
}
|
||||
|
||||
model form {
|
||||
@ -67,14 +59,14 @@ model form {
|
||||
formStatus String @default("active") @db.VarChar(50)
|
||||
formCreatedBy Int?
|
||||
formCreatedDate DateTime @default(now()) @db.DateTime(0)
|
||||
formModifiedDate DateTime? @updatedAt @db.DateTime(0)
|
||||
formModifiedDate DateTime? @db.DateTime(0)
|
||||
customCSS String? @db.Text
|
||||
customScript String? @db.LongText
|
||||
formEvents Json?
|
||||
scriptMode String? @default("safe") @db.VarChar(20)
|
||||
creator user? @relation("FormCreator", fields: [formCreatedBy], references: [userID])
|
||||
history formHistory[] @relation("FormHistoryEntries")
|
||||
tasks task[]
|
||||
creator user? @relation(fields: [formCreatedBy], references: [userID])
|
||||
formHistory formHistory[]
|
||||
task task[]
|
||||
|
||||
@@index([formCreatedBy], map: "FK_form_creator")
|
||||
}
|
||||
@ -95,13 +87,13 @@ model formHistory {
|
||||
changeDescription String? @db.Text
|
||||
savedBy Int?
|
||||
savedDate DateTime @default(now()) @db.DateTime(0)
|
||||
form form @relation("FormHistoryEntries", fields: [formID], references: [formID], onDelete: Cascade)
|
||||
savedByUser user? @relation(fields: [savedBy], references: [userID])
|
||||
form form @relation(fields: [formID], references: [formID], onDelete: Cascade)
|
||||
user user? @relation(fields: [savedBy], references: [userID])
|
||||
|
||||
@@index([formID], map: "FK_formHistory_form")
|
||||
@@index([savedBy], map: "FK_formHistory_savedBy")
|
||||
@@index([formUUID], map: "IDX_formHistory_uuid")
|
||||
@@index([savedDate], map: "IDX_formHistory_date")
|
||||
@@index([formUUID], map: "IDX_formHistory_uuid")
|
||||
}
|
||||
|
||||
model process {
|
||||
@ -114,7 +106,7 @@ model process {
|
||||
processStatus String @default("draft") @db.VarChar(50)
|
||||
processCreatedBy Int?
|
||||
processCreatedDate DateTime @default(now()) @db.DateTime(0)
|
||||
processModifiedDate DateTime? @updatedAt @db.DateTime(0)
|
||||
processModifiedDate DateTime? @db.DateTime(0)
|
||||
isTemplate Boolean @default(false)
|
||||
processCategory String? @db.VarChar(100)
|
||||
processOwner String? @db.VarChar(255)
|
||||
@ -125,13 +117,12 @@ model process {
|
||||
templateCategory String? @db.VarChar(100)
|
||||
processDeletedDate DateTime? @db.DateTime(0)
|
||||
caseInstance caseInstance[]
|
||||
creator user? @relation("ProcessCreator", fields: [processCreatedBy], references: [userID])
|
||||
history processHistory[] @relation("ProcessHistoryEntries")
|
||||
cases caseInstance[]
|
||||
creator user? @relation(fields: [processCreatedBy], references: [userID])
|
||||
processHistory processHistory[]
|
||||
|
||||
@@index([processCreatedBy], map: "FK_process_creator")
|
||||
@@index([processStatus], map: "IDX_process_status")
|
||||
@@index([processCategory], map: "IDX_process_category")
|
||||
@@index([processStatus], map: "IDX_process_status")
|
||||
@@index([isTemplate], map: "IDX_process_template")
|
||||
}
|
||||
|
||||
@ -155,72 +146,77 @@ model processHistory {
|
||||
changeDescription String? @db.Text
|
||||
savedBy Int?
|
||||
savedDate DateTime @default(now()) @db.DateTime(0)
|
||||
process process @relation("ProcessHistoryEntries", fields: [processID], references: [processID], onDelete: Cascade)
|
||||
savedByUser user? @relation(fields: [savedBy], references: [userID])
|
||||
process process @relation(fields: [processID], references: [processID], onDelete: Cascade)
|
||||
user user? @relation(fields: [savedBy], references: [userID])
|
||||
|
||||
@@index([processID], map: "FK_processHistory_process")
|
||||
@@index([savedBy], map: "FK_processHistory_savedBy")
|
||||
@@index([processUUID], map: "IDX_processHistory_uuid")
|
||||
@@index([savedDate], map: "IDX_processHistory_date")
|
||||
@@index([processUUID], map: "IDX_processHistory_uuid")
|
||||
}
|
||||
|
||||
model caseInstance {
|
||||
caseID Int @id @default(autoincrement())
|
||||
caseUUID String @unique @db.VarChar(36)
|
||||
processID Int
|
||||
caseName String @db.VarChar(255)
|
||||
caseStatus String @default("active") @db.VarChar(50)
|
||||
caseStartedBy Int?
|
||||
caseVariables Json?
|
||||
caseSettings Json?
|
||||
caseDefinition Json?
|
||||
caseCreatedDate DateTime @default(now()) @db.DateTime(0)
|
||||
caseModifiedDate DateTime? @updatedAt @db.DateTime(0)
|
||||
caseCompletedDate DateTime? @db.DateTime(0)
|
||||
process process @relation(fields: [processID], references: [processID])
|
||||
startedBy user? @relation("CaseStartedBy", fields: [caseStartedBy], references: [userID])
|
||||
tasks task[]
|
||||
timeline caseTimeline[]
|
||||
|
||||
@@index([processID], map: "FK_case_process")
|
||||
@@index([caseStartedBy], map: "FK_case_startedBy")
|
||||
@@index([caseStatus], map: "IDX_case_status")
|
||||
model role {
|
||||
roleID Int @id @default(autoincrement())
|
||||
roleName String? @db.VarChar(255)
|
||||
roleDescription String? @db.VarChar(255)
|
||||
roleStatus String? @db.VarChar(255)
|
||||
roleCreatedDate DateTime? @db.DateTime(0)
|
||||
roleModifiedDate DateTime? @db.DateTime(0)
|
||||
userrole userrole[]
|
||||
}
|
||||
|
||||
model task {
|
||||
taskID Int @id @default(autoincrement())
|
||||
taskUUID String @unique @db.VarChar(36)
|
||||
caseID Int
|
||||
taskName String @db.VarChar(255)
|
||||
taskType String @db.VarChar(50)
|
||||
taskStatus String @default("pending") @db.VarChar(50)
|
||||
taskAssignedTo Int?
|
||||
taskFormID Int?
|
||||
taskData Json?
|
||||
taskCreatedDate DateTime @default(now()) @db.DateTime(0)
|
||||
taskModifiedDate DateTime? @updatedAt @db.DateTime(0)
|
||||
taskID Int @id @default(autoincrement())
|
||||
taskUUID String @unique @db.VarChar(36)
|
||||
caseID Int
|
||||
taskName String @db.VarChar(255)
|
||||
taskType String @db.VarChar(50)
|
||||
taskStatus String @default("pending") @db.VarChar(50)
|
||||
taskAssignedTo Int?
|
||||
taskFormID Int?
|
||||
taskData Json?
|
||||
taskCreatedDate DateTime @default(now()) @db.DateTime(0)
|
||||
taskModifiedDate DateTime? @db.DateTime(0)
|
||||
taskCompletedDate DateTime? @db.DateTime(0)
|
||||
case caseInstance @relation(fields: [caseID], references: [caseID])
|
||||
assignedTo user? @relation("TaskAssignedTo", fields: [taskAssignedTo], references: [userID])
|
||||
form form? @relation(fields: [taskFormID], references: [formID])
|
||||
caseInstance caseInstance @relation(fields: [caseID], references: [caseID])
|
||||
user user? @relation(fields: [taskAssignedTo], references: [userID])
|
||||
form form? @relation(fields: [taskFormID], references: [formID])
|
||||
|
||||
@@index([caseID], map: "FK_task_case")
|
||||
@@index([taskAssignedTo], map: "FK_task_assignedTo")
|
||||
@@index([caseID], map: "FK_task_case")
|
||||
@@index([taskFormID], map: "FK_task_form")
|
||||
@@index([taskStatus], map: "IDX_task_status")
|
||||
}
|
||||
|
||||
model caseTimeline {
|
||||
timelineID Int @id @default(autoincrement())
|
||||
caseID Int
|
||||
timelineType String @db.VarChar(50)
|
||||
timelineDescription String? @db.Text
|
||||
timelineDate DateTime @default(now()) @db.DateTime(0)
|
||||
timelineCreatedBy Int?
|
||||
case caseInstance @relation(fields: [caseID], references: [caseID])
|
||||
createdBy user? @relation(fields: [timelineCreatedBy], references: [userID])
|
||||
|
||||
@@index([caseID], map: "FK_caseTimeline_case")
|
||||
@@index([timelineCreatedBy], map: "FK_caseTimeline_createdBy")
|
||||
@@index([timelineDate], map: "IDX_caseTimeline_date")
|
||||
model user {
|
||||
userID Int @id @default(autoincrement())
|
||||
userSecretKey String? @db.VarChar(255)
|
||||
userUsername String? @db.VarChar(255)
|
||||
userPassword String? @db.VarChar(255)
|
||||
userFullName String? @db.VarChar(255)
|
||||
userEmail String? @db.VarChar(255)
|
||||
userPhone String? @db.VarChar(255)
|
||||
userStatus String? @db.VarChar(255)
|
||||
userCreatedDate DateTime? @db.DateTime(0)
|
||||
userModifiedDate DateTime? @db.DateTime(0)
|
||||
caseInstance caseInstance[]
|
||||
caseTimeline caseTimeline[]
|
||||
form form[]
|
||||
formHistory formHistory[]
|
||||
process process[]
|
||||
processHistory processHistory[]
|
||||
task task[]
|
||||
userrole userrole[]
|
||||
}
|
||||
|
||||
model userrole {
|
||||
userRoleID Int @id @default(autoincrement())
|
||||
userRoleUserID Int @default(0)
|
||||
userRoleRoleID Int @default(0)
|
||||
userRoleCreatedDate DateTime @db.DateTime(0)
|
||||
role role @relation(fields: [userRoleRoleID], references: [roleID], onDelete: NoAction, onUpdate: NoAction, map: "FK_userrole_role")
|
||||
user user @relation(fields: [userRoleUserID], references: [userID], onDelete: NoAction, onUpdate: NoAction, map: "FK_userrole_user")
|
||||
|
||||
@@index([userRoleRoleID], map: "FK_userrole_role")
|
||||
@@index([userRoleUserID], map: "FK_userrole_user")
|
||||
}
|
||||
|
198
server/api/process/workflow-api-call.post.js
Normal file
198
server/api/process/workflow-api-call.post.js
Normal file
@ -0,0 +1,198 @@
|
||||
/**
|
||||
* Workflow API Call Proxy Endpoint
|
||||
*
|
||||
* This endpoint acts as a proxy for API calls made during workflow execution.
|
||||
* It handles the new API node structure with proper authorization and avoids CORS issues.
|
||||
*/
|
||||
|
||||
// Helper function to substitute variables in a string
|
||||
function substituteVariables(str, variables) {
|
||||
if (typeof str !== 'string') return str;
|
||||
|
||||
// Replace {{variable}} first
|
||||
str = str.replace(/\{\{\s*([a-zA-Z0-9_]+)\s*\}\}/g, (match, varName) => {
|
||||
const value = variables[varName];
|
||||
if (value === undefined || value === null) return '';
|
||||
if (typeof value === 'object') {
|
||||
return JSON.stringify(value);
|
||||
}
|
||||
return String(value);
|
||||
});
|
||||
|
||||
// Then replace {variable}
|
||||
str = str.replace(/\{([a-zA-Z0-9_]+)\}/g, (match, varName) => {
|
||||
const value = variables[varName];
|
||||
if (value === undefined || value === null) return '';
|
||||
if (typeof value === 'object') {
|
||||
return JSON.stringify(value);
|
||||
}
|
||||
return String(value);
|
||||
});
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
// Build API request from node data
|
||||
function buildApiRequest(nodeData, variables) {
|
||||
// 1. URL (with param substitution)
|
||||
let url = substituteVariables(nodeData.apiUrl, variables);
|
||||
|
||||
// 2. Params (for GET, DELETE, etc.)
|
||||
let params = Array.isArray(nodeData.params) ? nodeData.params : [];
|
||||
if (params.length) {
|
||||
const query = params
|
||||
.filter(p => p.key)
|
||||
.map(p => `${encodeURIComponent(substituteVariables(p.key, variables))}=${encodeURIComponent(substituteVariables(p.value, variables))}`)
|
||||
.join('&');
|
||||
if (query) {
|
||||
url += (url.includes('?') ? '&' : '?') + query;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Headers
|
||||
let headers = {};
|
||||
if (Array.isArray(nodeData.headers)) {
|
||||
nodeData.headers.forEach(h => {
|
||||
if (h.key) headers[substituteVariables(h.key, variables)] = substituteVariables(h.value, variables);
|
||||
});
|
||||
} else if (typeof nodeData.headers === 'object') {
|
||||
headers = { ...nodeData.headers };
|
||||
}
|
||||
|
||||
// 4. Authorization
|
||||
if (nodeData.authorization && nodeData.authorization.type && nodeData.authorization.type !== 'none') {
|
||||
const auth = nodeData.authorization;
|
||||
if (auth.type === 'bearer' && auth.token) {
|
||||
headers['Authorization'] = `Bearer ${substituteVariables(auth.token, variables)}`;
|
||||
} else if (auth.type === 'basic') {
|
||||
if (auth.token) {
|
||||
// Basic Auth with token (JWT or other token)
|
||||
headers['Authorization'] = `Basic ${substituteVariables(auth.token, variables)}`;
|
||||
} else if (auth.username && auth.password) {
|
||||
// Basic Auth with username/password
|
||||
const token = Buffer.from(`${substituteVariables(auth.username, variables)}:${substituteVariables(auth.password, variables)}`).toString('base64');
|
||||
headers['Authorization'] = `Basic ${token}`;
|
||||
}
|
||||
} else if (auth.type === 'apiKey' && auth.key && auth.value) {
|
||||
if (auth.in === 'header') {
|
||||
headers[substituteVariables(auth.key, variables)] = substituteVariables(auth.value, variables);
|
||||
} else if (auth.in === 'query') {
|
||||
url += (url.includes('?') ? '&' : '?') + `${encodeURIComponent(substituteVariables(auth.key, variables))}=${encodeURIComponent(substituteVariables(auth.value, variables))}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Body
|
||||
let body;
|
||||
if (nodeData.body && nodeData.body.type && nodeData.body.type !== 'none') {
|
||||
if (['form-data', 'x-www-form-urlencoded'].includes(nodeData.body.type)) {
|
||||
const dataArr = Array.isArray(nodeData.body.data) ? nodeData.body.data : [];
|
||||
if (nodeData.body.type === 'form-data') {
|
||||
// For server-side, we'll use URLSearchParams for form-data
|
||||
const formData = new URLSearchParams();
|
||||
dataArr.forEach(item => {
|
||||
if (item.key) formData.append(substituteVariables(item.key, variables), substituteVariables(item.value, variables));
|
||||
});
|
||||
body = formData.toString();
|
||||
headers['Content-Type'] = 'application/x-www-form-urlencoded';
|
||||
} else {
|
||||
// x-www-form-urlencoded
|
||||
body = dataArr
|
||||
.filter(item => item.key)
|
||||
.map(item => `${encodeURIComponent(substituteVariables(item.key, variables))}=${encodeURIComponent(substituteVariables(item.value, variables))}`)
|
||||
.join('&');
|
||||
headers['Content-Type'] = 'application/x-www-form-urlencoded';
|
||||
}
|
||||
} else if (nodeData.body.type === 'raw') {
|
||||
body = substituteVariables(nodeData.body.data, variables);
|
||||
// Try to detect JSON
|
||||
if (body && body.trim().startsWith('{')) {
|
||||
headers['Content-Type'] = 'application/json';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { url, headers, body };
|
||||
}
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
try {
|
||||
// Get request body
|
||||
const body = await readBody(event);
|
||||
|
||||
// Extract node configuration and process variables
|
||||
const { nodeData, processVariables } = body;
|
||||
|
||||
// Validate input
|
||||
if (!nodeData || !nodeData.apiUrl) {
|
||||
return {
|
||||
success: false,
|
||||
error: {
|
||||
message: 'Invalid API node configuration. Missing apiUrl.'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Build the API request
|
||||
const { url, headers, body: requestBody } = buildApiRequest(nodeData, processVariables);
|
||||
const apiMethod = nodeData.apiMethod || 'GET';
|
||||
const outputVariable = nodeData.outputVariable || 'apiResponse';
|
||||
const errorVariable = nodeData.errorVariable || 'apiError';
|
||||
const continueOnError = nodeData.continueOnError || false;
|
||||
|
||||
|
||||
|
||||
// Prepare fetch options
|
||||
const fetchOptions = {
|
||||
method: apiMethod,
|
||||
headers
|
||||
};
|
||||
|
||||
// Add body for non-GET requests
|
||||
if (!['GET', 'HEAD'].includes(apiMethod) && requestBody) {
|
||||
fetchOptions.body = requestBody;
|
||||
}
|
||||
|
||||
// Make the API call
|
||||
const response = await fetch(url, fetchOptions);
|
||||
|
||||
// Get response data
|
||||
let responseData;
|
||||
const contentType = response.headers.get('content-type');
|
||||
|
||||
if (contentType && contentType.includes('application/json')) {
|
||||
responseData = await response.json();
|
||||
} else {
|
||||
responseData = await response.text();
|
||||
}
|
||||
|
||||
// Prepare result
|
||||
const result = {
|
||||
success: response.ok,
|
||||
data: responseData,
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
headers: Object.fromEntries([...response.headers.entries()])
|
||||
};
|
||||
|
||||
if (!response.ok) {
|
||||
result.error = {
|
||||
message: `API call failed with status ${response.status}`,
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
data: responseData
|
||||
};
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
error: {
|
||||
message: error.message || 'An error occurred while making the API call',
|
||||
stack: process.env.NODE_ENV === 'development' ? error.stack : undefined
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user