尧图精选

OpenHuman referral 域名解析:基于托管后端 /referral/* 的薄 RPC 适配器

🕒 发布时间:2026/9/10 10:34:12 📁 来源:尧图网络
OpenHuman referral 域名解析基于托管后端 /referral/* 的薄 RPC 适配器【免费下载链接】openhumanOpenHuman is an open source personal AI for Mac, Windows and Linux — local-first memory, agent orchestration, and deep research.项目地址: https://gitcode.com/GitHub_Trending/op/openhumanOpenHuman 的 referral推荐奖励程序并不在客户端实现任何业务逻辑而是由一个刻意保持无状态、无 schema、无持久化的薄 RPC 适配器域名src/openhuman/hosted/referral/负责它用服务端reqwest通道向托管后端发出已认证的 HTTP 请求再把后端的原始data载荷原样暴露给 CLI 与 JSON-RPC 客户端。本文将基于该模块的 README 与 ops.rs、schemas.rs 源码完整拆解它的职责边界、两个控制器referral.get_stats/referral.claim的调用链、会话令牌的 fail-closed 前置校验、参数清洗规则、响应包装协议以及它背后的工程动机——规避桌面 WebViewfetch的 Load failedCORS / TLS / WebKit问题。一、模块定位一个刻意无状态的 RPC 适配器referral 域在整个 OpenHuman 架构中的定位非常明确README 第一句话就划定了边界Thin RPC adapter domain for the referral program. It doesnotown any business logic, state, or schema of its own.也就是说这个域名不拥有任何自己的业务逻辑、状态或 schema它的全部工作只有三件事用已认证的reqwest调用托管后端的/referral/*端点把后端响应中的原始data载荷透传出去通过统一的控制器注册表暴露给 CLI / JSON-RPC 客户端。从目录结构看该目录只包含 6 个文件README.md、mod.rs、ops.rs、ops_tests.rs、schemas.rs、schemas_tests.rs没有types.rs、store.rs、tools.rs或bus.rs——这是判断它纯 RPC 适配器而非有状态域的最直接证据它不挂 agent 工具tools不订阅事件总线bus也没有自己的存储store。README 的 Notes/gotchas 一节也明确列了这一条。为什么需要这样一个夹层README 给出了核心工程动机the desktop WebViewfetchto the backend can fail with a generic Load failed (CORS / TLS / WebKit), so these ops reuse the same server-sidereqwestpath as the billing domain.在 Tauri 桌面端渲染进程WebView直接对托管后端发起fetch时可能因为 CORS、TLS 或 WebKit 底层网络栈的差异而失败报出笼统的 Load failed。因此 referral 域刻意复用与 billing计费域完全相同的服务端reqwest通道——所有请求都由 Rust 侧发出绕开 WebView 网络栈从而获得与计费功能一致的稳定性和可观测性。这个设计取舍在 ops.rs 的模块文档中也有同样的说明。二、职责清单Responsibilities按 README该域名的职责可以归纳为五条职责说明拉取推荐统计通过GET /referral/stats获取推荐码code、推荐链接link、累计数据totals以及被推荐用户的行列表referred-user rows认领推荐码通过POST /referral/claim为当前用户认领推荐码可附带可选的设备指纹device fingerprint作为滥用信号会话前置校验任何调用之前都要求解析出后端会话 token没有存储 token 时 fail closed并返回清晰的错误信息参数清洗转发前 trim 推荐码code对deviceFingerprint做 trim 并丢弃纯空白值响应包装将后端响应包装为RpcOutcomeValue附带 grep 友好的日志行其中fail closed是一个重要的安全语义宁可调用失败也不允许未认证请求穿透到后端。两个 ops 在拿不到会话 token 时都会返回固定错误no backend session token; run auth_store_session first。三、关键文件与分工文件角色mod.rs仅做导出export-onlypub use ops::*并重新导出 schema/controller 对all_referral_controller_schemas、all_referral_registered_controllers、referral_schemasops.rs业务逻辑核心require_token私有、get_stats、claim_referral。基于有效后端 URL 构造BackendOAuthClient并发起认证 JSON 请求附带针对 Axum mock 后端的行内测试ops_tests.rsschemas.rs控制器 schema 定义 handle_*函数加载 Config 并委托给 ops定义ReferralClaimParamscamelCase 反序列化与辅助函数to_json、deserialize_params、json_output三层职责分离得很干净mod.rs只管对外导出schemas.rs只管 RPC 契约与参数解析ops.rs只管实际的后端调用。四、公开 API 表面Public surface通过mod.rs的 re-export该模块对外暴露以下 5 个符号// 拉取推荐统计返回 CLI 兼容 JSON pub async fn get_stats(config: Config) - ResultRpcOutcomeValue, String // 认领推荐码code 必填device_fingerprint 可选 pub async fn claim_referral( config: Config, code: str, device_fingerprint: Optionstr, ) - ResultRpcOutcomeValue, String // 返回全部控制器 schemareferral_get_stats referral_claim pub fn all_referral_controller_schemas() - VecControllerSchema // 返回已注册控制器schema handler 对 pub fn all_referral_registered_controllers() - VecRegisteredController // 按函数名查单个 schema未知名返回 unknown 占位 pub fn referral_schemas(function: str) - ControllerSchema注意require_token是私有辅助函数不对外导出README 特别注明。五、RPC 控制器referral.get_stats 与 referral.claim两个控制器都位于referral命名空间通过 src/core/all.rs 注册进全局控制器注册表all_referral_registered_controllers()从而同时暴露给 CLI 与 JSON-RPC。完整契约如下Method输入输出后端调用referral_get_statsreferral.get_stats无statsJSONGET /referral/statsreferral_claimreferral.claimcodestring必填、deviceFingerprintstring可选resultJSONPOST /referral/claimget_stats零输入纯透传get_stats的 handlerschemas.rs不解析任何参数直接加载 Config 后调用 opsfn handle_referral_get_stats(_params: MapString, Value) - ControllerFuture { Box::pin(async move { let config config_rpc::load_config_with_timeout().await?; to_json(crate::openhuman::hosted::referral::get_stats(config).await?) }) }对应的 ops 实现ops.rspub async fn get_stats(config: Config) - ResultRpcOutcomeValue, String { let token require_token(config)?; let api_url effective_backend_api_url(config.api_url); let client BackendOAuthClient::new(api_url).map_err(|e| e.to_string())?; let data client .authed_json(token, Method::GET, /referral/stats, None) .await .map_err(|e| e.to_string())?; Ok(RpcOutcome::single_log( data, referral stats fetched from backend GET /referral/stats, )) }claim两个输入字段的完整处理链claim的 schema 定义了输入契约schemas.rscodeTypeSchema::Stringrequired注释为 Referral code to claim.deviceFingerprintTypeSchema::Option(String)optional注释为 Optional client fingerprint for abuse signals.handlerschemas.rs先反序列化参数再经过与 ops 内部一致的 trim/空白过滤后委托给 opsfn handle_referral_claim(params: MapString, Value) - ControllerFuture { Box::pin(async move { let config config_rpc::load_config_with_timeout().await?; let payload deserialize_params::ReferralClaimParams(params)?; let fp payload .device_fingerprint .as_deref() .map(str::trim) .filter(|s| !s.is_empty()); to_json( crate::openhuman::hosted::referral::claim_referral(config, payload.code.trim(), fp) .await?, ) }) }ops 侧的claim_referralops.rs会构建请求体并转发pub async fn claim_referral( config: Config, code: str, device_fingerprint: Optionstr, ) - ResultRpcOutcomeValue, String { let token require_token(config)?; let api_url effective_backend_api_url(config.api_url); let client BackendOAuthClient::new(api_url).map_err(|e| e.to_string())?; let mut body Map::new(); body.insert(code.to_string(), json!(code.trim())); if let Some(fp) device_fingerprint.map(str::trim).filter(|s| !s.is_empty()) { body.insert(deviceFingerprint.to_string(), json!(fp)); } let data client .authed_json( token, Method::POST, /referral/claim, Some(Value::Object(body)), ) .await .map_err(|e| e.to_string())?; Ok(RpcOutcome::single_log( data, referral claim accepted by backend POST /referral/claim, )) }unknown 占位 schemaschema.rs的referral_schemas对任何无法识别的函数名返回一个unknown占位 schemaschemas.rsnamespace仍为referralfunction为unknown输出只有一个必填的error字段TypeSchema::String注释 Lookup error details.。这让外部调用者在拼错函数名时能拿到结构化错误而非静默失败schemas_tests.rs 中unknown_function_returns_unknown_placeholder测试对该行为做了断言。六、会话令牌fail-closed 的认证前置两个 ops 在发起任何网络请求前都会先经过私有的require_tokenops.rsfn require_token(config: Config) - ResultString, String { get_session_token(config)? .and_then(|v| { let t v.trim().to_string(); if t.is_empty() { None } else { Some(t) } }) .ok_or_else(|| no backend session token; run auth_store_session first.to_string()) }这里的语义是三层防御从凭据库读取会话 tokenget_session_token见 session_support.rs经 src/api/jwt.rs 转发、src/api/mod.rs 统一导出trim 掉首尾空白若结果为空白字符串等价于无 token直接 fail closed。require_token返回ResultString, String要么拿到干净可用的 token 字符串要么返回错误no backend session token; run auth_store_session first——错误信息中明确提示了补救命令auth_store_session。三个单元测试分别覆盖了无存储 token 报错、存储值被 trim、纯空白 token 被拒绝三种情况ops_tests.rs。测试中还展示了如何用AuthService向凭据库播种会话 tokenops_tests.rs这正好印证了 README Dependencies 中的说明测试专用依赖为crate::openhuman::security::credentials::{AuthService, APP_SESSION_PROVIDER, DEFAULT_AUTH_PROFILE_NAME}。七、参数清洗与防御性冗余过滤code与deviceFingerprint的清洗规则是一致且双重的code始终执行trim()。测试claim_referral_posts_trimmed_code_and_drops_whitespace_fingerprintops_tests.rs验证了 ABC-123 会被转成ABC-123再发送deviceFingerprint先trim()再用filter(|s| !s.is_empty())丢弃纯空白值。同样是上述测试验证传Some( )时请求体里不出现deviceFingerprint字段assert!(out.value[echoed].get(deviceFingerprint).is_none())而传Some( fp-1 )时会被清洗为fp-1见claim_referral_forwards_non_empty_device_fingerprint_trimmedops_tests.rs。README 特别强调这套 trim/空白丢弃逻辑在ops::claim_referral和 schema 处理器handle_referral_claim两处各做了一遍README。这是刻意的防御性冗余过滤——无论调用方走哪一层入口输入都能被清洗避免出现CLI 路径干净、JSON-RPC 路径脏的不一致。八、响应包装RpcOutcome 与 CLI 兼容 JSON所有 ops 的返回值都是RpcOutcomeValue——该类型定义在 src/rpc/mod.rs包含两个字段value: TRPC 调用返回的真实数据和logs: VecString审计/调试用日志。两个 ops 都用RpcOutcome::single_log构造返回值src/rpc/mod.rs并附带grep 友好的固定前缀日志行get_stats→referral stats fetched from backend GET /referral/statsclaim_referral→referral claim accepted by backend POST /referral/claim在 schema handler 层to_json调用outcome.into_cli_compatible_json()src/rpc/mod.rs将其转为 CLI 兼容的 JSON。根据 src/rpc/mod.rs 中定义的唯一规则log envelopelogs.is_empty() - value (bare直接输出值) otherwise - { result: value, logs: … } (wrapped包一层信封)由于 referral 的两个 ops 总是写入一条日志实际响应形状会是{ result: …, logs: […] }。schemas_tests 中的to_json_wraps_result_and_logsschemas_tests.rs对该行为做了验证。九、后端 URL 解析链effective_backend_api_urlreferral ops 使用effective_backend_api_url(config.api_url)解析后端 API 基址src/api/config.rs。这是 OpenHuman 所有托管后端调用auth、billing、team、referral、webhooks、credentials、channels、voice 等共用的单一真相源其解析顺序为用户显式配置config.api_url——但只有当它不像推理端点时才会被采用looks_like_local_ai_endpointOllama/vLLM/LM Studio 等本地模型服务端口 11434/8000/8080/1234 等或含/v1/chat/completions路径looks_like_inference_provider_endpointopenrouter.ai、openai.com、anthropic.com 等托管推理服务商域名或/v1、/api/v1基路径is_cloud_inference内置云服务商主机名以上任一命中且不是 OpenHuman 自有后端api.tinyhumans.ai/staging-api.tinyhumans.ai时跳过用户覆盖回退到环境/默认链。环境变量BACKEND_URL、VITE_BACKEND_URL运行时优先其次编译期option_env!内嵌。环境感知默认值OPENHUMAN_APP_ENVstaging时回落到https://staging-api.tinyhumans.ai否则使用https://api.tinyhumans.aisrc/api/config.rs。这套守卫的意义在于用户把config.api_url指向本地 Ollama 或第三方推理服务时referral、billing 等控制面调用不会误路由到推理服务商否则会得到 400/404/500从而保证了 referral 请求始终落在真正的托管后端上。README Dependencies 对该依赖的注释是 resolves the effective backend API base URL fromconfig.api_url。十、测试策略基于 Axum mock 后端的全链路验证该模块的测试质量很高ops_tests.rs采用真实 HTTP 环回验证而非 mock 函数返回值spawn_mockops_tests.rs在127.0.0.1:0上启动一个真实 Axum 服务并做就绪探测指数退避最长 2 秒返回其临时端口 URL测试配置把config.api_url指向该 mock 基址并用AuthService::store_provider_token播种test-session-tokenconfig_with_backendops_tests.rsmock 路由直接返回 JSONGET /referral/stats返回{referrals: 3, earned_cents: 1500}POST /referral/claim回显请求体{echoed: body}以便断言清洗后的字段。覆盖的用例包括无会话报错stats/claim 各一个、stats 载荷透传与日志存在性、claim 的 code trim 与空白 fingerprint 丢弃、非空 fingerprint 的 trim 转发。schemas_tests.rs则从 RPC 契约角度验证schema 数量与控制器数量一致、get_stats 无输入且输出必填、claim 的 code 必填而 fingerprint 可选、camelCase 参数解析、缺失 fingerprint 容错、缺失 code 报错、类型错误返回invalid params前缀、unknown 占位 schema 等。十一、设计约束与注意事项gotchas结合 README 与源码这个域名的工程约束可以总结为以下几点刻意零内部结构没有types.rs/store.rs/tools.rs/bus.rs——纯 RPC 适配器不持有状态不提供 agent 工具不订阅事件总线。判断一个模块是不是有状态域看它是否挂载这些文件即可。无持久化完全无状态只通过get_session_token从凭据库读取后端会话 token自身不落盘任何数据。fail-closed 语义两个 ops 在缺少会话 token 时一律失败错误信息固定为no backend session token; run auth_store_session first。资格判定在后端claim的准入条件only users who have not yet subscribed——仅限尚未订阅的用户由托管后端强制执行本模块只做请求转发不参与判定README。双重防御性清洗code 与 fingerprint 的 trim/空白丢弃在 ops 层与 schema 层各执行一次防止入口不一致。刻意复用 billing 通道绕开 WebViewfetchCORS/TLS/WebKit Load failed所有请求走 Rust 侧reqwest保证与计费域同等稳定的网络行为与统一的可观测性日志。小结referral 域是 OpenHuman 托管后端 客户端薄适配器架构的一个典型样本它把所有业务复杂度留在服务端客户端只做认证、清洗、转发、包装四件事同时通过统一的控制器注册表src/core/all.rs向 CLI 与 JSON-RPC 提供一致的调用面。理解它的关键不在于它写了多少逻辑而在于它刻意不写哪些逻辑——无状态、无 schema、无工具、无持久化配合 fail-closed 的会话校验、双层的参数清洗和 grep 友好的日志构成一个高度可审计、可替换、易测试的薄适配层。如果你的目标是扩展 OpenHuman 的托管后端能力如新增一个/referral/*端点只需沿这条路径在ops.rs增加一个认证请求函数、在schemas.rs增加 schema 与 handler、在all.rs完成注册并在ops_tests.rs用 Axum mock 补上链路测试即可。【免费下载链接】openhumanOpenHuman is an open source personal AI for Mac, Windows and Linux — local-first memory, agent orchestration, and deep research.项目地址: https://gitcode.com/GitHub_Trending/op/openhuman创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联 返回资讯列表 →