Skip to content

راهنمای استفاده (Usage Guide) ​

قابلیت‌های کلیدی موتور گردش کار:

۱. شروع فرایند جدید
۲. فهرست‌گیری از اجراهای یک فرایند (processKey)
۳. ارجاع کار به کاربر، گروه، یا چند نفر هم‌زمان
۴. کارتابل وظایف باز
۵. بررسی وضعیت تکمیل ارجاع‌های چندنفره
۶. تکمیل وظیفه و بستن کامل پرونده
۷. دریافت آمار و فهرست فرایندهای مرتبط با یک کاربر
۸. رفتن خودکار به مرحله بعد با onAllCompleted (بعد از join موازی)

bash
dotnet run --project src/TaskFlow.Server

مستندات تعاملی Swagger: http://127.0.0.1:8081/swagger

عملیاتنحوهٔ فراخوانی (REST API)
شروع فرایندPOST /v1/processes/start با بدنهٔ { "processKey", "initiator", "parameters?" }
لیست اجراهاGET /v1/processes/{processKey}/instances
ارجاع کارPOST /v1/assignments با بدنهٔ { "definitionKey", "parentInstanceId?", "to" }
کارتابل وظایفGET /v1/tasks?user=mortenaho یا GET /v1/tasks?group=legal
تحویل گرفتن تسک (Claim)POST /v1/tasks/{id}/claim
لغو تحویل تسک (Unclaim)POST /v1/tasks/{id}/unclaim
وضعیت تکمیل چندنفرهGET /v1/instances/{id}/completion
تکمیل وظیفهPOST /v1/tasks/{id}/complete
تکمیل و پایان فرایندPOST /v1/tasks/{id}/complete-and-end
فرایندهای کاربرGET /v1/users/{user}/processes با فیلتر اختیاری state=open، closed یا notStarted

برای میکروسرویس‌ها مسیر پیشنهادی SDK کلاینت (TaskFlow.Client) است: یک بار TaskFlow.Server را بالا می‌آورید و هر سرویس فقط با HTTP به همان آدرس وصل می‌شود. در تست‌های واحد یا وقتی خودتان میزبان انجین هستید، می‌توانید Engine را داخل‌پردازشی هم بسازید. نمونهٔ curl: examples/curl.sh.


۱. مفاهیم پایه ​

واژهتوضیحات و مفهوم
processKeyکلید شناسهٔ نوع فرایند (مانند purchase یا employeeTermination)
definitionKeyکلید تعریف ثبت‌شده برای فرایند که در خروجی متد شروع بازگردانده می‌شود
instanceIdشناسهٔ یک نمونهٔ اجرایی؛ فراخوانی Start نمونهٔ ریشه را می‌سازد و هر ارجاع (AssignTo) یک نمونهٔ جدید ایجاد می‌کند
Taskرکورد وظیفه در کارتابل که به یک کاربر یا یک گروه تخصیص یافته است
Inbox / Tasksوظایف در وضعیت باز (open) متعلق به یک کاربر (شامل وظایف فردی و گروه‌های عضو) یا یک گروه

شناسهٔ انجام‌دهندهٔ عملیات از طریق هدر X-Actor-Id ارسال می‌گردد. این مقدار برای انجین یک شناسهٔ مات (opaque) است — مثلاً 102 به‌عنوان شناسهٔ کاربر در سامانهٔ خودتان. انجین جدول کاربران ندارد و لاگین نمی‌کند؛ فقط همان رشته را در تسک‌ها ذخیره و با آن مجوز عملیات را می‌سنجد.

اگر WF_USERS و WF_GROUP_* تنظیم نشوند، سرویس به‌صورت پیش‌فرض از OpenDirectory استفاده می‌کند: هر شناسهٔ کاربر یا گروه پذیرفته می‌شود و عضویت گروه اجباری نیست. وقتی فهرست کاربران/گروه‌ها را تعریف کنید، StaticDirectory فعال می‌شود و ارجاع به گروه خالی یا ادعای عضویت نادرست رد می‌گردد. در پروداکشن می‌توانید به‌جای آن‌ها IDirectory را به SSO / LDAP وصل کنید.


