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

View File

@@ -732,6 +732,7 @@ export type QueuesResponseDto = {
storageTemplateMigration: QueueResponseDto;
thumbnailGeneration: QueueResponseDto;
videoConversion: QueueResponseDto;
workflow: QueueResponseDto;
};
export type JobCreateDto = {
name: ManualJobName;
@@ -926,6 +927,36 @@ export type AssetFaceUpdateDto = {
export type PersonStatisticsResponseDto = {
assets: number;
};
export type PluginActionResponseDto = {
description: string;
id: string;
methodName: string;
pluginId: string;
schema: object | null;
supportedContexts: PluginContext[];
title: string;
};
export type PluginFilterResponseDto = {
description: string;
id: string;
methodName: string;
pluginId: string;
schema: object | null;
supportedContexts: PluginContext[];
title: string;
};
export type PluginResponseDto = {
actions: PluginActionResponseDto[];
author: string;
createdAt: string;
description: string;
filters: PluginFilterResponseDto[];
id: string;
name: string;
title: string;
updatedAt: string;
version: string;
};
export type SearchExploreItem = {
data: AssetResponseDto;
value: string;
@@ -1411,6 +1442,7 @@ export type SystemConfigJobDto = {
smartSearch: JobSettingsDto;
thumbnailGeneration: JobSettingsDto;
videoConversion: JobSettingsDto;
workflow: JobSettingsDto;
};
export type SystemConfigLibraryScanDto = {
cronExpression: string;
@@ -1667,6 +1699,54 @@ export type CreateProfileImageResponseDto = {
profileImagePath: string;
userId: string;
};
export type WorkflowActionResponseDto = {
actionConfig: object | null;
actionId: string;
id: string;
order: number;
workflowId: string;
};
export type WorkflowFilterResponseDto = {
filterConfig: object | null;
filterId: string;
id: string;
order: number;
workflowId: string;
};
export type WorkflowResponseDto = {
actions: WorkflowActionResponseDto[];
createdAt: string;
description: string;
enabled: boolean;
filters: WorkflowFilterResponseDto[];
id: string;
name: string | null;
ownerId: string;
triggerType: TriggerType;
};
export type WorkflowActionItemDto = {
actionConfig?: object;
actionId: string;
};
export type WorkflowFilterItemDto = {
filterConfig?: object;
filterId: string;
};
export type WorkflowCreateDto = {
actions: WorkflowActionItemDto[];
description?: string;
enabled?: boolean;
filters: WorkflowFilterItemDto[];
name: string;
triggerType: PluginTriggerType;
};
export type WorkflowUpdateDto = {
actions?: WorkflowActionItemDto[];
description?: string;
enabled?: boolean;
filters?: WorkflowFilterItemDto[];
name?: string;
};
/**
* List all activities
*/
@@ -3510,6 +3590,30 @@ export function getPersonThumbnail({ id }: {
...opts
}));
}
/**
* List all plugins
*/
export function getPlugins(opts?: Oazapfts.RequestOpts) {
return oazapfts.ok(oazapfts.fetchJson<{
status: 200;
data: PluginResponseDto[];
}>("/plugins", {
...opts
}));
}
/**
* Retrieve a plugin
*/
export function getPlugin({ id }: {
id: string;
}, opts?: Oazapfts.RequestOpts) {
return oazapfts.ok(oazapfts.fetchJson<{
status: 200;
data: PluginResponseDto;
}>(`/plugins/${encodeURIComponent(id)}`, {
...opts
}));
}
/**
* Retrieve assets by city
*/
@@ -4824,6 +4928,72 @@ export function getUniqueOriginalPaths(opts?: Oazapfts.RequestOpts) {
...opts
}));
}
/**
* List all workflows
*/
export function getWorkflows(opts?: Oazapfts.RequestOpts) {
return oazapfts.ok(oazapfts.fetchJson<{
status: 200;
data: WorkflowResponseDto[];
}>("/workflows", {
...opts
}));
}
/**
* Create a workflow
*/
export function createWorkflow({ workflowCreateDto }: {
workflowCreateDto: WorkflowCreateDto;
}, opts?: Oazapfts.RequestOpts) {
return oazapfts.ok(oazapfts.fetchJson<{
status: 201;
data: WorkflowResponseDto;
}>("/workflows", oazapfts.json({
...opts,
method: "POST",
body: workflowCreateDto
})));
}
/**
* Delete a workflow
*/
export function deleteWorkflow({ id }: {
id: string;
}, opts?: Oazapfts.RequestOpts) {
return oazapfts.ok(oazapfts.fetchText(`/workflows/${encodeURIComponent(id)}`, {
...opts,
method: "DELETE"
}));
}
/**
* Retrieve a workflow
*/
export function getWorkflow({ id }: {
id: string;
}, opts?: Oazapfts.RequestOpts) {
return oazapfts.ok(oazapfts.fetchJson<{
status: 200;
data: WorkflowResponseDto;
}>(`/workflows/${encodeURIComponent(id)}`, {
...opts
}));
}
/**
* Update a workflow
*/
export function updateWorkflow({ id, workflowUpdateDto }: {
id: string;
workflowUpdateDto: WorkflowUpdateDto;
}, opts?: Oazapfts.RequestOpts) {
return oazapfts.ok(oazapfts.fetchJson<{
status: 200;
data: WorkflowResponseDto;
}>(`/workflows/${encodeURIComponent(id)}`, oazapfts.json({
...opts,
method: "PUT",
body: workflowUpdateDto
})));
}
export enum ReactionLevel {
Album = "album",
Asset = "asset"
@@ -4976,6 +5146,10 @@ export enum Permission {
PinCodeCreate = "pinCode.create",
PinCodeUpdate = "pinCode.update",
PinCodeDelete = "pinCode.delete",
PluginCreate = "plugin.create",
PluginRead = "plugin.read",
PluginUpdate = "plugin.update",
PluginDelete = "plugin.delete",
ServerAbout = "server.about",
ServerApkLinks = "server.apkLinks",
ServerStorage = "server.storage",
@@ -5025,6 +5199,10 @@ export enum Permission {
UserProfileImageRead = "userProfileImage.read",
UserProfileImageUpdate = "userProfileImage.update",
UserProfileImageDelete = "userProfileImage.delete",
WorkflowCreate = "workflow.create",
WorkflowRead = "workflow.read",
WorkflowUpdate = "workflow.update",
WorkflowDelete = "workflow.delete",
AdminUserCreate = "adminUser.create",
AdminUserRead = "adminUser.read",
AdminUserUpdate = "adminUser.update",
@@ -5083,7 +5261,8 @@ export enum QueueName {
Library = "library",
Notifications = "notifications",
BackupDatabase = "backupDatabase",
Ocr = "ocr"
Ocr = "ocr",
Workflow = "workflow"
}
export enum QueueCommand {
Start = "start",
@@ -5104,6 +5283,11 @@ export enum PartnerDirection {
SharedBy = "shared-by",
SharedWith = "shared-with"
}
export enum PluginContext {
Asset = "asset",
Album = "album",
Person = "person"
}
export enum SearchSuggestionType {
Country = "country",
State = "state",
@@ -5255,3 +5439,11 @@ export enum OAuthTokenEndpointAuthMethod {
ClientSecretPost = "client_secret_post",
ClientSecretBasic = "client_secret_basic"
}
export enum TriggerType {
AssetCreate = "AssetCreate",
PersonRecognized = "PersonRecognized"
}
export enum PluginTriggerType {
AssetCreate = "AssetCreate",
PersonRecognized = "PersonRecognized"
}