mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-10-09 03:20:56 +00:00
Split anonymous ID into server (UUID-on-disk) and CLI (MAC-based hash) strategies
Server is long-lived on a fixed host, so a persisted UUID at ~/.arc/.id is appropriate. CLI runs ephemerally, so an MD5 of the MAC address avoids file I/O and is stable per-machine. CLI falls back to ~/.arc/.id if it exists for migration. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
2b8d6a40a1
commit
f4b530c691
5 changed files with 92 additions and 12 deletions
42
Cargo.lock
generated
42
Cargo.lock
generated
|
|
@ -364,6 +364,8 @@ dependencies = [
|
|||
"chrono",
|
||||
"console 0.15.11",
|
||||
"dirs",
|
||||
"mac_address",
|
||||
"md5",
|
||||
"regex",
|
||||
"reqwest 0.12.28",
|
||||
"serde",
|
||||
|
|
@ -2525,6 +2527,16 @@ version = "0.1.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
|
||||
|
||||
[[package]]
|
||||
name = "mac_address"
|
||||
version = "1.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0aeb26bf5e836cc1c341c8106051b573f1766dfa05aa87f0b98be5e51b02303"
|
||||
dependencies = [
|
||||
"nix 0.29.0",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "markup5ever"
|
||||
version = "0.35.0"
|
||||
|
|
@ -2584,12 +2596,27 @@ dependencies = [
|
|||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "md5"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mime"
|
||||
version = "0.3.17"
|
||||
|
|
@ -2681,6 +2708,19 @@ version = "1.0.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
"memoffset",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.31.2"
|
||||
|
|
@ -3216,7 +3256,7 @@ checksum = "ccd9713fe2c91c3c85ac388b31b89de339365d2c995146e630b5e0da9d06526a"
|
|||
dependencies = [
|
||||
"futures",
|
||||
"indexmap 2.13.0",
|
||||
"nix",
|
||||
"nix 0.31.2",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"windows",
|
||||
|
|
|
|||
|
|
@ -42,6 +42,8 @@ semver = "1"
|
|||
aho-corasick = "1"
|
||||
sqlx = { version = "0.8", features = ["runtime-tokio", "sqlite", "chrono"] }
|
||||
dirs = "6"
|
||||
mac_address = "1"
|
||||
md5 = "0.7"
|
||||
indicatif = "0.18"
|
||||
termimad = "0.34"
|
||||
toml = "0.8"
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ base64.workspace = true
|
|||
dirs.workspace = true
|
||||
tracing.workspace = true
|
||||
anyhow.workspace = true
|
||||
mac_address.workspace = true
|
||||
md5.workspace = true
|
||||
|
||||
[build-dependencies]
|
||||
toml = "0.8"
|
||||
|
|
|
|||
|
|
@ -3,13 +3,14 @@ use std::fs;
|
|||
use std::path::PathBuf;
|
||||
use uuid::Uuid;
|
||||
|
||||
fn anonymous_id_path() -> Result<PathBuf> {
|
||||
fn dot_id_path() -> Result<PathBuf> {
|
||||
let home = dirs::home_dir().context("could not determine home directory")?;
|
||||
Ok(home.join(".arc").join("anonymous_id"))
|
||||
Ok(home.join(".arc").join(".id"))
|
||||
}
|
||||
|
||||
pub fn load_or_create_anonymous_id() -> Result<String> {
|
||||
let path = anonymous_id_path()?;
|
||||
/// Server: UUID persisted at ~/.arc/.id
|
||||
pub fn load_or_create_server_id() -> Result<String> {
|
||||
let path = dot_id_path()?;
|
||||
|
||||
if let Ok(contents) = fs::read_to_string(&path) {
|
||||
let id = contents.trim().to_string();
|
||||
|
|
@ -31,17 +32,40 @@ pub fn load_or_create_anonymous_id() -> Result<String> {
|
|||
Ok(id)
|
||||
}
|
||||
|
||||
/// CLI: prefer existing ~/.arc/.id, else MD5 of MAC address
|
||||
pub fn compute_cli_id() -> Result<String> {
|
||||
let path = dot_id_path()?;
|
||||
|
||||
if let Ok(contents) = fs::read_to_string(&path) {
|
||||
let id = contents.trim().to_string();
|
||||
if !id.is_empty() {
|
||||
return Ok(id);
|
||||
}
|
||||
}
|
||||
|
||||
let mac = mac_address::get_mac_address()
|
||||
.context("failed to get MAC address")?
|
||||
.context("no MAC address found")?;
|
||||
|
||||
let digest = md5::compute(mac.bytes());
|
||||
Ok(format!("{:x}", digest))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn load_or_create_returns_stable_id() {
|
||||
// Calls the real function, which uses ~/.arc/anonymous_id
|
||||
let id1 = load_or_create_anonymous_id().unwrap();
|
||||
let id2 = load_or_create_anonymous_id().unwrap();
|
||||
fn compute_cli_id_returns_non_empty_string() {
|
||||
let id = compute_cli_id().unwrap();
|
||||
assert!(!id.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn load_or_create_server_id_returns_stable_uuid() {
|
||||
let id1 = load_or_create_server_id().unwrap();
|
||||
let id2 = load_or_create_server_id().unwrap();
|
||||
assert_eq!(id1, id2);
|
||||
// Should be a valid UUID
|
||||
Uuid::parse_str(&id1).unwrap();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,9 +24,21 @@ pub struct Telemetry {
|
|||
}
|
||||
|
||||
impl Telemetry {
|
||||
pub fn new() -> Result<Self> {
|
||||
pub fn for_server() -> Result<Self> {
|
||||
let level = telemetry_level();
|
||||
let anonymous_id = anonymous_id::load_or_create_anonymous_id()?;
|
||||
let anonymous_id = anonymous_id::load_or_create_server_id()?;
|
||||
let context = context::build_context();
|
||||
|
||||
Ok(Self {
|
||||
level,
|
||||
anonymous_id,
|
||||
context,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn for_cli() -> Result<Self> {
|
||||
let level = telemetry_level();
|
||||
let anonymous_id = anonymous_id::compute_cli_id()?;
|
||||
let context = context::build_context();
|
||||
|
||||
Ok(Self {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue