"""Fresh-install initialization and non-destructive version upgrades.""" import json import os import shutil import sqlite3 from datetime import datetime from monitor_runtime.paths import data, resource, atomic_json def prepare_files(): data('').mkdir(parents=True, exist_ok=True) for name in ('.runtime', 'log', 'backups', 'static/upload/weight', 'static/upload/audio', 'static/storage', '.runtime/zlm', '.runtime/ultralytics/Ultralytics', '.runtime/matplotlib'): data(name).mkdir(parents=True, exist_ok=True) for target, source in (('config.json', 'deploy/windows/default-config.json'), ('settings.json', 'deploy/windows/default-settings.json'), ('zlm-template.ini', 'deploy/windows/zlm-template.ini')): if not data(target).exists(): shutil.copyfile(resource(source), data(target)) os.environ['YOLO_CONFIG_DIR'] = str(data('.runtime/ultralytics')) os.environ['MPLCONFIGDIR'] = str(data('.runtime/matplotlib')) os.environ['YOLO_OFFLINE'] = 'true' os.environ['YOLO_AUTOINSTALL'] = 'false' os.environ['CI'] = 'true' def initialize_database(): import django os.environ['DJANGO_SETTINGS_MODULE'] = 'framework.settings' os.environ['MONITOR_SERVICE_MODE'] = 'disabled' os.environ['MONITOR_BOOTSTRAP_SERVICES'] = 'false' django.setup() from django.core.management import call_command from django.db import connection from django.db.migrations.executor import MigrationExecutor executor = MigrationExecutor(connection) plan = executor.migration_plan(executor.loader.graph.leaf_nodes()) db = data('monitor.sqlite3') if plan and db.exists() and db.stat().st_size: backup = data('backups') / datetime.now().strftime('%Y%m%d-%H%M%S-%f') backup.mkdir() with sqlite3.connect(str(db)) as src, sqlite3.connect(str(backup / db.name)) as dst: src.backup(dst) for name in ('config.json', 'settings.json', 'network.json', '.runtime-secrets.json', 'license.json'): if data(name).exists(): shutil.copyfile(data(name), backup / name) call_command('migrate', interactive=False, verbosity=0) from django.contrib.auth.models import Group for role in ('system_admin', 'algorithm_admin', 'operator', 'viewer'): Group.objects.get_or_create(name=role) from app.utils.Credentials import migrate_existing_credentials migrate_existing_credentials() def has_admin(): from django.contrib.auth import get_user_model marker = data('.runtime/initialized.json') if marker.exists(): return True exists = get_user_model().objects.filter(is_superuser=True).exists() if exists: atomic_json(marker, {'schema_version': 1, 'initialized': True}) return exists def local_addresses(): import socket import psutil return sorted({a.address for values in psutil.net_if_addrs().values() for a in values if a.family == socket.AF_INET and not a.address.startswith(('127.', '169.254.')) and a.address != '0.0.0.0'}) def finish_setup(username, password, address): from django.contrib.auth import get_user_model from django.contrib.auth.password_validation import validate_password from django.db import transaction from monitor_runtime.licensing import require_license require_license() if address not in local_addresses(): raise ValueError('请选择本机有效的局域网 IPv4 地址') if len(password) < 12: raise ValueError('管理员密码至少 12 位') username = username.strip() if not username or len(username) > 150: raise ValueError('管理员用户名无效') user = get_user_model()(username=username) validate_password(password, user) with transaction.atomic(): if has_admin(): raise ValueError('初始化已完成') cfg = json.loads(data('config.json').read_text(encoding='utf-8')) cfg['host'] = address cfg.setdefault('sipServer', {})['sipServerIp'] = address atomic_json(data('config.json'), cfg) atomic_json(data('network.json'), {'allowed_hosts': [address], 'address': address}) get_user_model().objects.create_superuser(username=username, password=password) transaction.on_commit(lambda: atomic_json(data('.runtime/initialized.json'), {'schema_version': 1, 'initialized': True}))