HeadlessPluginApi 1.0.1

dotnet add package HeadlessPluginApi --version 1.0.1
                    
NuGet\Install-Package HeadlessPluginApi -Version 1.0.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="HeadlessPluginApi" Version="1.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="HeadlessPluginApi" Version="1.0.1" />
                    
Directory.Packages.props
<PackageReference Include="HeadlessPluginApi" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add HeadlessPluginApi --version 1.0.1
                    
#r "nuget: HeadlessPluginApi, 1.0.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package HeadlessPluginApi@1.0.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=HeadlessPluginApi&version=1.0.1
                    
Install as a Cake Addin
#tool nuget:?package=HeadlessPluginApi&version=1.0.1
                    
Install as a Cake Tool

🔌 HeadlessPluginApi

Thư viện Plugin Core - Tích hợp với nền tảng Low-Code Headless của Thần Nông

.NET NuGet License


📋 Mục Lục


🎯 Giới Thiệu

HeadlessPluginApi là thư viện Core Plugin cho phép developer tự do phát triển các plugin độc lập với host application. Plugin sau khi phát triển xong có thể triển khai trên host app như một module có thể addon vào hệ thống chính.

Tại sao cần HeadlessPluginApi?

Vấn Đề Giải Pháp của HeadlessPluginApi
❌ Monolith khó mở rộng Plugin-based architecture - Tách biệt logic nghiệp vụ
❌ Deploy lại toàn bộ app khi thay đổi nhỏ Hot reload/unload - Load plugin mới không cần restart
❌ Khó kiểm soát quyền truy cập JsonLogic Authorization - Phân quyền linh hoạt theo context
❌ Thiếu chuẩn hoá API Auto schema generation - Tự động tạo OpenAPI schema
❌ Vendor lock-in Isolated AssemblyLoadContext - Plugin hoàn toàn độc lập

Tính Năng Chính

1. Plugin Lifecycle Management

  • Load/Unload/Reload plugins động
  • Isolated AssemblyLoadContext (McMaster.NETCore.Plugins)
  • Version-based DLL naming (PluginName_1.0.0.dll)
  • Auto-discovery từ bin folder (Debug mode)
  • Manifest support (plugin.json)

2. Command Execution System

  • Strongly-typed parameters - Tự động deserialize từ JSON
  • Async/await support - Task<object?> return type
  • DI injection - IServiceProvider, UserSessionContext, CancellationToken
  • Qualified command names - PluginName.CommandName để tránh conflict
  • Scoped service provider - Mỗi execution có scope riêng

3. Authorization & Security

  • [RequireAuth] attribute - Declarative authorization
  • JsonLogic evaluation - Flexible rule-based authorization
  • Command-level security - Phân quyền chi tiết từng command
  • UserSessionContext integration - Tích hợp với hệ thống người dùng

4. Schema Auto-Generation

  • [CommandParameter] attribute - Metadata cho tham số
  • Auto-generate OpenAPI schema - Từ method signature
  • IntelliSense support - XML docs trong schema
  • Swagger UI integration - Try-it-out UI tự động

5. Developer Experience

  • Zero-config debug mode - Project reference + breakpoint support
  • DiscoveryHostApi - Standalone debug environment
  • In-memory implementations - Không cần MongoDB/Redis cho dev
  • Template download - Sample plugin qua API

🏗️ Kiến Trúc

┌─────────────────────────────────────────────────────────────────┐
│                        Host Application                         │
│  ┌───────────────────────────────────────────────────────────┐ │
│  │              PluginEngine (IPluginEngine)                 │ │
│  │  • Load/Unload/Reload lifecycle                           │ │
│  │  • Metadata persistence (MongoDB)                         │ │
│  └───────────┬─────────────────────────────────┬─────────────┘ │
│              │                                 │                 │
│  ┌───────────▼──────────┐          ┌──────────▼──────────────┐ │
│  │ PluginLoader         │          │ PluginRegistry          │ │
│  │ (McMaster.Plugins)   │          │ (Thread-safe cache)     │ │
│  │ • Isolated ALC       │          │ • GetPlugin()           │ │
│  │ • Auto-discovery     │          │ • Resolve(command)      │ │
│  └──────────────────────┘          └─────────────────────────┘ │
│                                                │                 │
│                                    ┌───────────▼──────────────┐ │
│                                    │ PluginDispatcher         │ │
│                                    │ • Route commands         │ │
│                                    │ • Authorization check    │ │
│                                    │ • Scoped DI              │ │
│                                    └──────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
                                     │
                          ┌──────────▼──────────┐
                          │    Plugin DLL       │
                          │  ┌──────────────┐   │
                          │  │ PluginEntry  │   │
                          │  │ (IPluginEntry)│  │
                          │  └──────┬───────┘   │
                          │         │           │
                          │  ┌──────▼───────┐   │
                          │  │  Commands    │   │
                          │  │ (IPluginCmd) │   │
                          │  └──────────────┘   │
                          └─────────────────────┘

