"""Install only IKAR units and its own Compose services on the mounted disk.""" from pathlib import Path import json, os, subprocess root = Path('/mnt/data/ikar_server') release = root / 'releases/20261006' assert os.path.ismount('/mnt/data') and root.resolve() == root assert release.is_dir() and (root / 'shared/server.env').is_file() marker = root / 'shared/data/.ikar-volume' marker.write_text('IKAR external data volume\n') os.chown(marker,1000,1000) current = root / 'current' if current.is_symlink(): current.unlink() elif current.exists(): raise SystemExit('Unexpected current directory') current.symlink_to(release, target_is_directory=True) def env_read(path): return dict(line.split('=',1) for line in path.read_text().splitlines() if '=' in line and not line.startswith('#')) turn = env_read(root / 'shared/turn.env') server_path = root / 'shared/server.env' server = env_read(server_path) ip = turn['TURN_EXTERNAL_IP'] server.update({ 'WebRtc__IceServers__0__Urls__0':f'stun:{ip}:3478', 'WebRtc__IceServers__1__Urls__0':f'turn:{ip}:3478?transport=udp', 'WebRtc__IceServers__1__Urls__1':f'turn:{ip}:3478?transport=tcp', 'IkarUpdates__LocalOnly':'true', 'Logging__LogLevel__Microsoft.EntityFrameworkCore.Database.Command':'Warning', 'Logging__LogLevel__System.Net.Http.HttpClient':'Warning', }) server_path.write_text(''.join(f'{key}={value}\n' for key,value in server.items())) os.chmod(server_path,0o600); os.chown(server_path,1000,1000) unit_root = Path('/etc/systemd/system') (unit_root / 'ikar_server.service').write_text('''[Unit] Description=IKAR messenger on the external disk Requires=docker.service After=docker.service network-online.target RequiresMountsFor=/mnt/data [Service] Type=oneshot RemainAfterExit=yes WorkingDirectory=/mnt/data/ikar_server/current ExecStartPre=/usr/bin/mountpoint -q /mnt/data ExecStart=/usr/bin/docker compose --env-file /mnt/data/ikar_server/shared/turn.env up -d ExecStop=/usr/bin/docker compose --env-file /mnt/data/ikar_server/shared/turn.env stop TimeoutStartSec=90 [Install] WantedBy=multi-user.target ''') (unit_root / 'ikar_server-backup.service').write_text('''[Unit] Description=Consistent IKAR backup RequiresMountsFor=/mnt/data After=ikar_server.service [Service] Type=oneshot User=sevenhill ExecStart=/usr/bin/python3 /mnt/data/ikar_server/current/backup.py Nice=15 IOSchedulingClass=idle ''') (unit_root / 'ikar_server-backup.timer').write_text('''[Unit] Description=Daily IKAR backup [Timer] OnCalendar=*-*-* 03:30:00 Persistent=true RandomizedDelaySec=300 [Install] WantedBy=timers.target ''') subprocess.run(['systemctl','daemon-reload'],check=True) subprocess.run(['docker','compose','--project-directory',str(current),'--env-file',str(root / 'shared/turn.env'),'up','-d'],check=True) subprocess.run(['systemctl','enable','--now','ikar_server.service','ikar_server-backup.timer'],check=True) print(json.dumps({'root':str(root),'release':str(release),'externalIp':ip,'backupTimer':'enabled'}))