۲. استفاده از طریق SDK در زبان C#‎ ​

هدف این بخش: یک TaskFlow.Server از قبل بالا آمده (مثلاً در Docker یا کلاستر) و چند میکروسرویس که هر کدام فقط پکیج TaskFlow.Client را دارند و به همان آدرس HTTP وصل می‌شوند. انجین، دیتابیس و IDirectory روی سرور می‌مانند؛ کلاینت‌ها Engine یا Postgres را داخل سرویس خودشان نمی‌سازند.

mermaid
flowchart RL
    ms1["Ordering API"]
    ms2["HR API"]
    ms3["Legal BFF"]
    eng["TaskFlow.Server"]
    db[("Postgres")]

    ms1 -->|"TaskFlow.Client"| eng
    ms2 -->|"TaskFlow.Client"| eng
    ms3 -->|"TaskFlow.Client"| eng
    eng --> db

    style eng fill:#C2E5FF,stroke:#3DADFF,color:#1a1a2e
    style db fill:#CDF4D3,stroke:#66D575,color:#1a1a2e
    style ms1 fill:#f3e5f5,stroke:#9c27b0,color:#1a1a2e
    style ms2 fill:#f3e5f5,stroke:#9c27b0,color:#1a1a2e
    style ms3 fill:#f3e5f5,stroke:#9c27b0,color:#1a1a2e

پیش‌نیاز: سرور مشترک ​

bash
dotnet run --project src/TaskFlow.Server
# یا: docker compose up --build
# Base URL پیش‌فرض: http://127.0.0.1:8081

در پروداکشن روی سرور WF_API_KEYS را تنظیم کنید؛ هر میکروسرویس همان کلید را فقط در env بک‌اند خودش نگه می‌دارد و به‌صورت X-API-Key می‌فرستد (نه در مرورگر).

افزودن پکیج به میکروسرویس ​

xml
<ItemGroup>
  <ProjectReference Include="..\..\path\to\TaskFlow.Client\TaskFlow.Client.csproj" />
  <!-- یا پس از انتشار: <PackageReference Include="TaskFlow.Client" Version="..." /> -->
</ItemGroup>

ثبت در Program.cs هر میکروسرویس ​

csharp
using TaskFlow.Client;

builder.Services.AddTaskFlowClient(o =>
{
    o.BaseAddress = new Uri(builder.Configuration["TaskFlow:BaseUrl"]
        ?? "http://taskflow:8081/");
    o.ApiKey = builder.Configuration["TaskFlow:ApiKey"];   // همان WF_API_KEYS سرور
    o.TenantId = builder.Configuration["TaskFlow:TenantId"]; // اختیاری؛ مثلاً acme
});

// بعداً در کنترلر / هندلر:
// private readonly TaskFlowClient _tf;
// private readonly TaskFlowOrchestrator _orch;

بدون DI هم می‌توانید بسازید:

csharp
using TaskFlow.Client;

var http = new HttpClient { BaseAddress = new Uri("http://127.0.0.1:8081/") };
var tf = new TaskFlowClient(http, new TaskFlowClientOptions
{
    ApiKey = Environment.GetEnvironmentVariable("TASKFLOW_API_KEY"),
    TenantId = "default",
});
await tf.EnsureHealthy();

شروع فرایند ​

خروجی شامل DefinitionKey و InstanceId است. هدر X-Actor-Id را SDK از آرگومان initiator / actor می‌گذارد.

csharp
using TaskFlow.Application;
using TaskFlow.Client;

var started = await tf.Start(
    "purchase",
    "sara",
    new Dictionary<string, object?> { ["amount"] = 1.5e8 });

ارجاع به کاربر یا گروه ​

