feat(cli): add craftlabs-auth-cli with status/activate/check/info/release commands

This commit is contained in:
2026-05-25 15:16:39 +08:00
parent 027ecbd375
commit 4b79533c70
6 changed files with 333 additions and 2 deletions
+175
View File
@@ -0,0 +1,175 @@
use clap::{Parser, Subcommand};
use craftlabs_auth_core::{
craft_activate, craft_check_license, craft_destroy, craft_free_license_info,
craft_get_license_info, craft_heartbeat, craft_initialize, craft_release,
};
use craftlabs_auth_core::device;
use std::ffi::CString;
use std::path::PathBuf;
#[derive(Parser)]
#[command(name = "craft", version, about = "CraftLabs 授权客户端")]
struct Cli {
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
/// 查看本地授权状态
Status,
/// 使用 SN 激活本机
Activate { sn: String },
/// 检查授权是否有效
Check,
/// 显示授权详情 + 功能特性
Info,
/// 撤销本机授权
Release,
/// 显示本机设备指纹
DeviceId,
/// 手动触发心跳
Heartbeat,
}
fn get_config_path() -> PathBuf {
let mut path = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
path.push("craftlabs-auth-config.json");
path
}
fn default_config() -> String {
r#"{"provider":"selfhosted","schemaVersion":1,"scenario":"floating"}"#.to_string()
}
fn load_or_create_config() -> String {
let path = get_config_path();
std::fs::read_to_string(&path).unwrap_or_else(|_| {
let cfg = default_config();
std::fs::write(&path, &cfg).ok();
cfg
})
}
fn main() {
let cli = Cli::parse();
let config = load_or_create_config();
let c_config = CString::new(config).unwrap();
match &cli.command {
Commands::Status => {
print_status();
}
Commands::Activate { sn } => {
let handle = craft_initialize(c_config.as_ptr());
if handle.is_null() {
eprintln!("错误: 初始化授权引擎失败");
std::process::exit(1);
}
let c_sn = CString::new(sn.as_str()).unwrap();
let result = craft_activate(handle, c_sn.as_ptr(), std::ptr::null());
if result.success != 0 {
println!("✅ 激活成功");
save_config_with_sn(sn);
} else {
let msg = if result.message.is_null() { "未知错误" } else {
unsafe { std::ffi::CStr::from_ptr(result.message) }.to_str().unwrap_or("未知错误")
};
eprintln!("❌ 激活失败: {}", msg);
}
craft_destroy(handle);
}
Commands::Check => {
let handle = craft_initialize(c_config.as_ptr());
if handle.is_null() { eprintln!("初始化失败"); return; }
let result = craft_check_license(handle);
if result.success != 0 {
println!("✅ 授权有效");
} else {
println!("❌ 授权无效");
}
craft_destroy(handle);
}
Commands::Info => {
let handle = craft_initialize(c_config.as_ptr());
if handle.is_null() { eprintln!("初始化失败"); return; }
let info = craft_get_license_info(handle);
if !info.is_null() {
let i = unsafe { &*info };
let names = unsafe { std::slice::from_raw_parts(i.feature_names, i.feature_count as usize) };
let values = unsafe { std::slice::from_raw_parts(i.feature_values, i.feature_count as usize) };
println!("授权状态: {}", if i.is_licensed != 0 { "已授权" } else { "未授权" });
if i.expiration_date > 0 {
println!("过期时间: {}", i.expiration_date);
}
if i.feature_count > 0 {
println!("功能特性 ({}):", i.feature_count);
for idx in 0..i.feature_count as usize {
let name = if idx < names.len() && !names[idx].is_null() {
unsafe { std::ffi::CStr::from_ptr(names[idx]) }
.to_str().unwrap_or("?")
} else { "?" };
let val = if idx < values.len() { values[idx] } else { 0 };
println!(" {}: {}", name, if val != 0 { "" } else { "" });
}
}
craft_free_license_info(info);
} else {
println!("无法获取授权信息");
}
craft_destroy(handle);
}
Commands::Release => {
let handle = craft_initialize(c_config.as_ptr());
if handle.is_null() { eprintln!("初始化失败"); return; }
let result = craft_release(handle);
if result.success != 0 {
println!("✅ 授权已撤销");
} else {
println!("❌ 撤销失败");
}
craft_destroy(handle);
}
Commands::DeviceId => {
let fp = device::collect();
println!("设备指纹: {}", fp.composite_hash);
}
Commands::Heartbeat => {
let handle = craft_initialize(c_config.as_ptr());
if handle.is_null() { eprintln!("初始化失败"); return; }
let result = craft_heartbeat(handle);
if result.success != 0 {
println!("✅ 心跳成功");
} else {
println!("❌ 心跳失败");
}
craft_destroy(handle);
}
}
}
fn print_status() {
let config = load_or_create_config();
let c_config = CString::new(config).unwrap();
let handle = craft_initialize(c_config.as_ptr());
if handle.is_null() { eprintln!("初始化失败"); return; }
let check = craft_check_license(handle);
println!("授权状态: {}", if check.success != 0 { "✅ 有效" } else { "❌ 无效" });
let fp = device::collect();
println!("设备指纹: {}", fp.composite_hash);
craft_destroy(handle);
}
fn save_config_with_sn(sn: &str) {
let config = serde_json::json!({
"provider": "selfhosted",
"schemaVersion": 1,
"scenario": "floating",
"floating": { "sn": sn }
});
let path = get_config_path();
std::fs::write(&path, serde_json::to_string_pretty(&config).unwrap()).ok();
}