初始化本地仓库
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\adminapi\controller\plugin;
|
||||
|
||||
use app\adminapi\controller\Crud;
|
||||
use app\adminapi\middleware\AccessTokenMiddleware;
|
||||
use app\adminapi\middleware\OperationMiddleware;
|
||||
use app\adminapi\middleware\PermissionMiddleware;
|
||||
use app\adminapi\schema\request\plugin\ApplicationAuthFormRequest;
|
||||
use app\adminapi\schema\request\plugin\ApplicationAuthQueryRequest;
|
||||
use app\adminapi\schema\response\plugin\ApplicationAuthResponse;
|
||||
use app\adminapi\validate\plugin\ApplicationAuthValidate;
|
||||
use app\schema\request\BatchDeleteRequest;
|
||||
use app\schema\request\IdRequest;
|
||||
use app\service\admin\plugin\ApplicationAuthService;
|
||||
use core\tool\Json;
|
||||
use madong\swagger\annotation\response\PageResponse;
|
||||
use madong\swagger\annotation\response\SimpleResponse;
|
||||
use madong\swagger\attribute\Permission;
|
||||
use OpenApi\Attributes as OA;
|
||||
use support\annotation\Middleware;
|
||||
use support\Request;
|
||||
use WebmanTech\Swagger\DTO\SchemaConstants;
|
||||
|
||||
#[Middleware(AccessTokenMiddleware::class, PermissionMiddleware::class, OperationMiddleware::class)]
|
||||
final class ApplicationAuthController extends Crud
|
||||
{
|
||||
public function __construct(ApplicationAuthService $service, ApplicationAuthValidate $validate)
|
||||
{
|
||||
$this->service = $service;
|
||||
$this->validate = $validate;
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/plugin/application-auth',
|
||||
summary: '列表',
|
||||
tags: ['应用授权'],
|
||||
x: [SchemaConstants::X_SCHEMA_REQUEST => ApplicationAuthQueryRequest::class]
|
||||
)]
|
||||
#[Permission(code: 'plugin:application-auth:list')]
|
||||
#[PageResponse(schema: ApplicationAuthResponse::class, example: [])]
|
||||
public function index(Request $request): \support\Response
|
||||
{
|
||||
return parent::index($request);
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/plugin/application-auth/{id}',
|
||||
summary: '详情',
|
||||
tags: ['应用授权'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'plugin:application-auth:read')]
|
||||
#[SimpleResponse(schema: ApplicationAuthResponse::class, example: [])]
|
||||
public function show(Request $request): \support\Response
|
||||
{
|
||||
return parent::show($request);
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/plugin/application-auth',
|
||||
summary: '创建',
|
||||
tags: ['应用授权'],
|
||||
x: [SchemaConstants::X_SCHEMA_REQUEST => ApplicationAuthFormRequest::class]
|
||||
)]
|
||||
#[Permission(code: 'plugin:application-auth:create')]
|
||||
#[SimpleResponse(example: ['code' => 0, 'message' => 'success', 'data' => []])]
|
||||
public function store(Request $request): \support\Response
|
||||
{
|
||||
return parent::store($request);
|
||||
}
|
||||
|
||||
#[OA\Put(
|
||||
path: '/plugin/application-auth/{id}',
|
||||
summary: '更新',
|
||||
tags: ['应用授权'],
|
||||
x: [SchemaConstants::X_SCHEMA_REQUEST => ApplicationAuthFormRequest::class]
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: 'id',
|
||||
description: 'ID',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'string', example: '123456789012345678')
|
||||
)]
|
||||
#[Permission(code: 'plugin:application-auth:update')]
|
||||
#[SimpleResponse(example: ['code' => 0, 'message' => 'success', 'data' => []])]
|
||||
public function update(Request $request): \support\Response
|
||||
{
|
||||
return parent::update($request);
|
||||
}
|
||||
#[OA\Post(
|
||||
path: '/plugin/application-auth/export',
|
||||
summary: '导出',
|
||||
tags: ['应用授权']
|
||||
)]
|
||||
#[Permission(code: 'plugin:application-auth:list', description: '导出')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function export(Request $request): \support\Response
|
||||
{
|
||||
return parent::export($request);
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/plugin/application-auth/{id}',
|
||||
summary: '删除',
|
||||
tags: ['应用授权'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'plugin:application-auth:delete')]
|
||||
#[SimpleResponse(example: ['code' => 0, 'message' => 'success', 'data' => []])]
|
||||
public function destroy(Request $request): \support\Response
|
||||
{
|
||||
return parent::destroy($request);
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/plugin/application-auth',
|
||||
summary: '批量删除',
|
||||
tags: ['应用授权'],
|
||||
x: [SchemaConstants::X_SCHEMA_REQUEST => BatchDeleteRequest::class]
|
||||
)]
|
||||
#[Permission(code: 'plugin:application-auth:delete')]
|
||||
#[SimpleResponse(example: ['code' => 0, 'message' => 'success', 'data' => []])]
|
||||
public function batchDelete(Request $request): \support\Response
|
||||
{
|
||||
return parent::destroy($request);
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/plugin/application-auth/{id}/reset-secret',
|
||||
summary: '重置密钥',
|
||||
tags: ['应用授权'],
|
||||
x: [
|
||||
SchemaConstants::X_PROPERTY_IN => 'id',
|
||||
SchemaConstants::X_SCHEMA_REQUEST => IdRequest::class,
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'plugin:application-auth:reset-secret')]
|
||||
#[SimpleResponse(example: ['code' => 0, 'message' => 'success', 'data' => ['id' => '123456789012345678', 'secret_key' => 'SK...']])]
|
||||
public function resetSecret(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$id = $request->route->param('id');
|
||||
return Json::success('ok', $this->service->resetSecret($id));
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,482 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\controller\plugin;
|
||||
|
||||
use app\adminapi\controller\Base;
|
||||
use app\adminapi\middleware\AccessTokenMiddleware;
|
||||
use app\adminapi\middleware\OperationMiddleware;
|
||||
use app\adminapi\middleware\PermissionMiddleware;
|
||||
use app\service\core\plugin\PluginDownloadService;
|
||||
use app\service\core\plugin\PluginInstallService;
|
||||
use app\service\core\plugin\PluginRemoteService;
|
||||
use app\service\core\plugin\PluginUninstallService;
|
||||
use app\service\core\plugin\PluginService;
|
||||
use core\tool\Sse;
|
||||
use madong\swagger\annotation\response\PageResponse;
|
||||
use madong\swagger\attribute\Permission;
|
||||
use core\tool\Json;
|
||||
use madong\swagger\annotation\response\SimpleResponse;
|
||||
use OpenApi\Attributes as OA;
|
||||
use support\annotation\Middleware;
|
||||
use support\Container;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
|
||||
#[Middleware(AccessTokenMiddleware::class, PermissionMiddleware::class, OperationMiddleware::class)]
|
||||
final class PluginController extends Base
|
||||
{
|
||||
|
||||
#[OA\Get(
|
||||
path: '/plugin',
|
||||
summary: '列表',
|
||||
tags: ['模块市场'],
|
||||
parameters: [
|
||||
new OA\Parameter(
|
||||
name: 'page',
|
||||
description: '页码',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: new OA\Schema(type: 'integer', default: 1)
|
||||
),
|
||||
new OA\Parameter(
|
||||
name: 'limit',
|
||||
description: '每页数量',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: new OA\Schema(type: 'integer', default: 10)
|
||||
),
|
||||
new OA\Parameter(
|
||||
name: 'type',
|
||||
description: '模块类型(module: 系统模块, plugin: 插件)',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: new OA\Schema(type: 'string', enum: ['module', 'plugin'])
|
||||
),
|
||||
new OA\Parameter(
|
||||
name: 'status',
|
||||
description: '状态(0:禁用,1:启用)',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: new OA\Schema(type: 'integer', enum: [0, 1])
|
||||
),
|
||||
new OA\Parameter(
|
||||
name: 'keyword',
|
||||
description: '搜索关键词',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: new OA\Schema(type: 'string')
|
||||
),
|
||||
new OA\Parameter(
|
||||
name: 'installed',
|
||||
description: '是否已安装(0:未安装,1:已安装)',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: new OA\Schema(type: 'integer', enum: [0, 1])
|
||||
),
|
||||
],
|
||||
)]
|
||||
#[PageResponse(schema: [], example: [[
|
||||
'id' => 3,
|
||||
'code' => 'official',
|
||||
'name' => 'official',
|
||||
'type' => 'madong',
|
||||
'version' => '1.0.0',
|
||||
'status' => 1,
|
||||
'description' => '官方应用插件',
|
||||
'detail_description' => '',
|
||||
'author' => 'Madong',
|
||||
'cover' => '',
|
||||
'poster' => '',
|
||||
'price' => 0,
|
||||
'downloads' => 0,
|
||||
'rating' => 0,
|
||||
'created_at' => '2026-03-19 16:26:19',
|
||||
'updated_at' => '2026-03-19 16:26:19',
|
||||
'update_time' => '',
|
||||
'update_logs' => [],
|
||||
'category' => null,
|
||||
'category_name' => '',
|
||||
'tags' => [],
|
||||
'is_new' => 0,
|
||||
'is_hot' => 0,
|
||||
'purchased' => 0,
|
||||
'installed' => 0,
|
||||
'manual_uninstall' => 0,
|
||||
'composer_dependencies' => [],
|
||||
'npm_dependencies' => [],
|
||||
'is_installed' => 1,
|
||||
'installed_version' => '1.0.0',
|
||||
'has_update' => 0,
|
||||
'is_purchased' => 1,
|
||||
'is_downloaded' => 1,
|
||||
'can_uninstall' => 1,
|
||||
'can_download' => 1,
|
||||
'undeletable' => 1,
|
||||
'version_info' => [
|
||||
'remote' => [
|
||||
'version' => '1.0.0',
|
||||
'release_date' => '2026-03-19 16:26:19',
|
||||
],
|
||||
'local' => [
|
||||
'version' => '1.0.0',
|
||||
'install_date' => '2026-03-19 16:26:19',
|
||||
],
|
||||
'comparison' => [
|
||||
'needs_update' => 0,
|
||||
'is_latest' => 1,
|
||||
'version_difference' => null,
|
||||
],
|
||||
],
|
||||
]])]
|
||||
#[Permission(code: 'plugin:module:list')]
|
||||
public function index(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$category = $request->input('category', null);
|
||||
$pageInput = $request->input('current', $request->input('page', 1));
|
||||
$limitInput = $request->input('size', $request->input('limit', 15));
|
||||
$page = max(1, (int)$pageInput);
|
||||
$limit = max(1, (int)$limitInput);
|
||||
$type = $request->input('type');
|
||||
$keyword = $request->input('keyword');
|
||||
/** @var PluginService $service */
|
||||
$service = Container::make(PluginService::class);
|
||||
$result = $service->getList($category, $type, $keyword, $page, $limit);
|
||||
return Json::success('common.operation.success', $result);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage(), []);
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/plugin/{name}/download',
|
||||
summary: '下载模块',
|
||||
tags: ['模块市场'],
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: 'name',
|
||||
description: '模块标识',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'string')
|
||||
)]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function download(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$pluginCode = $request->route->param('name');
|
||||
|
||||
// 1. 获取下载链接
|
||||
$remoteService = Container::make(PluginRemoteService::class);
|
||||
$authCode = config('madong.auth_code');
|
||||
$secretKey = config('madong.auth_secret');
|
||||
|
||||
$downloadInfo = $remoteService->getRemoteDownloadService($authCode, $secretKey, $pluginCode, 'latest');
|
||||
if (empty($downloadInfo['url'])) {
|
||||
return Json::fail('获取下载链接失败');
|
||||
}
|
||||
|
||||
// 2. 下载并解压插件
|
||||
$downloadService = Container::make(PluginDownloadService::class);
|
||||
$result = $downloadService->downloadAndExtract($pluginCode, $downloadInfo['url']);
|
||||
|
||||
return Json::success('下载成功', $result);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/plugin/detail/{name}',
|
||||
summary: '获取模块详情',
|
||||
tags: ['模块市场'],
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: 'name',
|
||||
description: '模块标识',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'string')
|
||||
)]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function detail(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$moduleName = $request->route->param('name');
|
||||
/** @var PluginService $service */
|
||||
$service = Container::make(PluginService::class);
|
||||
$result = $service->getList(null, null, $moduleName);
|
||||
return Json::success('common.operation.success', $result['items'][0]);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage(), []);
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/plugin/{name}/upgrade-logs',
|
||||
summary: '获取模块升级日志',
|
||||
tags: ['模块市场'],
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: 'name',
|
||||
description: '模块标识',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'string')
|
||||
)]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function upgradeLogs(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$moduleName = $request->route->param('name');
|
||||
/** @var PluginService $service */
|
||||
$service = Container::make(PluginService::class);
|
||||
$result = $service->getUpgradeLogs($moduleName);
|
||||
|
||||
return Json::success('common.operation.success', $result);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage(), []);
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/plugin/check-environment',
|
||||
summary: '环境检测',
|
||||
tags: ['模块市场'],
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: 'name',
|
||||
description: '模块标识',
|
||||
in: 'query',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'string')
|
||||
)]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function checkEnvironment(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$name = $request->input('name');
|
||||
/** @var PluginInstallService $service */
|
||||
$service = Container::make(PluginInstallService::class);
|
||||
$result = $service->installCheck($name);
|
||||
|
||||
// 提取路径列表和状态
|
||||
$paths = [];
|
||||
foreach ($result['checks'] as $check) {
|
||||
if (isset($check['path'])) {
|
||||
// 只展示从madong开始的路径
|
||||
$path = $check['path'];
|
||||
$madongPos = strpos(strtolower($path), 'madong');
|
||||
if ($madongPos !== false) {
|
||||
// 截取从madong开始的路径
|
||||
$path = substr($path, $madongPos);
|
||||
// 确保路径格式正确,madong后面应该有目录分隔符
|
||||
if (substr($path, 6, 1) !== DIRECTORY_SEPARATOR) {
|
||||
$path = 'madong' . DIRECTORY_SEPARATOR . substr($path, 6);
|
||||
}
|
||||
}
|
||||
$paths[] = [
|
||||
'path' => $path,
|
||||
'requirement' => $check['permission_type'] ?? 'readable',
|
||||
'status' => $check['status'] === 'success' ? 'success' : 'error',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return Json::success('common.operation.success', [
|
||||
'paths' => $paths,
|
||||
'overall_status' => $result['overall_status'],
|
||||
'message' => $result['message'],
|
||||
]);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage(), []);
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/plugin/install',
|
||||
summary: '插件安装',
|
||||
tags: ['模块市场'],
|
||||
)]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function install(Request $request): void
|
||||
{
|
||||
$connection = $request->connection;
|
||||
$code = $request->input('code');
|
||||
$mode = $request->input('mode', 'local');
|
||||
|
||||
// 验证参数
|
||||
if (empty($code)) {
|
||||
$connection->send(new Response(200, [
|
||||
'Content-Type' => 'text/event-stream',
|
||||
'Cache-Control' => 'no-cache',
|
||||
'Connection' => 'keep-alive',
|
||||
'Access-Control-Allow-Origin' => '*',
|
||||
'Access-Control-Allow-Credentials' => 'true',
|
||||
'Access-Control-Expose-Headers' => 'Content-Type',
|
||||
], "\r\n"));
|
||||
$connection->send(\core\tool\Sse::error('参数错误:缺少code参数'));
|
||||
return;
|
||||
}
|
||||
|
||||
// 发送SSE响应头
|
||||
$connection->send(new Response(200, [
|
||||
'Content-Type' => 'text/event-stream',
|
||||
'Cache-Control' => 'no-cache',
|
||||
'Connection' => 'keep-alive',
|
||||
'Access-Control-Allow-Origin' => '*',
|
||||
'Access-Control-Allow-Credentials' => 'true',
|
||||
'Access-Control-Expose-Headers' => 'Content-Type',
|
||||
], "\r\n"));
|
||||
|
||||
try {
|
||||
// 发送开始安装消息
|
||||
$connection->send(Sse::progress('开始安装插件 ' . $code, 0));
|
||||
|
||||
// 调用安装服务
|
||||
$generator = (new PluginInstallService())->install($code, $mode);
|
||||
foreach ($generator as $chunk) {
|
||||
$connection->send($chunk);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// 发送错误消息
|
||||
$connection->send(Sse::error('安装失败:' . $e->getMessage()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/plugin/uninstall',
|
||||
summary: '插件卸载(SSE流式)',
|
||||
tags: ['模块市场'],
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: 'code',
|
||||
description: '插件编码',
|
||||
in: 'query',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'string')
|
||||
)]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function uninstall(Request $request): void
|
||||
{
|
||||
$connection = $request->connection;
|
||||
$code = $request->input('code');
|
||||
// 验证参数
|
||||
if (empty($code)) {
|
||||
$connection->send(new Response(200, [
|
||||
'Content-Type' => 'text/event-stream',
|
||||
'Cache-Control' => 'no-cache',
|
||||
'Connection' => 'keep-alive',
|
||||
'Access-Control-Allow-Origin' => '*',
|
||||
'Access-Control-Allow-Credentials' => 'true',
|
||||
'Access-Control-Expose-Headers' => 'Content-Type',
|
||||
], "\r\n"));
|
||||
$connection->send(Sse::error('参数错误:缺少code参数'));
|
||||
return;
|
||||
}
|
||||
|
||||
// 发送SSE响应头
|
||||
$connection->send(new Response(200, [
|
||||
'Content-Type' => 'text/event-stream',
|
||||
'Cache-Control' => 'no-cache',
|
||||
'Connection' => 'keep-alive',
|
||||
'Access-Control-Allow-Origin' => '*',
|
||||
'Access-Control-Allow-Credentials' => 'true',
|
||||
'Access-Control-Expose-Headers' => 'Content-Type',
|
||||
], "\r\n"));
|
||||
|
||||
try {
|
||||
// 调用卸载服务(统一入口)
|
||||
$generator = (new PluginUninstallService())->uninstall($code);
|
||||
foreach ($generator as $chunk) {
|
||||
$connection->send($chunk);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// 发送错误消息
|
||||
$connection->send(Sse::error('卸载失败:' . $e->getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Delete(
|
||||
path: '/plugin/{name}',
|
||||
summary: '插件删除',
|
||||
tags: ['模块市场'],
|
||||
)]
|
||||
#[OA\Parameter(
|
||||
name: 'name',
|
||||
description: '插件名称',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'string')
|
||||
)]
|
||||
#[Permission(code: 'plugin:delete')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function destroy(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$name = $request->route->param('name');
|
||||
if (empty($name)) {
|
||||
return Json::fail('参数错误:缺少name参数', []);
|
||||
}
|
||||
|
||||
/** @var PluginUninstallService $service */
|
||||
$service = Container::get(PluginUninstallService::class);
|
||||
$service->delete($name);
|
||||
return Json::success('common.operation.delete.success', []);
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/plugin/select',
|
||||
summary: '获取已安装插件列表(用于下拉框)',
|
||||
tags: ['模块市场'],
|
||||
)]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function select(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
/** @var PluginService $service */
|
||||
$service = Container::make(PluginService::class);
|
||||
|
||||
// 获取本地插件列表(包括已安装和未安装的)
|
||||
$localModules = $service->getLocalModules();
|
||||
|
||||
// 过滤出已安装的插件
|
||||
$installedPlugins = array_filter($localModules, function ($module) {
|
||||
return $module['is_installed'] === true && $module['status'] == 1;
|
||||
});
|
||||
|
||||
// 构建下拉框格式数据
|
||||
$selectItems = [];
|
||||
foreach ($installedPlugins as $plugin) {
|
||||
$selectItems[] = [
|
||||
'value' => $plugin['name'],
|
||||
'label' => $plugin['name'] . ' - ' . $plugin['description'],
|
||||
'name' => $plugin['name'],
|
||||
'description' => $plugin['description'],
|
||||
'version' => $plugin['version'],
|
||||
'type' => $plugin['type'] ?? 'plugin',
|
||||
];
|
||||
}
|
||||
|
||||
return Json::success('common.operation.success', array_values($selectItems));
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage(), []);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,301 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\controller\plugin;
|
||||
|
||||
use app\adminapi\controller\Crud;
|
||||
use app\adminapi\middleware\AccessTokenMiddleware;
|
||||
use app\adminapi\middleware\OperationMiddleware;
|
||||
use app\adminapi\middleware\PermissionMiddleware;
|
||||
use app\adminapi\validate\plugin\PluginValidate;
|
||||
use app\service\admin\plugin\PluginDevelopService;
|
||||
use madong\swagger\annotation\response\DataResponse;
|
||||
use madong\swagger\annotation\response\PageResponse;
|
||||
use madong\swagger\annotation\response\SimpleResponse;
|
||||
use madong\swagger\attribute\Permission;
|
||||
use core\tool\Json;
|
||||
use OpenApi\Attributes as OA;
|
||||
use support\annotation\Middleware;
|
||||
use support\Request;
|
||||
|
||||
#[Middleware(AccessTokenMiddleware::class, PermissionMiddleware::class, OperationMiddleware::class)]
|
||||
final class PluginDevelopController extends Crud
|
||||
{
|
||||
|
||||
public function __construct(PluginDevelopService $service, PluginValidate $validate)
|
||||
{
|
||||
$this->service = $service;
|
||||
$this->validate = $validate;
|
||||
}
|
||||
|
||||
/**
|
||||
* 插件列表
|
||||
* 扫描插件目录,获取type为madong:前缀的插件,同步到数据库并返回列表
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return \support\Response
|
||||
*/
|
||||
#[OA\Get(
|
||||
path: '/plugin/develop',
|
||||
summary: '插件列表',
|
||||
tags: ['插件开发'],
|
||||
parameters: [
|
||||
new OA\Parameter(
|
||||
name: 'page',
|
||||
description: '页码',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: new OA\Schema(type: 'integer', default: 1)
|
||||
),
|
||||
new OA\Parameter(
|
||||
name: 'limit',
|
||||
description: '每页数量',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: new OA\Schema(type: 'integer', default: 15)
|
||||
),
|
||||
new OA\Parameter(
|
||||
name: 'title',
|
||||
description: '插件标题',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: new OA\Schema(type: 'string')
|
||||
),
|
||||
new OA\Parameter(
|
||||
name: 'author',
|
||||
description: '作者',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: new OA\Schema(type: 'string')
|
||||
),
|
||||
new OA\Parameter(
|
||||
name: 'status',
|
||||
description: '状态',
|
||||
in: 'query',
|
||||
required: false,
|
||||
schema: new OA\Schema(type: 'integer')
|
||||
),
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'plugin:develop:list')]
|
||||
#[PageResponse(schema: [], example: [[
|
||||
"id" => "2032735331152958596",
|
||||
"name" => "官网管理",
|
||||
"icon" => null,
|
||||
"code" => "official",
|
||||
"description" => "官网管理插件,用于搭建和管理企业官方网站",
|
||||
"enabled" => 1,
|
||||
"author" => "Mr.April",
|
||||
"version" => "1.0.0",
|
||||
"cover" => null,
|
||||
"type" => "madong:plugin",
|
||||
"support_app" => "admin",
|
||||
"installed_at" => null,
|
||||
"created_at" => 1773476844,
|
||||
"updated_at" => 1773476844,
|
||||
"variables" => null,
|
||||
"created_date" => "2026-03-14 16:27:24",
|
||||
"updated_date" => "2026-03-14 16:27:24",
|
||||
]])]
|
||||
public function index(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
// 使用标准模式:通过 selectInput 处理查询参数
|
||||
[$where, , $limit, , , $page] = $this->selectInput($request);
|
||||
|
||||
// 调用服务层获取列表数据
|
||||
$result = $this->service->getList($where, $page, $limit);
|
||||
|
||||
// 使用标准格式返回
|
||||
return Json::success($result);
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/plugin/develop/{id}',
|
||||
summary: '插件详情',
|
||||
tags: ['插件开发'],
|
||||
parameters: [
|
||||
new OA\Parameter(
|
||||
name: 'id',
|
||||
description: '插件ID',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'integer')
|
||||
),
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'plugin:develop:read')]
|
||||
#[DataResponse(schema: [], example: [
|
||||
"id" => "2032735331152958596",
|
||||
"title" => "官网管理",
|
||||
"icon" => null,
|
||||
"key" => "official",
|
||||
"desc" => "官网管理插件,用于搭建和管理企业官方网站",
|
||||
"status" => 1,
|
||||
"author" => "Mr.April",
|
||||
"version" => "1.0.0",
|
||||
"cover" => null,
|
||||
"type" => "madong:plugin",
|
||||
"support_app" => "admin",
|
||||
"installed_at" => null,
|
||||
"created_at" => 1773476844,
|
||||
"updated_at" => 1773476844,
|
||||
"variables" => null,
|
||||
"created_date" => "2026-03-14 16:27:24",
|
||||
"updated_date" => "2026-03-14 16:27:24",
|
||||
])]
|
||||
public function show(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$id = $request->route->param('id');
|
||||
$plugin = $this->service->read($id);
|
||||
if (!$plugin) {
|
||||
return Json::fail('插件不存在');
|
||||
}
|
||||
return Json::success($plugin->toArray());
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建插件
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return \support\Response
|
||||
* @throws \Throwable
|
||||
*/
|
||||
#[OA\Post(
|
||||
path: '/plugin/develop',
|
||||
summary: '创建插件',
|
||||
requestBody: new OA\RequestBody(
|
||||
required: true,
|
||||
content: new OA\JsonContent(
|
||||
required: ['title', 'key'],
|
||||
properties: [
|
||||
new OA\Property(property: 'title', description: '插件标题', type: 'string'),
|
||||
new OA\Property(property: 'key', description: '插件标识(唯一值)', type: 'string'),
|
||||
new OA\Property(property: 'desc', description: '插件描述', type: 'string'),
|
||||
new OA\Property(property: 'author', description: '作者', type: 'string'),
|
||||
new OA\Property(property: 'version', description: '版本', type: 'string', default: '1.0.0'),
|
||||
new OA\Property(property: 'type', description: '类型', type: 'string', default: 'madong:plugin'),
|
||||
new OA\Property(property: 'icon', description: '图标(base64)', type: 'string'),
|
||||
new OA\Property(property: 'cover', description: '封面(base64)', type: 'string'),
|
||||
new OA\Property(property: 'frontend_type', description: '前端类型', type: 'string', default: 'admin'),
|
||||
]
|
||||
)
|
||||
),
|
||||
tags: ['插件开发']
|
||||
)]
|
||||
#[Permission(code: 'plugin:develop:create')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function store(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$this->validate->scene('store')->check($request->all());
|
||||
$this->service->store($request->all());
|
||||
return Json::success();
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 打包插件
|
||||
* 将后端、后台前端、前台前端复制到后端server/plugin/xxx/resource目录,并打包为zip
|
||||
*
|
||||
* @param Request $request
|
||||
*
|
||||
* @return \support\Response
|
||||
*/
|
||||
#[OA\Post(
|
||||
path: '/plugin/develop/build',
|
||||
summary: '打包插件',
|
||||
requestBody: new OA\RequestBody(
|
||||
required: true,
|
||||
content: new OA\JsonContent(
|
||||
required: ['plugin_key'],
|
||||
properties: [
|
||||
new OA\Property(property: 'plugin_key', description: '插件标识', type: 'string'),
|
||||
]
|
||||
)
|
||||
),
|
||||
tags: ['插件开发']
|
||||
)]
|
||||
#[Permission(code: 'plugin:develop:build')]
|
||||
#[SimpleResponse(schema: [], example: [
|
||||
"plugin_key" => "test",
|
||||
"zip_path" => "D:\\MyProject\\private\\madong/server/runtime/adminapi/test.zip",
|
||||
"has_frontend" => false,
|
||||
])]
|
||||
public function build(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$pluginKey = $request->input('plugin_key');
|
||||
if (empty($pluginKey)) {
|
||||
return Json::fail('插件标识不能为空');
|
||||
}
|
||||
|
||||
$result = $this->service->buildPlugin($pluginKey);
|
||||
return Json::success('插件打包成功',$result);
|
||||
} catch (\Exception $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
#[OA\Put(
|
||||
path: '/plugin/develop/{id}',
|
||||
summary: '编辑插件',
|
||||
requestBody: new OA\RequestBody(
|
||||
required: true,
|
||||
content: new OA\JsonContent(
|
||||
properties: [
|
||||
new OA\Property(property: 'title', description: '插件标题', type: 'string'),
|
||||
new OA\Property(property: 'desc', description: '插件描述', type: 'string'),
|
||||
new OA\Property(property: 'author', description: '作者', type: 'string'),
|
||||
new OA\Property(property: 'version', description: '版本', type: 'string'),
|
||||
new OA\Property(property: 'icon', description: '图标(base64)', type: 'string'),
|
||||
new OA\Property(property: 'cover', description: '封面(base64)', type: 'string'),
|
||||
]
|
||||
)
|
||||
),
|
||||
tags: ['插件开发'],
|
||||
parameters: [
|
||||
new OA\Parameter(
|
||||
name: 'id',
|
||||
description: '插件ID',
|
||||
in: 'path',
|
||||
required: true,
|
||||
schema: new OA\Schema(type: 'integer')
|
||||
),
|
||||
]
|
||||
)]
|
||||
#[Permission(code: 'plugin:develop:update')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function update(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$id = $request->route->param('id');
|
||||
$this->service->update($id, $request->all());
|
||||
return Json::success('更新成功');
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\adminapi\controller\plugin;
|
||||
|
||||
use app\adminapi\controller\Base;
|
||||
use app\adminapi\middleware\AccessTokenMiddleware;
|
||||
use app\adminapi\middleware\OperationMiddleware;
|
||||
use app\adminapi\middleware\PermissionMiddleware;
|
||||
use app\service\core\plugin\PluginRemoteService;
|
||||
use core\tool\Json;
|
||||
use madong\swagger\annotation\auth\AllowAnonymous;
|
||||
use madong\swagger\annotation\response\SimpleResponse;
|
||||
use madong\swagger\attribute\Permission;
|
||||
use OpenApi\Attributes as OA;
|
||||
use support\annotation\Middleware;
|
||||
use support\Request;
|
||||
|
||||
#[Middleware(AccessTokenMiddleware::class, PermissionMiddleware::class, OperationMiddleware::class)]
|
||||
final class PluginMadongAuthController extends Base
|
||||
{
|
||||
|
||||
public function __construct(PluginRemoteService $service)
|
||||
{
|
||||
$this->service = $service;
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/madong/auth-info',
|
||||
summary: '获取插件授权信息',
|
||||
tags: ['应用授权']
|
||||
)]
|
||||
#[Permission('madong:delegation:read')]
|
||||
#[AllowAnonymous(requireToken: true, requirePermission: true)]
|
||||
#[SimpleResponse(example: '{"code": 0,"msg": "ok","data": {"company_name": "-","domain": "-","auth_code": "202601***********888888"}}')]
|
||||
public function read(Request $request): \support\Response
|
||||
{
|
||||
|
||||
$data = [
|
||||
'company_name' => '-',
|
||||
'domain' => '-',
|
||||
'auth_code' => config('madong.auth_code'),
|
||||
];
|
||||
return Json::success($data);
|
||||
}
|
||||
|
||||
#[OA\Post(
|
||||
path: '/madong/auth-info',
|
||||
summary: '设置插件授权信息',
|
||||
tags: ['应用授权']
|
||||
)]
|
||||
#[Permission('madong:delegation:setting')]
|
||||
#[SimpleResponse(schema: [], example: [])]
|
||||
public function store(Request $request): \support\Response
|
||||
{
|
||||
try {
|
||||
$param = $request->all();
|
||||
if (empty($param['auth_code'])) {
|
||||
throw new \Exception('auth_code 不能为空');
|
||||
}
|
||||
if (empty($param['auth_secret'])) {
|
||||
throw new \Exception('auth_secret 不能为空');
|
||||
}
|
||||
$this->service->verifyRemoteAuthorization($param['auth_code'],$param['auth_secret']);
|
||||
//追加到系统配置
|
||||
$this->updateMadongConfig($param['auth_code'], $param['auth_secret']);
|
||||
|
||||
return Json::success('授权设置成功');
|
||||
} catch (\Throwable $e) {
|
||||
return Json::fail($e->getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新server/config/madong.php文件中的配置
|
||||
*
|
||||
* @param string $authCode 授权码
|
||||
* @param string $authSecret 授权密钥
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function updateMadongConfig(string $authCode, string $authSecret): void
|
||||
{
|
||||
$configPath = base_path() . '/config/madong.php';
|
||||
if (!file_exists($configPath)) {
|
||||
throw new \Exception('madong.php配置文件不存在');
|
||||
}
|
||||
|
||||
$configContent = file_get_contents($configPath);
|
||||
|
||||
// 定义需要替换的参数映射
|
||||
$replacements = [
|
||||
'auth_code' => $authCode,
|
||||
'auth_secret' => $authSecret,
|
||||
];
|
||||
|
||||
foreach ($replacements as $key => $value) {
|
||||
$pattern = '/\'' . $key . '\'\s*=>\s*env\(\'madong\..*?\'\s*,\s*\'(.*?)\'\)/';
|
||||
$replacement = "'$key' => env('madong." . ($key === 'auth_code' ? 'code' : 'secret') . "', '$value')";
|
||||
$configContent = preg_replace($pattern, $replacement, $configContent);
|
||||
}
|
||||
|
||||
file_put_contents($configPath, $configContent);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user