ToKind می‌تواند user، group یا users باشد (AssigneeKind). برای حالت چندنفره از ToIds استفاده کنید.

csharp
using TaskFlow.Domain;

var refer = await tf.AssignTo("sara", new AssignToInput
{
    DefinitionKey = started.DefinitionKey,
    ParentInstanceId = started.InstanceId,
    Title = "Legal review",
    ToKind = AssigneeKind.User,
    ToId = "mortenaho",
    // ToIds = ["mortenaho", "tina"],  // AssigneeKind.Users
});

تخصیص موازی ​

مرحله بعد را با onAllCompleted روی همان AssignTo تعریف کنید. هر کاربر فقط CompleteTask می‌زند؛ بعد از آخرین تکمیل، ParallelJoinHandler روی TaskCompleted ارجاع بعدی را می‌سازد.

mermaid
flowchart TD
    start([Start]) --> parallel[AssignTo users]
    parallel --> m[mortenaho]
    parallel --> c[tina]
    m --> join{all done?}
    c --> join
    join -->|no| wait[open tasks remain]
    join -->|yes| next[AssignTo onAllCompleted]
  • هر AssignTo یک instance فرزند می‌سازد؛ AllCompleted فقط برای همان فرزند است.
  • DefinitionKey و ParentInstanceId در onAllCompleted را می‌توان خالی گذاشت.

نمونه ​

csharp
var parallel = await tf.AssignTo("sara", new AssignToInput
{
    DefinitionKey = started.DefinitionKey,
    ParentInstanceId = started.InstanceId,
    Title = "تأیید موازی",
    ToKind = AssigneeKind.Users,
    ToIds = ["mortenaho", "tina"],
    OnAllCompleted = new AssignToInput
    {
        Title = "بررسی حقوقی",
        ToKind = AssigneeKind.Group,
        ToId = "legal",
    },
});

foreach (var task in parallel.Tasks)
    await tf.CompleteTask(task.Id, task.AssigneeId, "تأیید شد");

var legalInbox = await tf.PendingTasks(group: "legal");

REST معادل:

bash
curl -s -X POST http://127.0.0.1:8081/v1/assignments \
  -H 'Content-Type: application/json' -H 'X-Actor-Id: sara' \
  -d '{
    "definitionKey": "purchase",
    "parentInstanceId": "INSTANCE",
    "from": "sara",
    "title": "تأیید موازی",
    "join": "all",
    "to": { "kind": "users", "ids": ["mortenaho", "tina"] },
    "onAllCompleted": {
      "title": "بررسی حقوقی",
      "to": { "kind": "group", "id": "legal" }
    }
  }'

BFF ​

یک endpoint برای approve کافی است؛ taskId تفاوت می‌کند، handler یکی است. BFF فقط CompleteTask می‌زند — join با انجین است.

csharp
[HttpPost("tasks/{taskId}/approve")]
public async Task<IActionResult> Approve(string taskId, [FromBody] ApproveRequest body)
{
    var userId = User.FindFirst("sub")?.Value ?? "";
    await _tf.CompleteTask(taskId, userId, body.Note ?? "تأیید شد");
    return Ok();
}

برای برگرداندن approved / waiting_for_others / all_done در همان پاسخ: CompleteTaskWithOutcome و فیلد StatusKey.

ثبت کلاینت:

csharp
builder.Services.AddTaskFlowClient(o =>
{
    o.BaseAddress = new Uri("http://taskflow:8080/");
    o.ApiKey = builder.Configuration["TaskFlow:ApiKey"];
});

ProcessOrchestrator / CompleteAndAssignTo برای callback دستی قدیمی است.

نمونه in-process: examples/Scenario.
اپ کامل با UI: employee-termination-app (Next.js BFF، همان الگوی بالا).