Luồng Thực Thi Command

1. HTTP Request → POST /api/plugin/execute
   {
     "command": "DemoCalculator.add",
     "payload": { "a": 10, "b": 5 }
   }

2. PluginDispatcher.ExecuteAsync()
   ├─ Resolve plugin & command → PluginRegistry
   ├─ Authorization check → JsonLogicAuthEvaluator
   ├─ Create scoped ServiceProvider
   ├─ Bind parameters (UserSessionContext, payload, IServiceProvider, CancellationToken)
   └─ Invoke command.ExecuteAsync() via reflection

3. Command Execution
   public async Task<object?> ExecuteAsync(
       UserSessionContext ctx,
       int a, 
       int b,
       IServiceProvider services,
       CancellationToken ct)
   {
       var logger = services.GetRequiredService<ILogger<AddCommand>>();
       logger.LogInformation("Adding {A} + {B}", a, b);
       return new { result = a + b };
   }

4. Response → PluginExecuteResponse
   {
     "success": true,
     "result": { "result": 15 },
     "executionTimeMs": 12
   }

💻 Yêu Cầu Hệ Thống

  • .NET 8 SDK trở lên
  • Visual Studio 2022 hoặc VS Code (với C# extension)
  • Headless.SharedServices (core dependency)

📦 Cài Đặt

1. Cài Đặt NuGet Package

dotnet add package HeadlessPluginApi --version 1.0.1

Hoặc trong .csproj:

<ItemGroup>
  <PackageReference Include="HeadlessPluginApi" Version="1.0.1" />
</ItemGroup>

2. Cài Đặt Dependencies

# Core dependency (bắt buộc)
dotnet add package Headless.SharedServices

# Optional dependencies (nếu chưa có)
dotnet add package Microsoft.Extensions.DependencyInjection
dotnet add package Microsoft.Extensions.Hosting

🚀 Hướng Dẫn Sử Dụng

1. Tạo Plugin Đơn Giản

Bước 1: Tạo Plugin Entry
using HeadlessPluginApi.Abstractions;

public class MyPlugin : PluginEntryBase
{
    protected override void OnLoad(IServiceProvider hostServices)
    {
        Logger?.LogInformation("MyPlugin loading...");
    }
    
    public override void OnInit()
    {
        Logger?.LogInformation("MyPlugin initialized with {Count} commands", 
            GetCommands().Count);
    }
    
    public override void OnUnload()
    {
        Logger?.LogInformation("MyPlugin unloading...");
    }
}
Bước 2: Thêm Assembly Metadata (cho auto-detect Name/Version)

<PropertyGroup>
  <AssemblyTitle>MyPlugin</AssemblyTitle>
  <Version>1.0.0</Version>
  <AssemblyVersion>1.0.0</AssemblyVersion>
</PropertyGroup>

2. Định Nghĩa Commands

Command Đơn Giản
using HeadlessPluginApi.Abstractions;
using Headless.Contracts.Models.Users;

public class AddCommand : PluginCommand
{
    public override string CommandName => "add";
    public override string Description => "Cộng hai số";
    
    public async Task<object?> ExecuteAsync(
        UserSessionContext context,
        int a,
        int b,
        CancellationToken cancellationToken)
    {
        return new 
        { 
            result = a + b,
            operation = "add",
            executedBy = context.user?.userName ?? "Anonymous"
        };
    }
}
Command với DI Services
public class SaveUserCommand : PluginCommand
{
    public override string CommandName => "save_user";
    
    public async Task<object?> ExecuteAsync(
        UserSessionContext context,
        string username,
        string email,
        IServiceProvider services,
        CancellationToken cancellationToken)
    {
        // Resolve services từ host
        var db = services.GetRequiredService<IMultiScopeDBRepository>();
        var logger = services.GetRequiredService<ILoggerFactory>()
            .CreateLogger<SaveUserCommand>();
        
        logger.LogInformation("Saving user: {Username}", username);
        
        var user = new User { Username = username, Email = email };
        await db.InsertOne(user, cancellationToken: cancellationToken);
        
        return new { success = true, userId = user.Id };
    }
}

3. Tích Hợp vào Host Application

Bước 1: Register Plugin Module
// Program.cs (ASP.NET Core)
using HeadlessPluginApi.Hosting;

var builder = WebApplication.CreateBuilder(args);

// ✅ Register plugin services
builder.Services.AddPluginModule(options =>
{
    options.pluginFolder = Path.Combine(builder.Environment.ContentRootPath, "Plugins");
    options.autoLoadOnStartup = true;
    options.enableManagementApi = true;
});

// ✅ Register host services (plugins có thể dùng)
builder.Services.AddSingleton<IMultiScopeDBRepository, MyDbRepo>();
builder.Services.AddSingleton<ICacheRepository, InMemoryCacheRepo>();

var app = builder.Build();

// ✅ Map plugin endpoints
app.MapPluginEndpoints();

app.Run();
Bước 2: Upload Plugin
# Build plugin
cd MyPlugin
dotnet build -c Release

# Upload qua API
curl -X POST http://localhost:5000/api/plugin/upload \
  -F "file=@bin/Release/net8.0/MyPlugin.dll"
Bước 3: Load Plugin
curl -X POST http://localhost:5000/api/plugin/load \
  -H "Content-Type: application/json" \
  -d '{"pluginPath": "MyPlugin"}'

4. Thực Thi Commands

Via HTTP API
curl -X POST http://localhost:5000/api/plugin/execute \
  -H "Content-Type: application/json" \
  -d '{
    "command": "MyPlugin.add",
    "payload": { "a": 10, "b": 5 }
  }'

