Files
biss 05683e502a 第二阶段已完成:统一插件平台已从“内置功能开关”扩展为可安装、签名校验、版本激活和回滚的插件系统。
主要成果:
- 新增独立插件 SDK:[PluginContracts.cs (line 11)](E:/jiaowu/src/Jiaowu.Plugin.Abstractions/PluginContracts.cs:11)
- 支持 RSA-PSS/SHA-256 签名插件包、Host API 兼容性和 ZIP 安全检查:[PluginPackageValidator.cs (line 14)](E:/jiaowu/src/Jiaowu.Api/Infrastructure/Plugins/PluginPackageValidator.cs:14)
- 支持暂存、待激活、重启装载、失败记录、版本回滚:[PluginPackageService.cs (line 32)](E:/jiaowu/src/Jiaowu.Api/Infrastructure/Plugins/PluginPackageService.cs:32)
- 插件可注册控制器、服务、数据库迁移、后台任务和事件处理器
- 外部插件 API 统一使用 /api/plugin-extensions/{pluginId},并受启用状态中间件控制
- 插件中心新增 ZIP/SIG 上传、状态展示、激活、回滚和删除功能:[PluginsView.vue (line 212)](E:/jiaowu/web/src/views/PluginsView.vue:212)
- 提供可运行示例插件:[SamplePlugin.cs (line 10)](E:/jiaowu/samples/Jiaowu.SamplePlugin/SamplePlugin.cs:10)
- 提供打包签名脚本:[package-plugin.ps1](E:/jiaowu/scripts/package-plugin.ps1)
- 完整开发文档:[plugin-sdk.md (line 1)](E:/jiaowu/docs/plugin-sdk.md:1)
- 系统版本已调整为 3.0.0-beta1
2026-10-01 10:22:46 +08:00

78 lines
3.1 KiB
PowerShell

param(
[Parameter(Mandatory = $true)]
[string] $Project,
[Parameter(Mandatory = $true)]
[string] $Manifest,
[Parameter(Mandatory = $true)]
[string] $PrivateKey,
[string] $OutputDirectory = '.artifacts/plugins'
)
$ErrorActionPreference = 'Stop'
$repositoryRoot = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..'))
$projectPath = [IO.Path]::GetFullPath($Project, (Get-Location).Path)
$manifestPath = [IO.Path]::GetFullPath($Manifest, (Get-Location).Path)
$privateKeyPath = [IO.Path]::GetFullPath($PrivateKey, (Get-Location).Path)
$outputRoot = [IO.Path]::GetFullPath($OutputDirectory, $repositoryRoot)
foreach ($requiredPath in @($projectPath, $manifestPath, $privateKeyPath)) {
if (-not (Test-Path -LiteralPath $requiredPath)) {
throw "Required path does not exist: $requiredPath"
}
}
$manifestObject = Get-Content -Raw -LiteralPath $manifestPath | ConvertFrom-Json
if ([string]::IsNullOrWhiteSpace($manifestObject.id) -or
[string]::IsNullOrWhiteSpace($manifestObject.version)) {
throw 'plugin.json must contain id and version.'
}
if ($manifestObject.id -notmatch '^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)+$' -or
$manifestObject.version -notmatch '^\d+\.\d+\.\d+(?:-[0-9A-Za-z]+(?:[.-][0-9A-Za-z]+)*)?$') {
throw 'plugin.json contains an unsafe id or invalid semantic version.'
}
$temporaryRoot = Join-Path ([IO.Path]::GetTempPath()) ("jiaowu-plugin-" + [Guid]::NewGuid().ToString('N'))
$publishRoot = Join-Path $temporaryRoot 'publish'
$packageRoot = Join-Path $temporaryRoot 'package'
$backendRoot = Join-Path $packageRoot 'backend'
try {
New-Item -ItemType Directory -Path $publishRoot, $backendRoot -Force | Out-Null
$dotnet = (Get-Command dotnet).Source
$publishArgs = @('publish', $projectPath, '-c', 'Release', '-o', $publishRoot)
& $dotnet @publishArgs
if ($LASTEXITCODE -ne 0) { throw "dotnet publish failed with exit code $LASTEXITCODE" }
Copy-Item -LiteralPath $manifestPath -Destination (Join-Path $packageRoot 'plugin.json')
Copy-Item -Path (Join-Path $publishRoot '*') -Destination $backendRoot -Recurse -Force
New-Item -ItemType Directory -Path $outputRoot -Force | Out-Null
$baseName = "$($manifestObject.id)-$($manifestObject.version)"
$zipPath = Join-Path $outputRoot "$baseName.zip"
$signaturePath = Join-Path $outputRoot "$baseName.sig"
if (Test-Path -LiteralPath $zipPath) { Remove-Item -LiteralPath $zipPath -Force }
Compress-Archive -Path (Join-Path $packageRoot '*') -DestinationPath $zipPath
$rsa = [Security.Cryptography.RSA]::Create()
try {
$rsa.ImportFromPem((Get-Content -Raw -LiteralPath $privateKeyPath))
$content = [IO.File]::ReadAllBytes($zipPath)
$signature = $rsa.SignData(
$content,
[Security.Cryptography.HashAlgorithmName]::SHA256,
[Security.Cryptography.RSASignaturePadding]::Pss)
[IO.File]::WriteAllBytes($signaturePath, $signature)
}
finally {
$rsa.Dispose()
}
Write-Output $zipPath
Write-Output $signaturePath
}
finally {
if (Test-Path -LiteralPath $temporaryRoot) {
Remove-Item -LiteralPath $temporaryRoot -Recurse -Force
}
}