مفهوممعنی
Users + ToIdsچند تسک موازی؛ همه باید done شوند
onAllCompletedمرحله بعد بعد از join
CompleteTaskبستن تسک؛ join در انجین
CompleteTaskWithOutcomeهمان + StatusKey برای پاسخ HTTP
CompleteAndAssignToقدیمی

کارتابل، تکمیل، و فرایندهای کاربر ​

csharp
var inbox = await tf.PendingTasks(user: "mortenaho");
var groupInbox = await tf.PendingTasks(group: "legal");

var ended = await tf.CompleteAndEnd(refer.Task!.Id, "mortenaho", "Case closed");
// ended.Process.Status == "completed"

var mine = await tf.ListUserProcesses("sara");
var open = await tf.ListUserProcesses("sara", state: "open");

سایر متدهای مهم SDK: ClaimTask / UnclaimTask، Completion(instanceId)، ListByProcessKey، GetInstance، RegisterDefinition.

چند سرویس، یک tenant یا چند tenant ​

سناریوتنظیم
همهٔ میکروسرویس‌ها یک سازمانTenantId را خالی بگذارید یا همه default
جداسازی داده بین مشتری‌هادر هر سرویس o.TenantId = "acme" یا در هر فراخوانی آرگومان tenantId
محیط امنهمان ApiKey مشترک بک‌اندها؛ React کلید را نمی‌بیند

حالت توکار (فقط وقتی خودتان سرور را می‌سازید) ​

اگر می‌خواهید در تست یا یک باینری واحد، انجین را داخل همان پروسس داشته باشید (نه اتصال به سرور ریموت)، هنوز می‌توانید Engine + MemoryStore/PostgresStore بسازید. دایرکتوری کاربران (IDirectory) فقط روی میزبان سرور معنی دارد، نه روی میکروسرویس کلاینت:

csharp
using TaskFlow.Application;
using TaskFlow.Infrastructure;

var dir = new StaticDirectory(
    ["sara", "mortenaho", "tina", "hamid"],
    new Dictionary<string, IReadOnlyList<string>>
    {
        ["legal"] = ["mortenaho", "tina"],
        ["finance"] = ["hamid", "tina"],
    });
var eng = new Engine(new MemoryStore(), dir);
var orch = new ProcessOrchestrator(eng);

برای اتصال سرور به SSO / LDAP، IDirectory را روی همان پروسس TaskFlow.Server پیاده کنید (نه داخل هر میکروسرویس). نمونهٔ رابط:

csharp
public interface IDirectory
{
    bool EnforcesMembership { get; }
    Task<bool> UserExists(string userId, CancellationToken cancellationToken = default);
    Task<IReadOnlyList<string>> GroupMembers(string groupId, CancellationToken cancellationToken = default);
    Task<IReadOnlyList<string>> UserGroups(string userId, CancellationToken cancellationToken = default);
    Task<bool> IsMember(string userId, string groupId, CancellationToken cancellationToken = default);
}

جزئیات معماری کلید API و BFF: architecture.md.


۳. راهنمای وب‌سرویس REST API ​

آدرس پایه: http://localhost:8081.
هدرهای اصلی:

  • X-Actor-Id: شناسهٔ کاربر انجام‌دهندهٔ درخواست (اجباری در اکثر عملیات). این هدر لاگین نیست و انجین توکن صادر نمی‌کند.
  • X-Tenant-Id: شناسهٔ سازمان جهت جداسازی داده‌ها (اختیاری؛ پیش‌فرض: default).
  • X-API-Key: کلید مشترک سرویس برای بک‌اند یا API Gateway. در پروداکشن اجباری است (متغیر WF_API_KEYS). مسیرهای /health و مستندات مستثنی هستند.

اتصال از React: کلید را در مرورگر نفرستید ​

X-API-Key برای curl، تست، و بک‌اند شما است، نه برای fetch در React. اگر فرانت کلید را در هدر بگذارد، در Network مرورگر لو می‌رود. معماری پیشنهادی و دیاگرام‌ها: architecture.md — بخش ۶.

