Some checks failed
Security / Security check (push) Failing after 2s
Track the platform rename: the hub spawn path is now ~/.chain/bin/chain (was ~/.fai/bin/fai.exe on Windows — both dir and binary were stale, so Studio could not launch the hub after the config-dir rename), the ~/.fai/* help strings become ~/.chain/*, FAI_REGISTRY_TOKEN -> CHAIN_REGISTRY_TOKEN, and the two in-app doc URLs point at the public fai/chain repo (fai/platform was renamed to the private fai/chain-private). The .fai module bundle extension is left unchanged (format phase). flutter analyze: no issues. Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
67 lines
2.4 KiB
Dart
67 lines
2.4 KiB
Dart
import 'dart:io';
|
|
import 'package:path/path.dart' as p;
|
|
|
|
/// Operator-managed registry auth token, kept at
|
|
/// `~/.chain/registry-token` (mode 0600 on Unix). The hub reads
|
|
/// this file at install time when `CHAIN_REGISTRY_TOKEN` is unset
|
|
/// — see `download_to_temp` in `crates/fai_hub/src/lib.rs`.
|
|
///
|
|
/// Studio writes the file directly so a fresh install never
|
|
/// requires the operator to fiddle with shell env vars.
|
|
class RegistryToken {
|
|
static String _faiHome() {
|
|
final home =
|
|
Platform.environment['HOME'] ?? Platform.environment['USERPROFILE'];
|
|
if (home == null || home.isEmpty) {
|
|
throw StateError('Cannot resolve home directory (no HOME / USERPROFILE)');
|
|
}
|
|
return p.join(home, '.fai');
|
|
}
|
|
|
|
/// Absolute path to the token file.
|
|
static String get path => p.join(_faiHome(), 'registry-token');
|
|
|
|
/// True iff the file exists with a non-empty trimmed body.
|
|
static Future<bool> isConfigured() async {
|
|
final f = File(path);
|
|
if (!await f.exists()) return false;
|
|
final content = await f.readAsString();
|
|
return content.trim().isNotEmpty;
|
|
}
|
|
|
|
/// Length of the trimmed token, or null when the file is
|
|
/// missing / empty. Used for status display ("Configured
|
|
/// (40 chars)") without ever surfacing the secret.
|
|
static Future<int?> charCount() async {
|
|
final f = File(path);
|
|
if (!await f.exists()) return null;
|
|
final content = await f.readAsString();
|
|
final trimmed = content.trim();
|
|
return trimmed.isEmpty ? null : trimmed.length;
|
|
}
|
|
|
|
/// Persist [token] to disk, creating `~/.chain/` if needed.
|
|
/// On Unix the file is chmod-ed to 0600 (owner read/write
|
|
/// only). On Windows the default user-ACL is left alone —
|
|
/// best-effort, no PowerShell handshake.
|
|
static Future<void> write(String token) async {
|
|
final f = File(path);
|
|
await f.parent.create(recursive: true);
|
|
await f.writeAsString(token.trim(), flush: true);
|
|
if (Platform.isLinux || Platform.isMacOS) {
|
|
try {
|
|
await Process.run('chmod', ['600', f.path]);
|
|
} catch (_) {
|
|
// best-effort; if chmod is unavailable the file still
|
|
// exists with default perms (usually 0644, world-readable
|
|
// but only on the operator's own machine).
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Delete the token file. No-op when the file doesn't exist.
|
|
static Future<void> delete() async {
|
|
final f = File(path);
|
|
if (await f.exists()) await f.delete();
|
|
}
|
|
}
|