"""Prepare only our new directories and credentials on the external disk.""" from pathlib import Path import os, secrets, shutil, socket, subprocess, tarfile, json root = Path('/mnt/data/ikar_server') if not os.path.ismount('/mnt/data'): raise SystemExit('External disk is not mounted') if (root / 'shared/server.env').exists(): raise SystemExit('Credentials already exist; inspect before updating') root.mkdir(exist_ok=True) for name in ('shared/data/secrets', 'releases/20261005', 'backups', 'rollback'): (root / name).mkdir(parents=True, exist_ok=True) baseline = subprocess.check_output(['docker', 'ps', '--format', '{{json .}}'], text=True) (root / 'rollback/services-before.jsonl').write_text(baseline) archive = root / 'rollback/old-ikar-20261005.tar.gz' with tarfile.open(archive, 'w:gz') as bundle: for name in ('ikar-deploy', 'ikar-data'): source = Path('/mnt/data') / name if source.exists(): bundle.add(source, arcname=name) os.chmod(archive, 0o600) old = {} for line in Path('/mnt/data/ikar-deploy/.env.server').read_text().splitlines(): if '=' in line and not line.lstrip().startswith('#'): key, value = line.split('=', 1) old[key] = value turn = {} for line in Path('/mnt/data/ikar-deploy/.env').read_text().splitlines(): if '=' in line: key, value = line.split('=', 1) turn[key] = value shutil.copy2('/mnt/data/ikar-data/secrets/firebase-admin.json', root / 'shared/data/secrets/firebase-admin.json') username = 'ikar' password = secrets.token_urlsafe(32) private_ip = socket.gethostbyname(socket.gethostname()) if private_ip.startswith('127.'): private_ip = subprocess.check_output(['hostname', '-I'], text=True).split()[0] try: public_ip = socket.gethostbyname('ikar.kusoft.xyz') except socket.gaierror: public_ip = socket.gethostbyname('argus.kusoft.xyz') values = { 'ASPNETCORE_ENVIRONMENT': 'Production', 'SeedDemoData': 'false', 'ConnectionStrings__IkarDb': 'Data Source=/app/Data/ikar.db', 'Jwt__SigningKey': secrets.token_urlsafe(64), 'Push__FirebaseProjectId': old.get('Push__FirebaseProjectId', 'ikar-3bdfb'), 'Push__ServiceAccountJsonPath': '/app/Data/secrets/firebase-admin.json', 'Admin__Username': 'admin', 'Admin__Password': secrets.token_urlsafe(32), 'QlyraPublicEndpoint__BaseUrl': 'https://ikar.kusoft.xyz', 'WebRtc__IceServers__0__Urls__0': 'stun:ikar.kusoft.xyz:3478', 'WebRtc__IceServers__1__Urls__0': 'turn:ikar.kusoft.xyz:3478?transport=udp', 'WebRtc__IceServers__1__Urls__1': 'turn:ikar.kusoft.xyz:3478?transport=tcp', 'WebRtc__IceServers__1__Username': username, 'WebRtc__IceServers__1__Credential': password, } values.update({k: v for k, v in old.items() if k.startswith('EmailAuth__')}) env = root / 'shared/server.env' env.write_text(''.join(f'{k}={v}\n' for k, v in values.items())) os.chmod(env, 0o600) env = root / 'shared/turn.env' env.write_text(f'TURN_USERNAME={username}\nTURN_PASSWORD={password}\nTURN_PRIVATE_IP={private_ip}\nTURN_EXTERNAL_IP={public_ip}\n') os.chmod(env, 0o600) subprocess.run(['chown', '-R', '1000:1000', str(root)], check=True) print(json.dumps({'path': str(root), 'oldArchiveBytes': archive.stat().st_size, 'privateIp': private_ip, 'publicIp': public_ip}))