Files
youle_app_ios_v2/ylgamehall/Source/Network/QiniuConfig.swift
T
joywayerandClaude Opus 4.7 83d9502dee ADR-009 凭证集中化:AppSecrets.plist + 七牛运行参数走 RemoteConfig.audio_*(契约影响)
【数据流改造(三分制)】
- 微信 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>
2026-06-27 21:07:55 +08:00

61 lines
2.6 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//
// 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)"
}
}