2.0.0 Beta : Self-hosted Release

This commit is contained in:
2026-08-09 02:03:29 +08:00
parent 63a0751aba
commit 37cb05eb70
33 changed files with 810 additions and 31 deletions
+107
View File
@@ -0,0 +1,107 @@
import argparse
from datetime import datetime, timezone
import hashlib
import json
from pathlib import Path
import shutil
import sqlite3
from database.paths import DATABASE_CODE_ROOT, PROJECT_ROOT
DATABASE_LAYOUT = {
'L1': ('L1', 'L1.db'),
'L2': ('L2', 'L2.db'),
'L3': ('L3', 'L3.db'),
'Web': ('Web', 'Web_App.sqlite'),
}
def _sha256(path):
digest = hashlib.sha256()
with path.open('rb') as file:
for chunk in iter(lambda: file.read(1024 * 1024), b''):
digest.update(chunk)
return digest.hexdigest()
def _sqlite_copy(source, target):
target.parent.mkdir(parents=True, exist_ok=True)
source_db = sqlite3.connect(str(source))
target_db = sqlite3.connect(str(target))
try:
source_db.backup(target_db)
result = target_db.execute('PRAGMA quick_check').fetchone()[0]
if result != 'ok':
raise RuntimeError(f'{target.name} quick_check failed: {result}')
finally:
source_db.close()
target_db.close()
def migrate_data(source_root, target_root, include_imports=False):
source_root = Path(source_root).expanduser().resolve()
target_root = Path(target_root).expanduser().resolve()
if source_root == target_root:
raise ValueError('Source and target data roots must differ')
target_root.mkdir(parents=True, exist_ok=True)
manifest = {
'created_at': datetime.now(timezone.utc).isoformat(),
'source_root': str(source_root),
'target_root': str(target_root),
'databases': {},
}
for name, (directory, filename) in DATABASE_LAYOUT.items():
source = source_root / directory / filename
target = target_root / directory / filename
if not source.exists():
raise FileNotFoundError(f'Missing source database: {source}')
_sqlite_copy(source, target)
manifest['databases'][name] = {
'path': str(target),
'size_bytes': target.stat().st_size,
'sha256': _sha256(target),
'quick_check': 'ok',
}
if include_imports:
legacy_imports = PROJECT_ROOT / 'output_arena'
if legacy_imports.exists():
destination = target_root / 'imports'
shutil.copytree(
legacy_imports,
destination,
dirs_exist_ok=True,
)
manifest['imports'] = str(destination)
manifest_path = target_root / 'migration-manifest.json'
manifest_path.write_text(
json.dumps(manifest, ensure_ascii=True, indent=2),
encoding='utf-8',
)
return manifest_path
def _parse_args():
parser = argparse.ArgumentParser()
parser.add_argument(
'--source',
default=str(DATABASE_CODE_ROOT),
help='Existing runtime data root',
)
parser.add_argument('--target', required=True)
parser.add_argument('--include-imports', action='store_true')
return parser.parse_args()
if __name__ == '__main__':
args = _parse_args()
path = migrate_data(
args.source,
args.target,
include_imports=args.include_imports,
)
print(f'Migration completed: {path}')