/// 약속 (modular_sync #6 · F11): 원격 서버 등록 하나 - 설정의 서버 목록 ([RemoteRegistry.configure]) → 서버 id 마다 클라이언트 하나 /// ([RemoteRegistry.client]). WebDAV · SFTP · SMB · FTP 가 같은 몸통을 쓰고, 서버마다 다른 것은 셋뿐: 비밀 값 목록 · "설정 없음" 오류 · 만들기. /// 다시 접속 (예전 클라이언트를 닫고 다음에 새로) 하는 바뀜 - 예전 4벌과 똑같이 (관리자 10/24 조건 ①): /// 서버를 지움 · 새로 들어옴 · toJson 이 다름 (비밀이 아닌 모든 칸: 주소 · 포트 · 사용자 · 루트 · 보안 · 지문 · 공유 · 도메인 · insecure …) · /// 비밀 값이 다름 (WebDAV · SMB · FTP = password, SFTP = password · privateKey · keyPassphrase) /// 문 ([requireSecretGate]): 비밀 값이 있으면 마스터 확인 (SecretGate) 을 지나야 연다 - 못 지나면 [DavLockedException] (연결 전에) library; import 'dart:convert'; import '../l10n/tr.dart'; import 'remote_fs.dart'; import 'secret_gate.dart'; import 'webdav.dart' show DavLockedException; class RemoteRegistry { RemoteRegistry({required this.idOf, required this.toJson, required this.secretsOf, required this.create, required this.missing, this.onDrop}); final String Function(S server) idOf; final Map Function(S server) toJson; /// 비밀 값들 (toJson 에 없는 것 - 바뀌면 다시 접속) final List Function(S server) secretsOf; final C Function(S server) create; /// 등록에 없는 id 로 [client] 를 부를 때의 오류 final Exception Function(String id) missing; /// 클라이언트를 버릴 때 (다시 접속 · 지움) - SMB 의 지난 연결 서명 기록 지우기 등 final void Function(String id)? onDrop; final _clients = {}; final _servers = {}; bool _same(S old, S now) { final a = secretsOf(old), b = secretsOf(now); if (a.length != b.length) return false; for (var i = 0; i < a.length; i++) { if (a[i] != b[i]) return false; } return jsonEncode(toJson(old)) == jsonEncode(toJson(now)); } void configure(List servers) { final keep = {for (final s in servers) idOf(s): s}; for (final id in _clients.keys.toList()) { final old = _servers[id], now = keep[id]; if (now == null || old == null || !_same(old, now)) { _clients.remove(id)?.close(); onDrop?.call(id); } } _servers ..clear() ..addAll(keep); } List get servers => _servers.values.toList(); S? server(String id) => _servers[id]; /// 열린 클라이언트를 모두 닫는다 (다음 [client] 에서 새로 - 연결 규칙이 바뀐 때) void closeAll() { for (final c in _clients.values) { c.close(); } _clients.clear(); } C client(String id) { final s = _servers[id]; if (s == null) throw missing(id); return _clients.putIfAbsent(id, () => create(s)); } } /// 문: 비밀 값이 있으면 마스터 확인을 지나야 한다 (못 지나면 연결 전에 [DavLockedException]). 비밀 값이 없으면 묻지 않는다 Future requireSecretGate(bool hasSecret) async { if (!await SecretGate.passIf(hasSecret)) throw DavLockedException(tr(secretGateMessage)); }