feat(rust): add Provider trait + refactor C ABI to route through Provider

SelfHostedProvider implements Provider trait for offline license verification
This commit is contained in:
2026-05-18 22:17:05 +08:00
parent 8b90a71077
commit fbce298f2b
3 changed files with 163 additions and 70 deletions
@@ -6,6 +6,7 @@ use crate::{
CraftContext, LicenseInfo,
device::DeviceFingerprint,
error::LicenseError,
trait_provider::{Provider, ActivateResponse, HeartbeatResponse, LicenseStatus},
};
pub struct SelfHostedConfig {
@@ -131,3 +132,58 @@ impl SelfHostedProvider {
self.cache.persist(&self.device_fp)
}
}
// ── Provider trait 实现 ──
impl Provider for SelfHostedProvider {
fn initialize(&mut self, _ctx: &CraftContext, config_json: &str) -> Result<(), LicenseError> {
let cfg: serde_json::Value = serde_json::from_str(config_json)
.map_err(|_| LicenseError::InvalidFormat("config json"))?;
let sh = cfg.get("selfhosted").ok_or(LicenseError::ConfigMissing("selfhosted section"))?;
let base_url = sh.get("baseUrl").and_then(|v| v.as_str()).unwrap_or("").to_string();
let tenant_key = sh.get("tenantKey").and_then(|v| v.as_str()).unwrap_or("").to_string();
let offline_grace_days = sh.get("offlineGraceDays").and_then(|v| v.as_u64()).unwrap_or(7) as u32;
let heartbeat_interval_hours = sh.get("heartbeatIntervalHours").and_then(|v| v.as_u64()).unwrap_or(24) as u32;
let public_key_pem = option_env!("CRAFTLABS_SELFHOSTED_PUBKEY").unwrap_or("").to_string();
self.initialize(base_url, tenant_key, offline_grace_days, heartbeat_interval_hours, public_key_pem)
}
fn activate(&self, _ctx: &CraftContext, _license_key: &str) -> Result<ActivateResponse, LicenseError> {
Err(LicenseError::Network("online activation not yet implemented".into()))
}
fn check_license(&self, _ctx: &CraftContext) -> Result<LicenseStatus, LicenseError> {
self.check_license_offline()?;
let cached = self.cache.license.as_ref().ok_or(LicenseError::NoCachedLicense)?;
Ok(LicenseStatus {
licensed: true,
not_after: cached.not_after,
features: cached.features.clone(),
device_count: 0,
max_devices: cached.max_devices,
heartbeat_due: None,
})
}
fn heartbeat(&self, _ctx: &CraftContext) -> Result<HeartbeatResponse, LicenseError> {
Err(LicenseError::Network("heartbeat not yet implemented".into()))
}
fn has_feature(&self, _ctx: &CraftContext, name: &str) -> bool {
self.has_feature_offline(name)
}
fn release(&mut self) -> Result<(), LicenseError> {
Ok(())
}
fn get_license_info(&self, _ctx: &CraftContext) -> LicenseInfo {
self.get_license_info_offline()
}
fn close(&mut self) {
let _ = self.persist_cache();
}
}