feat: workflow foundation (#23621)

* feat: plugins

* feat: table definition

* feat: type and migration

* feat: add repositories

* feat: validate manifest with class-validator and load manifest info to database

* feat: workflow/plugin controller/service layer

* feat: implement workflow logic

* feat: make trigger static

* feat: dynamical instantiate plugin instances

* fix: access control and helper script

* feat: it works

* chore: simplify

* refactor: refactor and use queue for workflow execution

* refactor: remove unsused property in plugin-schema

* build wasm in prod

* feat: plugin loader in transaction

* fix: docker build arm64

* generated files

* shell check

* fix tests

* fix: waiting for migration to finish before loading plugin

* remove context reassignment

* feat: use mise to manage extism tools (#23760)

* pr feedback

* refactor: create workflow now including create filters and actions

* feat: workflow medium tests

* fix: broken medium test

* feat: medium tests

* chore: unify workflow job

* sign user id with jwt

* chore: query plugin with filters and action

* chore: read manifest in repository

* chore: load manifest from server configs

* merge main

* feat: endpoint documentation

* pr feedback

* load plugin from absolute path

* refactor:handle trigger

* throw error and return early

* pr feedback

* unify plugin services

* fix: plugins code

* clean up

* remove triggerConfig

* clean up

* displayName and methodName

---------

Co-authored-by: Jason Rasmussen <jason@rasm.me>
Co-authored-by: bo0tzz <git@bo0tzz.me>
This commit is contained in:
Alex
2025-11-14 14:05:05 -06:00
committed by GitHub
parent d784d431d0
commit 4dcc049465
89 changed files with 7264 additions and 14 deletions
+754 -3
View File
@@ -7865,6 +7865,111 @@
"x-immich-state": "Stable"
}
},
"/plugins": {
"get": {
"description": "Retrieve a list of plugins available to the authenticated user.",
"operationId": "getPlugins",
"parameters": [],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"items": {
"$ref": "#/components/schemas/PluginResponseDto"
},
"type": "array"
}
}
},
"description": ""
}
},
"security": [
{
"bearer": []
},
{
"cookie": []
},
{
"api_key": []
}
],
"summary": "List all plugins",
"tags": [
"Plugins"
],
"x-immich-history": [
{
"version": "v2.3.0",
"state": "Added"
},
{
"version": "v2.3.0",
"state": "Alpha"
}
],
"x-immich-permission": "plugin.read",
"x-immich-state": "Alpha"
}
},
"/plugins/{id}": {
"get": {
"description": "Retrieve information about a specific plugin by its ID.",
"operationId": "getPlugin",
"parameters": [
{
"name": "id",
"required": true,
"in": "path",
"schema": {
"format": "uuid",
"type": "string"
}
}
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PluginResponseDto"
}
}
},
"description": ""
}
},
"security": [
{
"bearer": []
},
{
"cookie": []
},
{
"api_key": []
}
],
"summary": "Retrieve a plugin",
"tags": [
"Plugins"
],
"x-immich-history": [
{
"version": "v2.3.0",
"state": "Added"
},
{
"version": "v2.3.0",
"state": "Alpha"
}
],
"x-immich-permission": "plugin.read",
"x-immich-state": "Alpha"
}
},
"/search/cities": {
"get": {
"description": "Retrieve a list of assets with each asset belonging to a different city. This endpoint is used on the places pages to show a single thumbnail for each city the user has assets in.",
@@ -13485,6 +13590,276 @@
],
"x-immich-state": "Stable"
}
},
"/workflows": {
"get": {
"description": "Retrieve a list of workflows available to the authenticated user.",
"operationId": "getWorkflows",
"parameters": [],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"items": {
"$ref": "#/components/schemas/WorkflowResponseDto"
},
"type": "array"
}
}
},
"description": ""
}
},
"security": [
{
"bearer": []
},
{
"cookie": []
},
{
"api_key": []
}
],
"summary": "List all workflows",
"tags": [
"Workflows"
],
"x-immich-history": [
{
"version": "v2.3.0",
"state": "Added"
},
{
"version": "v2.3.0",
"state": "Alpha"
}
],
"x-immich-permission": "workflow.read",
"x-immich-state": "Alpha"
},
"post": {
"description": "Create a new workflow, the workflow can also be created with empty filters and actions.",
"operationId": "createWorkflow",
"parameters": [],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/WorkflowCreateDto"
}
}
},
"required": true
},
"responses": {
"201": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/WorkflowResponseDto"
}
}
},
"description": ""
}
},
"security": [
{
"bearer": []
},
{
"cookie": []
},
{
"api_key": []
}
],
"summary": "Create a workflow",
"tags": [
"Workflows"
],
"x-immich-history": [
{
"version": "v2.3.0",
"state": "Added"
},
{
"version": "v2.3.0",
"state": "Alpha"
}
],
"x-immich-permission": "workflow.create",
"x-immich-state": "Alpha"
}
},
"/workflows/{id}": {
"delete": {
"description": "Delete a workflow by its ID.",
"operationId": "deleteWorkflow",
"parameters": [
{
"name": "id",
"required": true,
"in": "path",
"schema": {
"format": "uuid",
"type": "string"
}
}
],
"responses": {
"204": {
"description": ""
}
},
"security": [
{
"bearer": []
},
{
"cookie": []
},
{
"api_key": []
}
],
"summary": "Delete a workflow",
"tags": [
"Workflows"
],
"x-immich-history": [
{
"version": "v2.3.0",
"state": "Added"
},
{
"version": "v2.3.0",
"state": "Alpha"
}
],
"x-immich-permission": "workflow.delete",
"x-immich-state": "Alpha"
},
"get": {
"description": "Retrieve information about a specific workflow by its ID.",
"operationId": "getWorkflow",
"parameters": [
{
"name": "id",
"required": true,
"in": "path",
"schema": {
"format": "uuid",
"type": "string"
}
}
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/WorkflowResponseDto"
}
}
},
"description": ""
}
},
"security": [
{
"bearer": []
},
{
"cookie": []
},
{
"api_key": []
}
],
"summary": "Retrieve a workflow",
"tags": [
"Workflows"
],
"x-immich-history": [
{
"version": "v2.3.0",
"state": "Added"
},
{
"version": "v2.3.0",
"state": "Alpha"
}
],
"x-immich-permission": "workflow.read",
"x-immich-state": "Alpha"
},
"put": {
"description": "Update the information of a specific workflow by its ID. This endpoint can be used to update the workflow name, description, trigger type, filters and actions order, etc.",
"operationId": "updateWorkflow",
"parameters": [
{
"name": "id",
"required": true,
"in": "path",
"schema": {
"format": "uuid",
"type": "string"
}
}
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/WorkflowUpdateDto"
}
}
},
"required": true
},
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/WorkflowResponseDto"
}
}
},
"description": ""
}
},
"security": [
{
"bearer": []
},
{
"cookie": []
},
{
"api_key": []
}
],
"summary": "Update a workflow",
"tags": [
"Workflows"
],
"x-immich-history": [
{
"version": "v2.3.0",
"state": "Added"
},
{
"version": "v2.3.0",
"state": "Alpha"
}
],
"x-immich-permission": "workflow.update",
"x-immich-state": "Alpha"
}
}
},
"info": {
@@ -13566,6 +13941,10 @@
"name": "People",
"description": "A person is a collection of faces, which can be favorited and named. A person can also be merged into another person. People are automatically created via the face recognition job."
},
{
"name": "Plugins",
"description": "A plugin is an installed module that makes filters and actions available for the workflow feature."
},
{
"name": "Search",
"description": "Endpoints related to searching assets via text, smart search, optical character recognition (OCR), and other filters like person, album, and other metadata. Search endpoints usually support pagination and sorting."
@@ -13621,6 +14000,10 @@
{
"name": "Views",
"description": "Endpoints for specialized views, such as the folder view."
},
{
"name": "Workflows",
"description": "A workflow is a set of actions that run whenever a triggering event occurs. Workflows also can include filters to further limit execution."
}
],
"servers": [
@@ -17022,6 +17405,10 @@
"pinCode.create",
"pinCode.update",
"pinCode.delete",
"plugin.create",
"plugin.read",
"plugin.update",
"plugin.delete",
"server.about",
"server.apkLinks",
"server.storage",
@@ -17071,6 +17458,10 @@
"userProfileImage.read",
"userProfileImage.update",
"userProfileImage.delete",
"workflow.create",
"workflow.read",
"workflow.update",
"workflow.delete",
"adminUser.create",
"adminUser.read",
"adminUser.update",
@@ -17367,6 +17758,152 @@
],
"type": "object"
},
"PluginActionResponseDto": {
"properties": {
"description": {
"type": "string"
},
"id": {
"type": "string"
},
"methodName": {
"type": "string"
},
"pluginId": {
"type": "string"
},
"schema": {
"nullable": true,
"type": "object"
},
"supportedContexts": {
"items": {
"$ref": "#/components/schemas/PluginContext"
},
"type": "array"
},
"title": {
"type": "string"
}
},
"required": [
"description",
"id",
"methodName",
"pluginId",
"schema",
"supportedContexts",
"title"
],
"type": "object"
},
"PluginContext": {
"enum": [
"asset",
"album",
"person"
],
"type": "string"
},
"PluginFilterResponseDto": {
"properties": {
"description": {
"type": "string"
},
"id": {
"type": "string"
},
"methodName": {
"type": "string"
},
"pluginId": {
"type": "string"
},
"schema": {
"nullable": true,
"type": "object"
},
"supportedContexts": {
"items": {
"$ref": "#/components/schemas/PluginContext"
},
"type": "array"
},
"title": {
"type": "string"
}
},
"required": [
"description",
"id",
"methodName",
"pluginId",
"schema",
"supportedContexts",
"title"
],
"type": "object"
},
"PluginResponseDto": {
"properties": {
"actions": {
"items": {
"$ref": "#/components/schemas/PluginActionResponseDto"
},
"type": "array"
},
"author": {
"type": "string"
},
"createdAt": {
"type": "string"
},
"description": {
"type": "string"
},
"filters": {
"items": {
"$ref": "#/components/schemas/PluginFilterResponseDto"
},
"type": "array"
},
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"title": {
"type": "string"
},
"updatedAt": {
"type": "string"
},
"version": {
"type": "string"
}
},
"required": [
"actions",
"author",
"createdAt",
"description",
"filters",
"id",
"name",
"title",
"updatedAt",
"version"
],
"type": "object"
},
"PluginTriggerType": {
"enum": [
"AssetCreate",
"PersonRecognized"
],
"type": "string"
},
"PurchaseResponse": {
"properties": {
"hideBuyButtonUntil": {
@@ -17438,7 +17975,8 @@
"library",
"notifications",
"backupDatabase",
"ocr"
"ocr",
"workflow"
],
"type": "string"
},
@@ -17552,6 +18090,9 @@
},
"videoConversion": {
"$ref": "#/components/schemas/QueueResponseDto"
},
"workflow": {
"$ref": "#/components/schemas/QueueResponseDto"
}
},
"required": [
@@ -17570,7 +18111,8 @@
"smartSearch",
"storageTemplateMigration",
"thumbnailGeneration",
"videoConversion"
"videoConversion",
"workflow"
],
"type": "object"
},
@@ -20420,6 +20962,9 @@
},
"videoConversion": {
"$ref": "#/components/schemas/JobSettingsDto"
},
"workflow": {
"$ref": "#/components/schemas/JobSettingsDto"
}
},
"required": [
@@ -20434,7 +20979,8 @@
"sidecar",
"smartSearch",
"thumbnailGeneration",
"videoConversion"
"videoConversion",
"workflow"
],
"type": "object"
},
@@ -21999,6 +22545,211 @@
"webm"
],
"type": "string"
},
"WorkflowActionItemDto": {
"properties": {
"actionConfig": {
"type": "object"
},
"actionId": {
"format": "uuid",
"type": "string"
}
},
"required": [
"actionId"
],
"type": "object"
},
"WorkflowActionResponseDto": {
"properties": {
"actionConfig": {
"nullable": true,
"type": "object"
},
"actionId": {
"type": "string"
},
"id": {
"type": "string"
},
"order": {
"type": "number"
},
"workflowId": {
"type": "string"
}
},
"required": [
"actionConfig",
"actionId",
"id",
"order",
"workflowId"
],
"type": "object"
},
"WorkflowCreateDto": {
"properties": {
"actions": {
"items": {
"$ref": "#/components/schemas/WorkflowActionItemDto"
},
"type": "array"
},
"description": {
"type": "string"
},
"enabled": {
"type": "boolean"
},
"filters": {
"items": {
"$ref": "#/components/schemas/WorkflowFilterItemDto"
},
"type": "array"
},
"name": {
"type": "string"
},
"triggerType": {
"allOf": [
{
"$ref": "#/components/schemas/PluginTriggerType"
}
]
}
},
"required": [
"actions",
"filters",
"name",
"triggerType"
],
"type": "object"
},
"WorkflowFilterItemDto": {
"properties": {
"filterConfig": {
"type": "object"
},
"filterId": {
"format": "uuid",
"type": "string"
}
},
"required": [
"filterId"
],
"type": "object"
},
"WorkflowFilterResponseDto": {
"properties": {
"filterConfig": {
"nullable": true,
"type": "object"
},
"filterId": {
"type": "string"
},
"id": {
"type": "string"
},
"order": {
"type": "number"
},
"workflowId": {
"type": "string"
}
},
"required": [
"filterConfig",
"filterId",
"id",
"order",
"workflowId"
],
"type": "object"
},
"WorkflowResponseDto": {
"properties": {
"actions": {
"items": {
"$ref": "#/components/schemas/WorkflowActionResponseDto"
},
"type": "array"
},
"createdAt": {
"type": "string"
},
"description": {
"type": "string"
},
"enabled": {
"type": "boolean"
},
"filters": {
"items": {
"$ref": "#/components/schemas/WorkflowFilterResponseDto"
},
"type": "array"
},
"id": {
"type": "string"
},
"name": {
"nullable": true,
"type": "string"
},
"ownerId": {
"type": "string"
},
"triggerType": {
"enum": [
"AssetCreate",
"PersonRecognized"
],
"type": "string"
}
},
"required": [
"actions",
"createdAt",
"description",
"enabled",
"filters",
"id",
"name",
"ownerId",
"triggerType"
],
"type": "object"
},
"WorkflowUpdateDto": {
"properties": {
"actions": {
"items": {
"$ref": "#/components/schemas/WorkflowActionItemDto"
},
"type": "array"
},
"description": {
"type": "string"
},
"enabled": {
"type": "boolean"
},
"filters": {
"items": {
"$ref": "#/components/schemas/WorkflowFilterItemDto"
},
"type": "array"
},
"name": {
"type": "string"
}
},
"type": "object"
}
}
}