video_monitor/monitor_runtime/bootstrap.py
2026-09-04 18:16:14 +08:00

95 lines
4.4 KiB
Python

"""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}))