暂存
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
namespace app\service\AdminConsole;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class MaintenanceScriptService
|
||||
{
|
||||
private const ARG_PATTERN = '/^--[A-Za-z0-9_]+(?:=[A-Za-z0-9._:@\/-]+)?$/';
|
||||
|
||||
public function list(): array
|
||||
{
|
||||
$docs = new AdminDocService();
|
||||
$items = [];
|
||||
foreach ($this->definitions() as $definition) {
|
||||
$item = $definition;
|
||||
try {
|
||||
$doc = $docs->readScriptDoc($definition['doc_name']);
|
||||
$item['doc_title'] = $doc['title'];
|
||||
$item['doc_html'] = $doc['html'];
|
||||
$item['doc_content'] = $doc['content'];
|
||||
} catch (RuntimeException) {
|
||||
$item['doc_title'] = null;
|
||||
$item['doc_html'] = null;
|
||||
$item['doc_content'] = null;
|
||||
}
|
||||
$items[] = $item;
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
public function describe(string $name): array
|
||||
{
|
||||
$definitions = $this->definitions();
|
||||
if (!isset($definitions[$name])) {
|
||||
throw new RuntimeException('Script is not allowed.');
|
||||
}
|
||||
|
||||
foreach ($this->list() as $item) {
|
||||
if ($item['name'] === $name) {
|
||||
return $item;
|
||||
}
|
||||
}
|
||||
|
||||
throw new RuntimeException('Script metadata not found.');
|
||||
}
|
||||
|
||||
public function run(string $name, array $args = []): array
|
||||
{
|
||||
$definitions = $this->definitions();
|
||||
if (!isset($definitions[$name])) {
|
||||
throw new RuntimeException('Script is not allowed.');
|
||||
}
|
||||
|
||||
$script = $definitions[$name];
|
||||
$scriptPath = base_path('scripts/' . $script['file']);
|
||||
if (!is_file($scriptPath)) {
|
||||
throw new RuntimeException('Script file not found.');
|
||||
}
|
||||
|
||||
$safeArgs = [];
|
||||
foreach ($args as $arg) {
|
||||
$arg = trim((string) $arg);
|
||||
if ($arg === '') {
|
||||
continue;
|
||||
}
|
||||
if (!preg_match(self::ARG_PATTERN, $arg)) {
|
||||
throw new RuntimeException('Only --key=value style arguments are allowed.');
|
||||
}
|
||||
$safeArgs[] = $arg;
|
||||
}
|
||||
|
||||
$command = array_merge([PHP_BINARY, $scriptPath], $safeArgs);
|
||||
$descriptors = [
|
||||
0 => ['pipe', 'r'],
|
||||
1 => ['pipe', 'w'],
|
||||
2 => ['pipe', 'w'],
|
||||
];
|
||||
|
||||
$process = proc_open($command, $descriptors, $pipes, base_path());
|
||||
if (!is_resource($process)) {
|
||||
throw new RuntimeException('Failed to start script process.');
|
||||
}
|
||||
|
||||
fclose($pipes[0]);
|
||||
$stdout = (string) stream_get_contents($pipes[1]);
|
||||
fclose($pipes[1]);
|
||||
$stderr = (string) stream_get_contents($pipes[2]);
|
||||
fclose($pipes[2]);
|
||||
$exitCode = proc_close($process);
|
||||
|
||||
return [
|
||||
'script_name' => $name,
|
||||
'command' => array_merge(['php', 'scripts/' . $script['file']], $safeArgs),
|
||||
'exit_code' => $exitCode,
|
||||
'stdout' => $stdout,
|
||||
'stderr' => $stderr,
|
||||
'ok' => $exitCode === 0,
|
||||
];
|
||||
}
|
||||
|
||||
private function definitions(): array
|
||||
{
|
||||
return [
|
||||
'setup_database' => [
|
||||
'name' => 'setup_database',
|
||||
'file' => 'setup_database.php',
|
||||
'label' => '初始化数据库',
|
||||
'description' => '创建或补齐 archives、chunks 相关表结构与索引。',
|
||||
'doc_name' => 'setup_database.md',
|
||||
'args_hint' => '无参数',
|
||||
],
|
||||
'setup_opensearch' => [
|
||||
'name' => 'setup_opensearch',
|
||||
'file' => 'setup_opensearch.php',
|
||||
'label' => '初始化 OpenSearch',
|
||||
'description' => '创建或补齐 proofdb_chunks 索引与 mapping。',
|
||||
'doc_name' => 'setup_opensearch.md',
|
||||
'args_hint' => '无参数',
|
||||
],
|
||||
'reindex_opensearch' => [
|
||||
'name' => 'reindex_opensearch',
|
||||
'file' => 'reindex_opensearch.php',
|
||||
'label' => '重建 OpenSearch 索引',
|
||||
'description' => '把 PostgreSQL 中已向量化的数据重新写入 OpenSearch。',
|
||||
'doc_name' => 'reindex_opensearch.md',
|
||||
'args_hint' => '--archive_uid=01...',
|
||||
],
|
||||
'backfill_archive_content' => [
|
||||
'name' => 'backfill_archive_content',
|
||||
'file' => 'backfill_archive_content.php',
|
||||
'label' => '回填 archive content',
|
||||
'description' => '从 raw 或 chunks 回填 archives.content。',
|
||||
'doc_name' => 'backfill_archive_content.md',
|
||||
'args_hint' => '--archive_uid=01...',
|
||||
],
|
||||
'setup_admin_users' => [
|
||||
'name' => 'setup_admin_users',
|
||||
'file' => 'setup_admin_users.php',
|
||||
'label' => '初始化管理员用户',
|
||||
'description' => '创建 admin_users 表并写入或更新管理员账号。',
|
||||
'doc_name' => 'setup_admin_users.md',
|
||||
'args_hint' => '--username=admin --password=secret',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user