Response:

{
  "success": true,
  "result": {
    "result": 15,
    "operation": "add",
    "executedBy": "admin@example.com"
  },
  "executionTimeMs": 8
}
Via C# Code
public class MyService
{
    private readonly IPluginDispatcher _dispatcher;
    private readonly UserSessionContext _ctx;
    
    public async Task<object?> ExecutePluginCommand()
    {
        var payload = JsonDocument.Parse(@"{""a"": 10, ""b"": 5}").RootElement;
        
        var result = await _dispatcher.ExecuteAsync(
            "MyPlugin.add",
            payload,
            _ctx,
            CancellationToken.None);
        
        return result;
    }
}

🔐 Tính Năng Nâng Cao

Authorization (JsonLogic)

Định Nghĩa Quyền Trên Command
using HeadlessPluginApi.Authorization;

public class DeleteUserCommand : PluginCommand
{
    public override string CommandName => "delete_user";
    
    // ✅ Chỉ admin mới được xóa user
    [RequireAuth(
        rule: @"{
            ""==="": [
                {""var"": ""user.role""},
                ""admin""
            ]
        }",
        message: "Chỉ admin mới có quyền xóa người dùng")]
    public async Task<object?> ExecuteAsync(
        UserSessionContext context,
        string userId,
        CancellationToken cancellationToken)
    {
        // Implementation
    }
}
Authorization Check Flow
1. PluginDispatcher.ExecuteAsync()
   │
2. PluginAuthorizationEvaluator.EvaluateCommandAsync()
   │
   ├─ Check [RequireAuth] attribute
   │
   ├─ Build JsonLogic context:
   │  {
   │    "user": { "userId": "abc", "role": "admin", ... },
   │    "payload": { "userId": "user123" },
   │    "command": "delete_user"
   │  }
   │
   ├─ Evaluate rule với JsonLogic.Net
   │  {"===": [{"var": "user.role"}, "admin"]}
   │
   └─ Return AuthorizationResult
      • Succeeded = true  → Continue execution
      • Succeeded = false → Throw PluginAuthorizationException (403)
