【数据流改造(三分制)】 - 微信 AppID 唯一源 = Info.plist CFBundleURLTypes (URLName=weixin first scheme) - 应用级凭证 唯一源 = AppSecrets.plist (wxAppSecret/qiniuAccessKey/qiniuSecretKey) - 七牛运行参数 唯一源 = RemoteConfig 顶层 audio_domain / audio_bucket(远端动态注入) 【契约影响】 - ChannelConfig.plist:11 key → 10 key,移除 qiniudomain(ADR-007 守护规则同步) - BundleConfig:删除 qiniuDomain 属性 - RemoteConfig:顶层新增可选字段 audioDomain / audioBucket(JSON snake_case 自动归一化) - 启动期:WebContainerViewController parsed 分支校验 audio_domain/audio_bucket 非空, 缺失抛 BootError.audioConfigMissing 弹 modal 永停(与 showmessage 同等致命) - 上线前置:测试 / 生产远端 .txt 配置必须先补 audio_domain / audio_bucket 两个顶层 key - WeChatSDK.appID / WeChatAuth.appSecret / QiniuConfig.* 调用方零签名变化 【新增】 - ylgamehall/Resources/AppSecrets.plist(3 key) - ylgamehall/Source/Resource/AppSecrets.swift(单例加载,对齐 BundleConfig 模式) - QiniuConfig 改 actor:cdnDomain/bucketName 进 actor 状态 + update(...) async; accessKey/secretKey 仍 nonisolated(直接读 AppSecrets) - QiniuTokenSigner.uploadToken() 改 async(bucketName 来自 actor) - QiniuUploader 预取 cdnDomain 闭包外,SDK 同步 callback 内直接拼 URL 【删除】 - WeChatSDK.swift static let appID 硬编码 → Info.plist 启动期解析 - WeChatAuth.swift static let appSecret 硬编码 → AppSecrets.shared.wxAppSecret - QiniuConfig 中 accessKey / secretKey / bucketName / cdnDomain 四处硬编码 - ChannelConfig.plist 的 qiniudomain 字段(plist 与代码双源僵尸字段) 【文档同步】 - Plan:新增 ADR-009 + ADR-007 守护规则改 10 key + §236 BundleConfig 描述 - Design §3.4 多处 "11 项" → "10 项";§7.0.3 plist 示例 + BundleConfig 代码骨架 + Scripts/inject_channel.sh 同步 - SDK-Integration-Guide §0 凭证位置改三分制 + §尾"七牛域名读取"加新外壳路径 - Verification-Checklist L69 "(11 项)" → "(10 项)" 参考契约章节:docs/Development-Plan.md ADR-009、docs/H5-Native-Implementation-Design.md §7.0.3 BuildProject 通过,Xcode 即时诊断 0 警告。 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
61 lines
2.6 KiB
Swift
61 lines
2.6 KiB
Swift
//
|
||
// QiniuConfig.swift
|
||
// ylgamehall
|
||
//
|
||
// 七牛云配置访问层。
|
||
//
|
||
// 数据来源(单一真相):
|
||
// - accessKey / secretKey ← AppSecrets.plist(应用级凭证,跨渠道相同)
|
||
// - bucketName / cdnDomain ← RemoteConfig 顶层 audio_bucket / audio_domain
|
||
// (远端动态下发,缺失启动期致命,由 WebContainerViewController 校验后
|
||
// 调用 update(domain:bucket:) 注入)
|
||
//
|
||
// ⚠️ secretKey 客户端持有 = 父项目 CLAUDE.md「已识别但接受的安全风险」,
|
||
// 同款 wxAppSecret 路径。msext 已分发多年等价泄露,沿用不引入新攻击面。
|
||
//
|
||
|
||
import Foundation
|
||
|
||
/// 七牛云配置访问层。actor 隔离让远端注入的 cdnDomain / bucketName 在并发上传场景下
|
||
/// 写读安全。accessKey / secretKey 是 nonisolated immutable,无需 actor 屏障。
|
||
public actor QiniuConfig {
|
||
|
||
public static let shared = QiniuConfig()
|
||
|
||
// MARK: - 来自 AppSecrets.plist(应用级,启动期一次性加载,运行期不变)
|
||
|
||
/// 七牛 AccessKey — 来源 `AppSecrets.plist.qiniuAccessKey`
|
||
public nonisolated static var accessKey: String { AppSecrets.shared.qiniuAccessKey }
|
||
|
||
/// 七牛 SecretKey — 来源 `AppSecrets.plist.qiniuSecretKey`。HMAC-SHA1 签 putPolicy 用
|
||
public nonisolated static var secretKey: String { AppSecrets.shared.qiniuSecretKey }
|
||
|
||
/// 录音 key 模板前缀(msext QiniuConfig.m:22 为空字符串)
|
||
public nonisolated static let recordingDirectory = ""
|
||
|
||
// MARK: - 来自 RemoteConfig(远端,启动期动态注入)
|
||
|
||
private var _cdnDomain: String = ""
|
||
private var _bucketName: String = ""
|
||
|
||
/// 启动期由 WebContainerViewController 在远端配置拉到且校验非空后调用,
|
||
/// 之后 QiniuUploader / QiniuTokenSigner 即可 await 读取。
|
||
public func update(cdnDomain: String, bucketName: String) {
|
||
_cdnDomain = cdnDomain
|
||
_bucketName = bucketName
|
||
}
|
||
|
||
/// CDN 域名(不带 http:// 协议头)。访问前必须先 `update(...)`,否则返回空串
|
||
/// 导致后续上传/拼 URL 失败 —— 但启动流水线已保证 update 一定先于任何上传调用
|
||
public var cdnDomain: String { _cdnDomain }
|
||
|
||
/// 七牛 bucket 名(putPolicy.scope)。访问前必须先 `update(...)`。
|
||
public var bucketName: String { _bucketName }
|
||
|
||
/// 拼访问 URL:`http://{cdnDomain}/{key}`(msext QiniuManager.m:197 等价)
|
||
/// 协议头固定 http://(与 msext 一致;七牛 CDN 同时支持 https,按需评估)。
|
||
public func publicURL(forKey key: String) -> String {
|
||
"http://\(_cdnDomain)/\(key)"
|
||
}
|
||
}
|