React (فقط API خودتان، بدون کلید انجین):

فراخوانی مستقیم انجین از مرورگر با X-API-Key اشتباه است — کلید در Network لو می‌رود.

js
await fetch("/api/inbox", { credentials: "include" });

بک‌اند / BFF (این‌جا کلید از env خوانده می‌شود):

userId از جلسهٔ لاگین اپ شما می‌آید، نه از query مرورگر. نمونهٔ Express؛ معادل آن در ASP.NET / Nest یکسان است.

js
app.get("/api/inbox", async (req, res) => {
  const userId = req.session.userId;
  const r = await fetch(`${process.env.TASKFLOW_URL}/v1/tasks?user=${encodeURIComponent(userId)}`, {
    headers: {
      "X-API-Key": process.env.WF_API_KEYS.split(",")[0],
      "X-Actor-Id": userId,
    },
  });
  res.status(r.status).json(await r.json());
});

فلو خلاصه: کاربر در اپ شما لاگین می‌کند → React به /api/... همان اپ درخواست می‌زند → بک‌اند جلسه را چک می‌کند → با X-API-Key و X-Actor-Id به انجین می‌زند → پاسخ را به React برمی‌گرداند.

پیاده‌سازی مرجع: employee-termination-app (app/api/* + lib/infrastructure/engine).

۱. شروع فرایند (Start) ​

json
POST /v1/processes/start
{ "processKey": "purchase", "initiator": "sara", "parameters": { "amount": 150000000 } }

→ { "definitionKey": "purchase", "instanceId": "..." }

در صورت خالی بودن فیلد initiator، مقدار هدر X-Actor-Id جایگزین می‌شود. اگر برای processKey تعریفی وجود نداشته باشد، به‌صورت خودکار ساخته می‌شود.

یک processKey می‌تواند بارها شروع شود (برای نمونه فرایند employeeTermination برای هر کارمند به‌طور مستقل اجرا می‌گردد):

bash
GET /v1/processes/employeeTermination/instances
json
{
  "processKey": "employeeTermination",
  "total": 2,
  "instances": [
    {
      "instanceId": "...",
      "initiator": "hr",
      "status": "running",
      "parameters": { "employeeId": "1002" },
      "tasks": [ ... ],
      "taskTotal": 1,
      "tasksOpen": 1,
      "allTasksCompleted": false
    }
  ]
}

فهرست فوق فقط نمونه‌های ریشه (Start) را بازمی‌گرداند؛ وظایف مربوط به ارجاع‌های فرزند نیز در فیلد tasks همان نمونه لیست می‌شوند.

۲. ارجاع کار (Assignment) ​

parentInstanceId همان instanceId دریافتی از پاسخ Start است.

json
POST /v1/assignments
{
  "definitionKey": "purchase",
  "parentInstanceId": "<instanceId from start>",
  "from": "sara",
  "title": "Review",
  "to": { "kind": "user", "id": "mortenaho" }
}

→ { "instanceId": "...", "definitionKey": "purchase", "task": { ... }, "tasks": [ ... ] }

در صورت خالی بودن فیلد from، مقدار هدر X-Actor-Id درج می‌شود.

مقدار to.kindفیلدهای مورد نیازنتیجه
useridایجاد یک تسک اختصاصی برای همان کاربر
groupidایجاد یک تسک گروهی؛ کلیهٔ اعضای گروه آن را در کارتابل مشاهده می‌کنند
usersidsایجاد یک تسک مجزا به‌ازای هر کاربر؛ وضعیت تکمیل کل ارجاع با شناسهٔ instanceId قابل رهگیری است

۳. کارتابل وظایف (Tasks) ​

  • GET /v1/tasks?user=mortenaho — دریافت وظایف شخصی کاربر mortenaho به‌همراه وظایف گروه‌هایی که وی در آن‌ها عضویت دارد.
  • GET /v1/tasks?group=legal — صرفاً وظایف ارجاع‌شده به گروه legal.

هر تسک دارای مشخصه‌های assigneeKind و assigneeId است تا مالکیت آن مشخص باشد.

۴. تحویل گرفتن وظیفهٔ گروهی (Claim) ​

تسک‌های گروهی در ابتدا در وضعیت open قرار دارند و برای همهٔ اعضای گروه قابل مشاهده هستند. عضوی که می‌خواهد وظیفه را انجام دهد آن را رزرو می‌کند:

bash
POST /v1/tasks/{id}/claim
{ "from": "mortenaho" }

وضعیت تسک به claimed و فیلد claimedBy به mortenaho تغییر می‌یابد. در صورت تلاش عضو دیگر برای کلیم هم‌زمان، خطای 409 Conflict بازگردانده شده و تسک از کارتابل سایر اعضا خارج می‌شود. پس از این مرحله، صرفاً کاربر mortenaho مجاز به تکمیل تسک خواهد بود.

با فراخوانی POST /v1/tasks/{id}/unclaim تسک مجدداً آزاد شده و به وضعیت open بازمی‌گردد.

تسک‌های فردی نیازی به کلیم اجباری ندارند و مستقیماً قابل تکمیل هستند.

۵. بررسی وضعیت تکمیل چندنفره (Completion) ​

bash
GET /v1/instances/{assignmentInstanceId}/completion
json
{
  "instanceId": "...",
  "allCompleted": false,
  "total": 3,
  "completed": 1,
  "open": 2,
  "tasks": [ ... ]
}

پاسخ متد POST /v1/tasks/{id}/complete نیز شامل فیلد completion است.

تکمیل معمولی صرفاً همان وظیفه (و در صورت اتمام همهٔ وظایف ارجاع، اینستنس ارجاع مربوطه) را می‌بندد؛ اما اینستنس ریشه در وضعیت running باقی می‌ماند.

۶. تکمیل وظیفه و بستن کل فرایند (Complete and End) ​

مسیر POST /v1/tasks/{id}/complete-and-end با همان مجوزهای تکمیل وظیفه، اقدامات زیر را به‌صورت یکپارچه انجام می‌دهد:

۱. وضعیت وظیفهٔ جاری را به done تغییر می‌دهد.
۲. سایر وظایف باز (open یا claimed) در کل درخت فرایند را به وضعیت cancelled منتقل کرده و از کارتابل‌ها خارج می‌سازد.
۳. اینستنس ریشه و تمام ارجاع‌های فرزند را در وضعیت completed قرار می‌دهد.

پس از این مرحله، ثبت هرگونه ارجاع جدید روی این فرایند با خطای 400 Bad Request مواجه خواهد شد.

۷. فرایندهای کاربر (User Processes) ​

متد GET /v1/users/sara/processes فهرست فرایندهایی که توسط کاربر sara ایجاد شده‌اند را بازمی‌گرداند:

وضعیت (state)توضیحات
notStartedفرایند شروع شده اما هنوز ارجاعی برای آن ثبت نشده است
openفرایند دارای ارجاع بوده و ریشه همچنان در وضعیت running قرار دارد
closedفرایند به‌طور کامل خاتمه یافته (completed) است
json
{
  "user": "sara",
  "open": 1,
  "closed": 2,
  "notStarted": 3,
  "total": 6,
  "instances": [ ... ]
}

با ارسال پارامتر ?state=open، لیست خروجی بر اساس فرایندهای باز فیلتر می‌شود؛ در حالی که مقادیر آماری (open, closed, notStarted, total) همواره وضعیت کلی را گزارش می‌دهند.


۴. پیکربندی محیط و Docker ​

همهٔ تنظیمات اجرای سرویس از متغیرهای محیطی خوانده می‌شوند (کلاس AppComposition). مقداردهی از طریق .env، Docker، یا محیط سیستم کافی است؛ فایل تنظیمات جداگانه لازم نیست.

آدرس گوش دادن (Listen) ​

اگر ASPNETCORE_URLS تنظیم شده باشد، همان اولویت دارد و ADDR نادیده گرفته می‌شود. در غیر این صورت ADDR استفاده می‌شود (پیش‌فرض :8081).

  • مقدارهایی مثل :8081 روی همهٔ اینترفیس‌ها باز می‌شوند (http://0.0.0.0:8081).
  • می‌توانید آدرس کامل هم بدهید، مثلاً http://127.0.0.1:9000.

ذخیره‌سازی ​

وضعیت DATABASE_URLرفتار
خالیداده‌ها در حافظه (MemoryStore) می‌مانند و با خاموش شدن سرویس پاک می‌شوند
تنظیم‌شدهاتصال به Postgres؛ اگر دیتابیس هنوز آماده نباشد، تا حدود ۳۰ ثانیه تلاش مجدد می‌کند

فایل docker-compose.yml به‌طور پیش‌فرض Postgres را بالا می‌آورد و DATABASE_URL را تنظیم می‌کند. جزئیات جداول: database.md.

کاربران و گروه‌ها (Directory) ​

وضعیترفتار
نه WF_USERS و نه WF_GROUP_*حالت باز (OpenDirectory): هر شناسهٔ غیرخالی کاربر/گروه پذیرفته می‌شود
حداقل یکی تنظیم شدهحالت ثابت (StaticDirectory): فقط همان کاربران و عضویت‌های تعریف‌شده معتبرند

مثال:

bash
WF_USERS=sara,mortenaho,tina,hamid
WF_GROUP_legal=mortenaho,tina
WF_GROUP_finance=hamid,tina

نام گروه از پسوند متغیر گرفته می‌شود (WF_GROUP_legal → گروه legal) و به حروف کوچک نرمال می‌شود. مقادیر با ویرگول جدا می‌شوند.

کلید API ​

WF_API_KEYS یک یا چند کلید مشترک سرویس است (با ویرگول). این توکن لاگین کاربر نیست؛ فقط بک‌اند یا Gateway شما باید آن را با هدر X-API-Key یا Authorization: Bearer بفرستد. React و مرورگر نباید این کلید را ببینند.

محیط (ASPNETCORE_ENVIRONMENT)WF_API_KEYS
Developmentاختیاری؛ خالی = احراز هویت API خاموش
هر چیز دیگر (مثلاً Production)اجباری؛ بدون آن سرویس اصلاً بالا نمی‌آید

خلاصهٔ متغیرها ​

متغیرپیش‌فرضنقش
ADDR:8081آدرس گوش دادن؛ فقط وقتی ASPNETCORE_URLS خالی است
ASPNETCORE_URLSخالیاولویت بالاتر برای آدرس گوش دادن (استاندارد ASP.NET)
DATABASE_URLخالیاتصال Postgres؛ خالی = حافظه موقت
WF_USERSخالیفهرست کاربران (ویرگول‌جدا)؛ اگر کاربران یا گروه‌ها تعریف شوند، حالت ثابت فعال می‌شود
WF_GROUP_<id>—اعضای گروه id، مثلاً WF_GROUP_legal=a,b
WF_API_KEYSخالیکلید(های) مشترک API؛ خارج از Development اجباری
ASPNETCORE_ENVIRONMENTبسته به اجرادر Development می‌توان بدون کلید کار کرد

نمونهٔ اجرای محلی با Docker:

bash
cp .env.example .env
docker compose up --build
curl -s http://localhost:8081/health
curl -s -H 'X-API-Key: local-dev-key' -H 'X-Actor-Id: sara' \
  http://localhost:8081/v1/users/sara/processes

نمونهٔ curl بالا برای تست از سرور/ترمینال است، نه برای کپی در فرانت.

TaskFlow — موتور گردش کار WorkFlowEngine