Ví Dụ Rules Phức Tạp
// Rule 1: Chỉ admin hoặc chính chủ mới được sửa
[RequireAuth(
    rule: @"{
        ""or"": [
            {""==="": [{""var"": ""user.role""}, ""admin""]},
            {""==="": [{""var"": ""user.userId""}, {""var"": ""payload.userId""}]}
        ]
    }"]

// Rule 2: Chỉ được thực hiện trong giờ hành chính (8-17h)
[RequireAuth(
    rule: @"{
        ""and"": [
            {"">="": [{""var"": ""context.hour""}, 8]},
            {""<="": [{""var"": ""context.hour""}, 17]}
        ]
    }")]

Auto Schema Generation

Thêm Metadata Cho Parameters
using HeadlessPluginApi.Attributes;

public class CreateOrderCommand : PluginCommand
{
    public override string CommandName => "create_order";
    
    public async Task<object?> ExecuteAsync(
        UserSessionContext context,
        
        [CommandParameter(
            description: "Mã khách hàng",
            required: true,
            example: "CUST001")]
        string customerId,
        
        [CommandParameter(
            description: "Danh sách sản phẩm (product_id và quantity)",
            required: true)]
        List<OrderItem> items,
        
        [CommandParameter(
            description: "Ghi chú đơn hàng",
            required: false)]
        string? notes,
        
        CancellationToken cancellationToken)
    {
        // Implementation
    }
}
Lấy Schema Qua API
GET /api/plugin/commands/MyPlugin.create_order/schema

Response:

{
  "success": true,
  "pluginName": "MyPlugin",
  "commandName": "create_order",
  "parameterSchema": {
    "parameters": [
      {
        "name": "customerId",
        "type": "string",
        "description": "Mã khách hàng",
        "required": true,
        "example": "CUST001"
      },
      {
        "name": "items",
        "type": "array",
        "description": "Danh sách sản phẩm (product_id và quantity)",
        "required": true,
        "itemType": "OrderItem"
      },
      {
        "name": "notes",
        "type": "string",
        "description": "Ghi chú đơn hàng",
        "required": false
      }
    ]
  },
  "authorization": {
    "requiresAuth": false
  }
}

Version Management

Quy Ước Naming
✅ ĐÚNG (recommended):
   MyPlugin_1.0.0.dll
   MyPlugin_1.0.1.dll
   MyPlugin_1.1.0.dll
   MyPlugin_2.0.0.dll

⚠️ Hỗ trợ (legacy):
   MyPlugin.20250116120000000.dll  (timestamp format)
   MyPlugin.dll                     (fallback - không khuyến nghị)
Upload Workflow
# Version 1.0.0 (initial)
POST /api/plugin/upload
  → Uploaded: MyPlugin_1.0.0.dll ✅

# Version 1.0.0 again (duplicate)
POST /api/plugin/upload
  → 409 Conflict: Version 1.0.0 already exists ❌

# Version 1.1.0 (upgrade)
POST /api/plugin/upload
  → Uploaded: MyPlugin_1.1.0.dll ✅

# Reload để dùng version mới
POST /api/plugin/reload/MyPlugin
  → Loads MyPlugin_1.1.0.dll (latest version)
Version Selection Logic
// PluginLoader tự động chọn DLL theo thứ tự:
1. Highest semantic version (1.2.0 > 1.1.0 > 1.0.1)
2. Latest timestamp (nếu dùng format cũ)
3. Latest file modification time

Hot Reload/Unload

Reload Plugin Không Restart App
# Bước 1: Build plugin mới
dotnet build -c Release

# Bước 2: Upload version mới
curl -X POST http://localhost:5000/api/plugin/upload \
  -F "file=@bin/Release/net8.0/MyPlugin.dll"

# Bước 3: Reload
curl -X POST http://localhost:5000/api/plugin/reload/MyPlugin

⚠️ Lưu ý:

  • Plugin cũ sẽ bị unload (dispose resources)
  • AssemblyLoadContext mới được tạo
  • Tất cả commands được re-register
  • Active requests vẫn chạy với code cũ (safe)
Reload All Plugins
POST /api/plugin/reload-all

Behavior:

  1. Reload tất cả loaded plugins
  2. Auto-load các plugin chưa load trong Plugins/ folder
  3. Chọn version mới nhất cho mỗi plugin

🌐 API Endpoints

Plugin Management

Method Endpoint Description Auth Required
POST /api/plugin/upload Upload DLL/ZIP với version check ✅ Root
POST /api/plugin/load Load plugin từ path ✅ Root
POST /api/plugin/unload/{name} Unload plugin ✅ Root
POST /api/plugin/reload/{name} Reload plugin ✅ Root
POST /api/plugin/reload-all Reload all plugins ✅ Root
DELETE /api/plugin/remove/{name} Xóa plugin folder ✅ Root
GET /api/plugin/folders List plugin folders ❌ Public
GET /api/plugin/list List loaded plugins ❌ Public
GET /api/plugin/commands Get all commands ❌ Public
GET /api/plugin/commands/{name}/schema Get command schema ❌ Public

Plugin Execution

Method Endpoint Description Auth Required
POST /api/plugin/execute Execute command ❌ Public (command-level auth)

Developer Tools

Method Endpoint Description
GET /api/plugin/sdk/download Download SDK ZIP (DLL + docs)
GET /api/plugin/sdk/info Get SDK version info
GET /api/plugin/template/download Download sample plugin template
GET /api/plugin/services Discover available DI services
GET /api/plugin/services/{name} Get service detail với code examples

🐛 Debugging Plugins

Debug Mode (Project Reference)

Xem chi tiết tại: DiscoveryHostApi/README.md

Quick Start
  1. Add project reference trong DiscoveryHostApi.csproj:
<ItemGroup Condition="'$(Configuration)' == 'Debug'">
  <ProjectReference Include="..\MyPlugin\MyPlugin.csproj" />
</ItemGroup>
  1. Build plugin:
dotnet build -c Debug
  1. Run DiscoveryHostApi với debugger:
cd DiscoveryHostApi
dotnet run
  1. Set breakpoints trong plugin code → F5 trong Visual Studio

  2. Execute command qua Swagger UI → Breakpoint hit!


📚 Ví Dụ Mẫu

DemoCalculator Plugin

# Download template
curl -O http://localhost:5000/api/plugin/template/download
unzip DemoPluginTemplate.zip

# Explore code
cd DemoCalculator
cat Commands/AddCommand.cs
cat plugin.json

# Build
dotnet build -c Release

# Upload
cd bin/Release/net8.0
curl -X POST http://localhost:5000/api/plugin/upload \
  -F "file=@DemoCalculator.dll"

# Load
curl -X POST http://localhost:5000/api/plugin/load \
  -H "Content-Type: application/json" \
  -d '{"pluginPath": "DemoCalculator"}'

# Execute
curl -X POST http://localhost:5000/api/plugin/execute \
  -H "Content-Type: application/json" \
  -d '{
    "command": "DemoCalculator.add",
    "payload": {"a": 10, "b": 5}
  }'

Các Commands Mẫu

Command Description Example Payload
add Cộng hai số {"a": 10, "b": 5}
subtract Trừ hai số {"a": 10, "b": 3}
multiply Nhân hai số {"a": 4, "b": 5}
divide Chia hai số {"a": 20, "b": 4}
power Lũy thừa {"base": 2, "exponent": 3}
sqrt Căn bậc hai {"value": 16}
factorial Giai thừa {"n": 5}

🤝 Đóng Góp

  1. Fork repository
  2. Create feature branch: git checkout -b feature/my-feature
  3. Commit changes: git commit -am 'Add my feature'
  4. Push to branch: git push origin feature/my-feature
  5. Submit Pull Request

📄 License

Proprietary License - Copyright © 2025 Thần Nông

Sử dụng thư viện này yêu cầu giấy phép từ Thần Nông.


📞 Support


🔗 Tài Liệu Liên Quan

  • Plugin Development Guide: Docs/PLUGIN_DEVELOPMENT.md
  • Schema Auto-Generation: Docs/SCHEMA_AUTO_GENERATION.md
  • Authorization Quick Example: Docs/AUTHORIZATION_QUICK_EXAMPLE.md
  • DiscoveryHostApi README: DiscoveryHostApi/README.md

Happy Plugin Development! 🚀

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.1 430 12/8/2025

Version 1.0.1:
     - Plugin lifecycle management (Load/Unload/Reload)
     - JsonLogic-based authorization
     - Auto schema generation from method signatures
     - Version-based DLL naming support
     - Hot reload without app restart
     - Isolated AssemblyLoadContext per plugin
     - Debug mode with breakpoint support