第二阶段已完成:统一插件平台已从“内置功能开关”扩展为可安装、签名校验、版本激活和回滚的插件系统。
主要成果:
- 新增独立插件 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
This commit is contained in:
1 parent
7e3f138875
commit
05683e502a
35 files changed
+11247
-24
No files matched your search
@@ -0,0 +1,77 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user