This commit is contained in:
2026-05-08 00:05:51 +08:00
parent 549b706fcc
commit ed70a140a2
40 changed files with 5590 additions and 36 deletions
+117
View File
@@ -0,0 +1,117 @@
#!/usr/bin/env php
<?php
use app\service\ArticleImportService;
use support\Db;
require __DIR__ . '/../vendor/autoload.php';
require __DIR__ . '/../support/bootstrap.php';
require __DIR__ . '/../vendor/webman/database/src/support/Db.php';
$archiveUid = null;
$force = false;
$dryRun = false;
foreach (array_slice($argv, 1) as $argument) {
if (str_starts_with($argument, '--archive_uid=')) {
$archiveUid = substr($argument, strlen('--archive_uid='));
continue;
}
if ($argument === '--force') {
$force = true;
continue;
}
if ($argument === '--dry-run') {
$dryRun = true;
}
}
$query = Db::table('archives')->orderBy('id');
if ($archiveUid !== null && trim($archiveUid) !== '') {
$query->where('archive_uid', trim($archiveUid));
}
if (!$force) {
$query->where(function ($builder) {
$builder->whereNull('content')->orWhere('content', '');
});
}
$archives = $query->get(['archive_uid', 'title', 'content', 'raw'])->all();
$normalizer = new ArticleImportService();
$scanned = 0;
$updated = 0;
$fromRaw = 0;
$fromChunks = 0;
$skipped = 0;
foreach ($archives as $archive) {
$scanned++;
$archiveUidValue = (string) $archive->archive_uid;
$raw = is_string($archive->raw ?? null) ? $archive->raw : null;
$content = null;
$source = 'none';
if (is_string($raw) && trim($raw) !== '') {
$content = $normalizer->normalizeArchiveContentString($raw);
$source = 'raw';
} else {
$chunks = Db::table('chunks')
->where('archive_uid', $archiveUidValue)
->orderBy('chunk_index')
->pluck('text')
->all();
$chunks = array_values(array_filter(array_map(
static fn ($value): string => trim((string) $value),
$chunks
), static fn (string $value): bool => $value !== ''));
if ($chunks !== []) {
$content = trim(implode("\n\n", $chunks));
$source = 'chunks';
}
}
if ($content === null || $content === '') {
$skipped++;
echo "[skip] {$archiveUidValue} no usable raw/chunks" . PHP_EOL;
continue;
}
if ($dryRun) {
echo "[dry-run] {$archiveUidValue} source={$source} content_length=" . mb_strlen($content) . PHP_EOL;
if ($source === 'raw') {
$fromRaw++;
} else {
$fromChunks++;
}
continue;
}
Db::table('archives')
->where('archive_uid', $archiveUidValue)
->update(['content' => $content]);
$updated++;
if ($source === 'raw') {
$fromRaw++;
} else {
$fromChunks++;
}
echo "[updated] {$archiveUidValue} source={$source} content_length=" . mb_strlen($content) . PHP_EOL;
}
echo 'Archive content backfill completed.' . PHP_EOL;
echo 'Archive filter: ' . ($archiveUid ?: 'auto') . PHP_EOL;
echo 'Force mode: ' . ($force ? 'yes' : 'no') . PHP_EOL;
echo 'Dry run: ' . ($dryRun ? 'yes' : 'no') . PHP_EOL;
echo 'Scanned: ' . $scanned . PHP_EOL;
echo 'Updated: ' . $updated . PHP_EOL;
echo 'From raw: ' . $fromRaw . PHP_EOL;
echo 'From chunks: ' . $fromChunks . PHP_EOL;
echo 'Skipped: ' . $skipped . PHP_EOL;
+68
View File
@@ -0,0 +1,68 @@
#!/usr/bin/env php
<?php
use app\service\Search\ChunkSearchIndexHandler;
use app\service\Search\ChunkSearchIndexRepository;
use app\service\Search\OpenSearchChunkIndex;
use support\Db;
require __DIR__ . '/../vendor/autoload.php';
require __DIR__ . '/../support/bootstrap.php';
require __DIR__ . '/../vendor/webman/database/src/support/Db.php';
$archiveUid = null;
foreach (array_slice($argv, 1) as $argument) {
if (str_starts_with($argument, '--archive_uid=')) {
$archiveUid = substr($argument, strlen('--archive_uid='));
}
}
$repository = new ChunkSearchIndexRepository();
$handler = new ChunkSearchIndexHandler();
$index = new OpenSearchChunkIndex();
try {
$index->ensureExists();
$resetCount = $repository->resetEmbeddedChunksToPending($archiveUid);
$archiveCount = 0;
$indexedArchives = [];
$indexedChunks = 0;
while (true) {
$archiveUids = $repository->queuePendingArchiveTasks(100);
if ($archiveUids === []) {
break;
}
foreach ($archiveUids as $uid) {
$handler->handle([
'task_type' => 'search_index',
'target_type' => 'archive',
'target_uid' => $uid,
'attempt' => 1,
]);
$archiveCount++;
$indexedArchives[] = $uid;
}
}
$indexedChunksQuery = Db::table('chunks')->where('search_index_status', 3);
if ($archiveUid !== null && trim($archiveUid) !== '') {
$indexedChunksQuery->where('archive_uid', trim($archiveUid));
}
$indexedChunks = (int) $indexedChunksQuery->count();
echo 'OpenSearch reindex completed.' . PHP_EOL;
echo 'Index: ' . config('opensearch.indices.chunks', 'proofdb_chunks') . PHP_EOL;
echo 'Archive filter: ' . ($archiveUid ?: '(all embedded archives)') . PHP_EOL;
echo 'Reset chunks: ' . $resetCount . PHP_EOL;
echo 'Indexed archives: ' . $archiveCount . PHP_EOL;
echo 'Indexed chunk rows now marked indexed: ' . $indexedChunks . PHP_EOL;
if ($indexedArchives !== []) {
echo 'Archives: ' . implode(', ', $indexedArchives) . PHP_EOL;
}
} catch (Throwable $exception) {
fwrite(STDERR, $exception::class . ': ' . $exception->getMessage() . PHP_EOL);
exit(1);
}
+92
View File
@@ -0,0 +1,92 @@
#!/usr/bin/env php
<?php
use support\Db;
require __DIR__ . '/../vendor/autoload.php';
require __DIR__ . '/../support/bootstrap.php';
require __DIR__ . '/../vendor/webman/database/src/support/Db.php';
$username = null;
$password = null;
$displayName = null;
foreach (array_slice($argv, 1) as $argument) {
if (str_starts_with($argument, '--username=')) {
$username = substr($argument, strlen('--username='));
continue;
}
if (str_starts_with($argument, '--password=')) {
$password = substr($argument, strlen('--password='));
continue;
}
if (str_starts_with($argument, '--display_name=')) {
$displayName = substr($argument, strlen('--display_name='));
}
}
if (!is_string($username) || trim($username) === '' || !is_string($password) || $password === '') {
fwrite(STDERR, "Usage: php scripts/setup_admin_users.php --username=<username> --password=<password> [--display_name=<name>]" . PHP_EOL);
exit(1);
}
$username = trim($username);
$displayName = is_string($displayName) && trim($displayName) !== '' ? trim($displayName) : $username;
$passwordHash = password_hash($password, PASSWORD_DEFAULT);
$statements = [
<<<SQL
CREATE TABLE IF NOT EXISTS admin_users (
id BIGSERIAL PRIMARY KEY,
username VARCHAR(120) NOT NULL UNIQUE,
display_name TEXT,
password_hash TEXT NOT NULL,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
last_login_at TIMESTAMPTZ,
created_time TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_time TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
)
SQL,
'CREATE INDEX IF NOT EXISTS admin_users_is_active_index ON admin_users (is_active)',
<<<SQL
CREATE OR REPLACE FUNCTION set_updated_time()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_time = CURRENT_TIMESTAMP;
RETURN NEW;
END;
$$ LANGUAGE plpgsql
SQL,
'DROP TRIGGER IF EXISTS admin_users_set_updated_time ON admin_users',
<<<SQL
CREATE TRIGGER admin_users_set_updated_time
BEFORE UPDATE ON admin_users
FOR EACH ROW
EXECUTE FUNCTION set_updated_time()
SQL,
];
try {
Db::connection()->getPdo();
foreach ($statements as $statement) {
Db::statement($statement);
}
Db::table('admin_users')->updateOrInsert(
['username' => $username],
[
'display_name' => $displayName,
'password_hash' => $passwordHash,
'is_active' => true,
]
);
echo 'Admin users table initialized.' . PHP_EOL;
echo 'Seeded username: ' . $username . PHP_EOL;
echo 'Display name: ' . $displayName . PHP_EOL;
} catch (Throwable $exception) {
fwrite(STDERR, $exception::class . ': ' . $exception->getMessage() . PHP_EOL);
exit(1);
}