import Foundation#if canImport(FoundationNetworking)import FoundationNetworking#endif public enum AlibabaTokenPlanUsageError: LocalizedError, Sendable, Equatable { case loginRequired case invalidCredentials case apiError(String) case networkError(String) case parseFailed(String) public var errorDescription: String? { switch self { case .loginRequired: "Alibaba Token Plan login required." case .invalidCredentials: "Alibaba Token Plan credentials are invalid." case let .apiError(message): "Alibaba Token Plan API error: \(message)" case let .networkError(message): "Alibaba Token Plan network error: \(message)" case let .parseFailed(message): "Could not parse Alibaba Token Plan usage: \(message)" } }} // swiftlint:disable:next type_body_lengthpublic struct AlibabaTokenPlanUsageFetcher: Sendable { private static let log = CodexBarLog.logger("alibaba-token-plan") private static let gatewayBaseURLString = "https://bailian.console.aliyun.com" private static let dashboardOriginURLString = "https://bailian.console.aliyun.com" private static let currentRegionID = "cn-beijing" private static let bssServiceCode = "BssOpenAPI-V3" private static let subscriptionSummaryAction = "GetSubscriptionSummary" private static let tokenPlanProductCode = "sfm_tokenplanteams_dp_cn" private static let browserLikeUserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) " + "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36" private static let safariLikeUserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) " + "AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.3 Safari/605.1.15" public static var dashboardURL: URL { URL(string: "https://bailian.console.aliyun.com/cn-beijing?tab=plan#/efm/subscription/token-plan")! } public static func fetchUsage( cookieHeader: String, environment: [String: String] = ProcessInfo.processInfo.environment, now: Date = Date()) async throws -> AlibabaTokenPlanUsageSnapshot { guard let headers = AlibabaTokenPlanCookieHeaders(singleHeader: cookieHeader) else { throw AlibabaTokenPlanSettingsError.invalidCookie } return try await self.fetchUsage( apiCookieHeader: headers.apiCookieHeader, dashboardCookieHeader: headers.dashboardCookieHeader, environment: environment, now: now) } static func fetchUsage( apiCookieHeader: String, dashboardCookieHeader: String, environment: [String: String] = ProcessInfo.processInfo.environment, now: Date = Date(), session overrideSession: URLSession? = nil) async throws -> AlibabaTokenPlanUsageSnapshot { guard let normalizedAPIHeader = CookieHeaderNormalizer.normalize(apiCookieHeader), let normalizedDashboardHeader = CookieHeaderNormalizer.normalize(dashboardCookieHeader) else { throw AlibabaTokenPlanSettingsError.invalidCookie } let url = self.resolveQuotaURL(environment: environment) let apiRedirectDiagnostics = RedirectDiagnostics(cookieHeader: normalizedAPIHeader) let dashboardRedirectDiagnostics: RedirectDiagnostics? let apiSession: URLSession let dashboardSession: URLSession if let overrideSession { apiSession = overrideSession dashboardSession = overrideSession dashboardRedirectDiagnostics = nil } else { let dashboardDiagnostics = RedirectDiagnostics(cookieHeader: normalizedDashboardHeader) apiSession = URLSession( configuration: .default, delegate: apiRedirectDiagnostics, delegateQueue: nil) dashboardSession = URLSession( configuration: .default, delegate: dashboardDiagnostics, delegateQueue: nil) dashboardRedirectDiagnostics = dashboardDiagnostics } defer { if overrideSession == nil { apiSession.invalidateAndCancel() dashboardSession.invalidateAndCancel() } } let secToken = await self.resolveSECToken( dashboardCookieHeader: normalizedDashboardHeader, apiCookieHeader: normalizedAPIHeader, environment: environment, session: dashboardSession) Self.log.info( "Fetching Alibaba Token Plan usage", metadata: [ "apiHost": url.host ?? "unknown", "apiCookieNames": self.cookieNamesDescription(from: normalizedAPIHeader), "dashboardCookieNames": self.cookieNamesDescription(from: normalizedDashboardHeader), "hasCSRF": self.hasCSRF(in: normalizedAPIHeader) ? "1" : "0", "secTokenSource": secToken == nil ? "missing" : "resolved", ]) var request = URLRequest(url: url) request.httpMethod = "POST" request.timeoutInterval = 20 request.httpBody = self.subscriptionSummaryRequestBody(secToken: secToken) request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type") request.setValue("*/*", forHTTPHeaderField: "Accept") request.setValue(normalizedAPIHeader, forHTTPHeaderField: "Cookie") if let csrf = self.extractCookieValue(name: "login_aliyunid_csrf", from: normalizedAPIHeader) ?? self.extractCookieValue(name: "csrf", from: normalizedAPIHeader) { request.setValue(csrf, forHTTPHeaderField: "x-xsrf-token") request.setValue(csrf, forHTTPHeaderField: "x-csrf-token") } request.setValue("XMLHttpRequest", forHTTPHeaderField: "X-Requested-With") request.setValue(Self.browserLikeUserAgent, forHTTPHeaderField: "User-Agent") request.setValue(Self.dashboardOriginURLString, forHTTPHeaderField: "Origin") request.setValue(Self.dashboardURL.absoluteString, forHTTPHeaderField: "Referer") let data: Data let response: URLResponse do { (data, response) = try await apiSession.data(for: request) } catch { Self.log.error( "Alibaba Token Plan request failed", metadata: [ "apiHost": url.host ?? "unknown", "error": error.localizedDescription, ]) throw AlibabaTokenPlanUsageError.networkError(error.localizedDescription) } if let dashboardRedirectDiagnostics, !dashboardRedirectDiagnostics.redirects.isEmpty { Self.log.info( "Alibaba Token Plan dashboard redirects", metadata: [ "count": "\(dashboardRedirectDiagnostics.redirects.count)", "items": dashboardRedirectDiagnostics.redirects.joined(separator: " | "), ]) } if !apiRedirectDiagnostics.redirects.isEmpty { Self.log.info( "Alibaba Token Plan redirects", metadata: [ "count": "\(apiRedirectDiagnostics.redirects.count)", "items": apiRedirectDiagnostics.redirects.joined(separator: " | "), ]) } guard let httpResponse = response as? HTTPURLResponse else { Self.log.error("Alibaba Token Plan response was not HTTP") throw AlibabaTokenPlanUsageError.networkError("Invalid response") } Self.log.info( "Alibaba Token Plan HTTP response", metadata: [ "status": "\(httpResponse.statusCode)", "bodyBytes": "\(data.count)", "contentType": httpResponse.value(forHTTPHeaderField: "Content-Type") ?? "none", ]) guard httpResponse.statusCode == 200 else { if httpResponse.statusCode == 401 || httpResponse.statusCode == 403 { throw AlibabaTokenPlanUsageError.loginRequired } Self.log.error("Alibaba Token Plan returned HTTP \(httpResponse.statusCode)") throw AlibabaTokenPlanUsageError.apiError("HTTP \(httpResponse.statusCode)") } return try self.parseUsageSnapshot(from: data, now: now) } static func resolveQuotaURL(environment: [String: String]) -> URL { if let override = AlibabaTokenPlanSettingsReader.quotaURL(environment: environment) { return override } if let host = AlibabaTokenPlanSettingsReader.hostOverride(environment: environment), let hostURL = self.quotaURL(from: host) { return hostURL } return self.defaultQuotaURL } static var defaultQuotaURL: URL { var components = URLComponents(string: Self.gatewayBaseURLString)! components.path = "/data/api.json" components.queryItems = [ URLQueryItem(name: "action", value: Self.subscriptionSummaryAction), URLQueryItem(name: "product", value: Self.bssServiceCode), URLQueryItem(name: "_tag", value: ""), ] return components.url! } static func parseUsageSnapshot(from data: Data, now: Date = Date()) throws -> AlibabaTokenPlanUsageSnapshot { guard !data.isEmpty else { throw AlibabaTokenPlanUsageError.parseFailed("Empty response body") } let object: Any do { object = try JSONSerialization.jsonObject(with: data, options: []) } catch { if self.isLikelyLoginHTML(data) { throw AlibabaTokenPlanUsageError.loginRequired } throw AlibabaTokenPlanUsageError.parseFailed("Invalid JSON response") } let expanded = self.expandedJSON(object) guard let dictionary = expanded as? [String: Any] else { throw AlibabaTokenPlanUsageError.parseFailed("Unexpected payload") } try self.throwIfErrorPayload(dictionary) let summary = self.findSubscriptionSummary(in: dictionary) ?? dictionary let total = self.anyDouble(for: Self.totalQuotaKeys, in: summary) let remaining = self.anyDouble(for: Self.remainingQuotaKeys, in: summary) let used = self.anyDouble(for: Self.usedQuotaKeys, in: summary) ?? total.flatMap { total in remaining.map { max(0, total - $0) } } let resetsAt = self.findResetDate(in: summary) ?? self.findResetDate(in: dictionary) let totalCount = self.anyDouble(for: Self.subscriptionCountKeys, in: summary) let planName = self.findPlanName(in: summary) ?? ((totalCount ?? 0) > 0 || total != nil ? "TOKEN PLAN" : nil) if planName == nil, total == nil, used == nil, remaining == nil, totalCount == nil { let diagnostics = self.payloadDiagnostics(payload: dictionary) Self.log.error("Alibaba Token Plan payload missing expected fields: \(diagnostics)") throw AlibabaTokenPlanUsageError.parseFailed("Missing token plan data (\(diagnostics))") } return AlibabaTokenPlanUsageSnapshot( planName: planName, usedQuota: used, totalQuota: total, remainingQuota: remaining, resetsAt: resetsAt, updatedAt: now) } private static func subscriptionSummaryRequestBody(secToken: String?) -> Data { let paramsObject = ["ProductCode": Self.tokenPlanProductCode] guard let paramsData = try? JSONSerialization.data(withJSONObject: paramsObject, options: []), let paramsString = String(data: paramsData, encoding: .utf8) else { return Data() } var components = URLComponents() var queryItems = [ URLQueryItem(name: "product", value: Self.bssServiceCode), URLQueryItem(name: "action", value: Self.subscriptionSummaryAction), URLQueryItem(name: "params", value: paramsString), URLQueryItem(name: "region", value: Self.currentRegionID), ] if let secToken, !secToken.isEmpty { queryItems.append(URLQueryItem(name: "sec_token", value: secToken)) } components.queryItems = queryItems return Data((components.percentEncodedQuery ?? "").utf8) } private static func resolveSECToken( dashboardCookieHeader: String, apiCookieHeader: String, environment: [String: String], session: URLSession) async -> String? { let cookieSECToken = self.extractCookieValue(name: "sec_token", from: dashboardCookieHeader) ?? self.extractCookieValue(name: "sec_token", from: apiCookieHeader) var request = URLRequest(url: self.dashboardURL(environment: environment)) request.httpMethod = "GET" request.timeoutInterval = 10 request.setValue(dashboardCookieHeader, forHTTPHeaderField: "Cookie") request.setValue(Self.safariLikeUserAgent, forHTTPHeaderField: "User-Agent") request.setValue( "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", forHTTPHeaderField: "Accept") if let (data, response) = try? await session.data(for: request), let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200, let html = String(data: data, encoding: .utf8), let token = self.extractSECToken(from: html) { Self.log.info( "Resolved Alibaba Token Plan sec_token from dashboard HTML", metadata: [ "dashboardHost": request.url?.host ?? "unknown", "htmlBytes": "\(data.count)", ]) return token } if let token = await self.fetchSECTokenFromUserInfo( cookieHeader: dashboardCookieHeader, environment: environment, session: session) { return token } if let cookieSECToken, !cookieSECToken.isEmpty { Self.log.info("Resolved Alibaba Token Plan sec_token from cookies") return cookieSECToken } Self.log.info( "Alibaba Token Plan sec_token missing; continuing with cookie-only request", metadata: [ "dashboardCookieNames": self.cookieNamesDescription(from: dashboardCookieHeader), "apiCookieNames": self.cookieNamesDescription(from: apiCookieHeader), ]) return nil } private static func fetchSECTokenFromUserInfo( cookieHeader: String, environment: [String: String], session: URLSession) async -> String? { let baseURL = self.consoleBaseURL(environment: environment) let userInfoURL = baseURL.appendingPathComponent("tool/user/info.json") var request = URLRequest(url: userInfoURL) request.httpMethod = "GET" request.timeoutInterval = 10 request.setValue(cookieHeader, forHTTPHeaderField: "Cookie") request.setValue(Self.safariLikeUserAgent, forHTTPHeaderField: "User-Agent") request.setValue("application/json, text/plain, */*", forHTTPHeaderField: "Accept") let referer = baseURL.absoluteString.hasSuffix("/") ? baseURL.absoluteString : "\(baseURL.absoluteString)/" request.setValue(referer, forHTTPHeaderField: "Referer") guard let (data, response) = try? await session.data(for: request), let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200, let object = try? JSONSerialization.jsonObject(with: data, options: []) else { return nil } let expanded = self.expandedJSON(object) guard let token = self.findFirstString(forKeys: ["secToken", "sec_token"], in: expanded), !token.isEmpty else { return nil } Self.log.info( "Resolved Alibaba Token Plan sec_token from user info", metadata: [ "userInfoHost": userInfoURL.host ?? "unknown", "bodyBytes": "\(data.count)", ]) return token } private static func consoleBaseURL(environment: [String: String]) -> URL { let dashboard = self.dashboardURL(environment: environment) var components = URLComponents() components.scheme = dashboard.scheme components.host = dashboard.host components.port = dashboard.port return components.url ?? URL(string: Self.dashboardOriginURLString)! } private static func quotaURL(from rawHost: String) -> URL? { let cleaned = AlibabaTokenPlanSettingsReader.cleaned(rawHost) guard let cleaned else { return nil } let base = URL(string: cleaned)?.scheme == nil ? URL(string: "https://\(cleaned)") : URL(string: cleaned) guard let base else { return nil } var components = URLComponents(url: base, resolvingAgainstBaseURL: false) let defaultComponents = URLComponents(url: Self.defaultQuotaURL, resolvingAgainstBaseURL: false) components?.path = "/data/api.json" components?.queryItems = defaultComponents?.queryItems return components?.url } static func dashboardURL(environment: [String: String]) -> URL { if let host = AlibabaTokenPlanSettingsReader.hostOverride(environment: environment), let base = URL(string: host)?.scheme == nil ? URL(string: "https://\(host)") : URL(string: host), var components = URLComponents(url: base, resolvingAgainstBaseURL: false), let dashboardComponents = URLComponents(url: dashboardURL, resolvingAgainstBaseURL: false) { components.path = dashboardComponents.path components.percentEncodedQuery = dashboardComponents.percentEncodedQuery components.fragment = dashboardComponents.fragment return components.url ?? self.dashboardURL } return Self.dashboardURL } private final class RedirectDiagnostics: NSObject, URLSessionTaskDelegate, @unchecked Sendable { private let cookieHeader: String var redirects: [String] = [] init(cookieHeader: String) { self.cookieHeader = cookieHeader } func urlSession( _: URLSession, task _: URLSessionTask, willPerformHTTPRedirection response: HTTPURLResponse, newRequest request: URLRequest, completionHandler: @escaping (URLRequest?) -> Void) { let from = AlibabaTokenPlanUsageFetcher.redactedURLDescription(response.url) let to = AlibabaTokenPlanUsageFetcher.redactedURLDescription(request.url) self.redirects.append("\(response.statusCode) \(from) -> \(to)") completionHandler(AlibabaTokenPlanUsageFetcher.redirectedRequest( response: response, request: request, cookieHeader: self.cookieHeader)) } } private static func redactedURLDescription(_ url: URL?) -> String { guard let url else { return "unknown" } var components = URLComponents(url: url, resolvingAgainstBaseURL: false) components?.query = nil components?.fragment = nil return components?.string ?? "\(url.scheme ?? "unknown")://\(url.host ?? "unknown")" } static func redirectedRequest( response: HTTPURLResponse, request: URLRequest, cookieHeader: String) -> URLRequest? { guard request.url?.scheme?.lowercased() == "https" else { return nil } var updated = request if self.shouldForwardRedirectCookies(from: response.url, to: request.url) { updated.setValue(cookieHeader, forHTTPHeaderField: "Cookie") } else { updated.setValue(nil, forHTTPHeaderField: "Cookie") } return updated } private static func shouldForwardRedirectCookies(from sourceURL: URL?, to targetURL: URL?) -> Bool { guard let sourceHost = sourceURL?.host?.lowercased(), let targetHost = targetURL?.host?.lowercased() else { return false } return sourceHost == targetHost } private static func throwIfErrorPayload(_ dictionary: [String: Any]) throws { if self.parseBool(dictionary["successResponse"]) == false { if let statusCode = self.findFirstInt(forKeys: ["statusCode", "status_code", "code"], in: dictionary), statusCode == 401 || statusCode == 403 { throw AlibabaTokenPlanUsageError.invalidCredentials } let code = self.findFirstString(forKeys: ["code", "status", "statusCode"], in: dictionary) let message = self.findFirstString(forKeys: ["message", "msg", "statusMessage"], in: dictionary) ?? code ?? "request was not successful" if self.isLoginOrTokenError(code: code, message: message) { throw AlibabaTokenPlanUsageError.loginRequired } throw AlibabaTokenPlanUsageError.apiError(message) } if self.findBoolValues(forKeys: ["Success", "success"], in: dictionary).contains(false) { let code = self.findFirstString(forKeys: ["Code", "code"], in: dictionary) let message = self.findFirstString(forKeys: ["Message", "message", "msg", "Code", "code"], in: dictionary) ?? "request was not successful" if self.isLoginOrTokenError(code: code, message: message) { throw AlibabaTokenPlanUsageError.loginRequired } throw AlibabaTokenPlanUsageError.apiError(message) } if let statusCode = self.findFirstInt(forKeys: ["statusCode", "status_code", "code"], in: dictionary), statusCode != 0, statusCode != 200 { let message = self.findFirstString( forKeys: ["statusMessage", "status_msg", "message", "msg"],Lines 1–500 of 977. Load more
import Foundation #if canImport(FoundationNetworking) import FoundationNetworking #endif public enum AlibabaTokenPlanUsageError: LocalizedError, Sendable, Equatable { case loginRequired case invalidCredentials case apiError(String) case networkError(String) case parseFailed(String) public var errorDescription: String? { switch self { case .loginRequired: "Alibaba Token Plan login required." case .invalidCredentials: "Alibaba Token Plan credentials are invalid." case let .apiError(message): "Alibaba Token Plan API error: \(message)" case let .networkError(message): "Alibaba Token Plan network error: \(message)" case let .parseFailed(message): "Could not parse Alibaba Token Plan usage: \(message)" } } } // swiftlint:disable:next type_body_length public struct AlibabaTokenPlanUsageFetcher: Sendable { private static let log = CodexBarLog.logger("alibaba-token-plan") private static let gatewayBaseURLString = "https://bailian.console.aliyun.com" private static let dashboardOriginURLString = "https://bailian.console.aliyun.com" private static let currentRegionID = "cn-beijing" private static let bssServiceCode = "BssOpenAPI-V3" private static let subscriptionSummaryAction = "GetSubscriptionSummary" private static let tokenPlanProductCode = "sfm_tokenplanteams_dp_cn" private static let browserLikeUserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) " + "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36" private static let safariLikeUserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) " + "AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.3 Safari/605.1.15" public static var dashboardURL: URL { URL(string: "https://bailian.console.aliyun.com/cn-beijing?tab=plan#/efm/subscription/token-plan")! } public static func fetchUsage( cookieHeader: String, environment: [String: String] = ProcessInfo.processInfo.environment, now: Date = Date()) async throws -> AlibabaTokenPlanUsageSnapshot { guard let headers = AlibabaTokenPlanCookieHeaders(singleHeader: cookieHeader) else { throw AlibabaTokenPlanSettingsError.invalidCookie } return try await self.fetchUsage( apiCookieHeader: headers.apiCookieHeader, dashboardCookieHeader: headers.dashboardCookieHeader, environment: environment, now: now) } static func fetchUsage( apiCookieHeader: String, dashboardCookieHeader: String, environment: [String: String] = ProcessInfo.processInfo.environment, now: Date = Date(), session overrideSession: URLSession? = nil) async throws -> AlibabaTokenPlanUsageSnapshot { guard let normalizedAPIHeader = CookieHeaderNormalizer.normalize(apiCookieHeader), let normalizedDashboardHeader = CookieHeaderNormalizer.normalize(dashboardCookieHeader) else { throw AlibabaTokenPlanSettingsError.invalidCookie } let url = self.resolveQuotaURL(environment: environment) let apiRedirectDiagnostics = RedirectDiagnostics(cookieHeader: normalizedAPIHeader) let dashboardRedirectDiagnostics: RedirectDiagnostics? let apiSession: URLSession let dashboardSession: URLSession if let overrideSession { apiSession = overrideSession dashboardSession = overrideSession dashboardRedirectDiagnostics = nil } else { let dashboardDiagnostics = RedirectDiagnostics(cookieHeader: normalizedDashboardHeader) apiSession = URLSession( configuration: .default, delegate: apiRedirectDiagnostics, delegateQueue: nil) dashboardSession = URLSession( configuration: .default, delegate: dashboardDiagnostics, delegateQueue: nil) dashboardRedirectDiagnostics = dashboardDiagnostics } defer { if overrideSession == nil { apiSession.invalidateAndCancel() dashboardSession.invalidateAndCancel() } } let secToken = await self.resolveSECToken( dashboardCookieHeader: normalizedDashboardHeader, apiCookieHeader: normalizedAPIHeader, environment: environment, session: dashboardSession) Self.log.info( "Fetching Alibaba Token Plan usage", metadata: [ "apiHost": url.host ?? "unknown", "apiCookieNames": self.cookieNamesDescription(from: normalizedAPIHeader), "dashboardCookieNames": self.cookieNamesDescription(from: normalizedDashboardHeader), "hasCSRF": self.hasCSRF(in: normalizedAPIHeader) ? "1" : "0", "secTokenSource": secToken == nil ? "missing" : "resolved", ]) var request = URLRequest(url: url) request.httpMethod = "POST" request.timeoutInterval = 20 request.httpBody = self.subscriptionSummaryRequestBody(secToken: secToken) request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type") request.setValue("*/*", forHTTPHeaderField: "Accept") request.setValue(normalizedAPIHeader, forHTTPHeaderField: "Cookie") if let csrf = self.extractCookieValue(name: "login_aliyunid_csrf", from: normalizedAPIHeader) ?? self.extractCookieValue(name: "csrf", from: normalizedAPIHeader) { request.setValue(csrf, forHTTPHeaderField: "x-xsrf-token") request.setValue(csrf, forHTTPHeaderField: "x-csrf-token") } request.setValue("XMLHttpRequest", forHTTPHeaderField: "X-Requested-With") request.setValue(Self.browserLikeUserAgent, forHTTPHeaderField: "User-Agent") request.setValue(Self.dashboardOriginURLString, forHTTPHeaderField: "Origin") request.setValue(Self.dashboardURL.absoluteString, forHTTPHeaderField: "Referer") let data: Data let response: URLResponse do { (data, response) = try await apiSession.data(for: request) } catch { Self.log.error( "Alibaba Token Plan request failed", metadata: [ "apiHost": url.host ?? "unknown", "error": error.localizedDescription, ]) throw AlibabaTokenPlanUsageError.networkError(error.localizedDescription) } if let dashboardRedirectDiagnostics, !dashboardRedirectDiagnostics.redirects.isEmpty { Self.log.info( "Alibaba Token Plan dashboard redirects", metadata: [ "count": "\(dashboardRedirectDiagnostics.redirects.count)", "items": dashboardRedirectDiagnostics.redirects.joined(separator: " | "), ]) } if !apiRedirectDiagnostics.redirects.isEmpty { Self.log.info( "Alibaba Token Plan redirects", metadata: [ "count": "\(apiRedirectDiagnostics.redirects.count)", "items": apiRedirectDiagnostics.redirects.joined(separator: " | "), ]) } guard let httpResponse = response as? HTTPURLResponse else { Self.log.error("Alibaba Token Plan response was not HTTP") throw AlibabaTokenPlanUsageError.networkError("Invalid response") } Self.log.info( "Alibaba Token Plan HTTP response", metadata: [ "status": "\(httpResponse.statusCode)", "bodyBytes": "\(data.count)", "contentType": httpResponse.value(forHTTPHeaderField: "Content-Type") ?? "none", ]) guard httpResponse.statusCode == 200 else { if httpResponse.statusCode == 401 || httpResponse.statusCode == 403 { throw AlibabaTokenPlanUsageError.loginRequired } Self.log.error("Alibaba Token Plan returned HTTP \(httpResponse.statusCode)") throw AlibabaTokenPlanUsageError.apiError("HTTP \(httpResponse.statusCode)") } return try self.parseUsageSnapshot(from: data, now: now) } static func resolveQuotaURL(environment: [String: String]) -> URL { if let override = AlibabaTokenPlanSettingsReader.quotaURL(environment: environment) { return override } if let host = AlibabaTokenPlanSettingsReader.hostOverride(environment: environment), let hostURL = self.quotaURL(from: host) { return hostURL } return self.defaultQuotaURL } static var defaultQuotaURL: URL { var components = URLComponents(string: Self.gatewayBaseURLString)! components.path = "/data/api.json" components.queryItems = [ URLQueryItem(name: "action", value: Self.subscriptionSummaryAction), URLQueryItem(name: "product", value: Self.bssServiceCode), URLQueryItem(name: "_tag", value: ""), ] return components.url! } static func parseUsageSnapshot(from data: Data, now: Date = Date()) throws -> AlibabaTokenPlanUsageSnapshot { guard !data.isEmpty else { throw AlibabaTokenPlanUsageError.parseFailed("Empty response body") } let object: Any do { object = try JSONSerialization.jsonObject(with: data, options: []) } catch { if self.isLikelyLoginHTML(data) { throw AlibabaTokenPlanUsageError.loginRequired } throw AlibabaTokenPlanUsageError.parseFailed("Invalid JSON response") } let expanded = self.expandedJSON(object) guard let dictionary = expanded as? [String: Any] else { throw AlibabaTokenPlanUsageError.parseFailed("Unexpected payload") } try self.throwIfErrorPayload(dictionary) let summary = self.findSubscriptionSummary(in: dictionary) ?? dictionary let total = self.anyDouble(for: Self.totalQuotaKeys, in: summary) let remaining = self.anyDouble(for: Self.remainingQuotaKeys, in: summary) let used = self.anyDouble(for: Self.usedQuotaKeys, in: summary) ?? total.flatMap { total in remaining.map { max(0, total - $0) } } let resetsAt = self.findResetDate(in: summary) ?? self.findResetDate(in: dictionary) let totalCount = self.anyDouble(for: Self.subscriptionCountKeys, in: summary) let planName = self.findPlanName(in: summary) ?? ((totalCount ?? 0) > 0 || total != nil ? "TOKEN PLAN" : nil) if planName == nil, total == nil, used == nil, remaining == nil, totalCount == nil { let diagnostics = self.payloadDiagnostics(payload: dictionary) Self.log.error("Alibaba Token Plan payload missing expected fields: \(diagnostics)") throw AlibabaTokenPlanUsageError.parseFailed("Missing token plan data (\(diagnostics))") } return AlibabaTokenPlanUsageSnapshot( planName: planName, usedQuota: used, totalQuota: total, remainingQuota: remaining, resetsAt: resetsAt, updatedAt: now) } private static func subscriptionSummaryRequestBody(secToken: String?) -> Data { let paramsObject = ["ProductCode": Self.tokenPlanProductCode] guard let paramsData = try? JSONSerialization.data(withJSONObject: paramsObject, options: []), let paramsString = String(data: paramsData, encoding: .utf8) else { return Data() } var components = URLComponents() var queryItems = [ URLQueryItem(name: "product", value: Self.bssServiceCode), URLQueryItem(name: "action", value: Self.subscriptionSummaryAction), URLQueryItem(name: "params", value: paramsString), URLQueryItem(name: "region", value: Self.currentRegionID), ] if let secToken, !secToken.isEmpty { queryItems.append(URLQueryItem(name: "sec_token", value: secToken)) } components.queryItems = queryItems return Data((components.percentEncodedQuery ?? "").utf8) } private static func resolveSECToken( dashboardCookieHeader: String, apiCookieHeader: String, environment: [String: String], session: URLSession) async -> String? { let cookieSECToken = self.extractCookieValue(name: "sec_token", from: dashboardCookieHeader) ?? self.extractCookieValue(name: "sec_token", from: apiCookieHeader) var request = URLRequest(url: self.dashboardURL(environment: environment)) request.httpMethod = "GET" request.timeoutInterval = 10 request.setValue(dashboardCookieHeader, forHTTPHeaderField: "Cookie") request.setValue(Self.safariLikeUserAgent, forHTTPHeaderField: "User-Agent") request.setValue( "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", forHTTPHeaderField: "Accept") if let (data, response) = try? await session.data(for: request), let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200, let html = String(data: data, encoding: .utf8), let token = self.extractSECToken(from: html) { Self.log.info( "Resolved Alibaba Token Plan sec_token from dashboard HTML", metadata: [ "dashboardHost": request.url?.host ?? "unknown", "htmlBytes": "\(data.count)", ]) return token } if let token = await self.fetchSECTokenFromUserInfo( cookieHeader: dashboardCookieHeader, environment: environment, session: session) { return token } if let cookieSECToken, !cookieSECToken.isEmpty { Self.log.info("Resolved Alibaba Token Plan sec_token from cookies") return cookieSECToken } Self.log.info( "Alibaba Token Plan sec_token missing; continuing with cookie-only request", metadata: [ "dashboardCookieNames": self.cookieNamesDescription(from: dashboardCookieHeader), "apiCookieNames": self.cookieNamesDescription(from: apiCookieHeader), ]) return nil } private static func fetchSECTokenFromUserInfo( cookieHeader: String, environment: [String: String], session: URLSession) async -> String? { let baseURL = self.consoleBaseURL(environment: environment) let userInfoURL = baseURL.appendingPathComponent("tool/user/info.json") var request = URLRequest(url: userInfoURL) request.httpMethod = "GET" request.timeoutInterval = 10 request.setValue(cookieHeader, forHTTPHeaderField: "Cookie") request.setValue(Self.safariLikeUserAgent, forHTTPHeaderField: "User-Agent") request.setValue("application/json, text/plain, */*", forHTTPHeaderField: "Accept") let referer = baseURL.absoluteString.hasSuffix("/") ? baseURL.absoluteString : "\(baseURL.absoluteString)/" request.setValue(referer, forHTTPHeaderField: "Referer") guard let (data, response) = try? await session.data(for: request), let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200, let object = try? JSONSerialization.jsonObject(with: data, options: []) else { return nil } let expanded = self.expandedJSON(object) guard let token = self.findFirstString(forKeys: ["secToken", "sec_token"], in: expanded), !token.isEmpty else { return nil } Self.log.info( "Resolved Alibaba Token Plan sec_token from user info", metadata: [ "userInfoHost": userInfoURL.host ?? "unknown", "bodyBytes": "\(data.count)", ]) return token } private static func consoleBaseURL(environment: [String: String]) -> URL { let dashboard = self.dashboardURL(environment: environment) var components = URLComponents() components.scheme = dashboard.scheme components.host = dashboard.host components.port = dashboard.port return components.url ?? URL(string: Self.dashboardOriginURLString)! } private static func quotaURL(from rawHost: String) -> URL? { let cleaned = AlibabaTokenPlanSettingsReader.cleaned(rawHost) guard let cleaned else { return nil } let base = URL(string: cleaned)?.scheme == nil ? URL(string: "https://\(cleaned)") : URL(string: cleaned) guard let base else { return nil } var components = URLComponents(url: base, resolvingAgainstBaseURL: false) let defaultComponents = URLComponents(url: Self.defaultQuotaURL, resolvingAgainstBaseURL: false) components?.path = "/data/api.json" components?.queryItems = defaultComponents?.queryItems return components?.url } static func dashboardURL(environment: [String: String]) -> URL { if let host = AlibabaTokenPlanSettingsReader.hostOverride(environment: environment), let base = URL(string: host)?.scheme == nil ? URL(string: "https://\(host)") : URL(string: host), var components = URLComponents(url: base, resolvingAgainstBaseURL: false), let dashboardComponents = URLComponents(url: dashboardURL, resolvingAgainstBaseURL: false) { components.path = dashboardComponents.path components.percentEncodedQuery = dashboardComponents.percentEncodedQuery components.fragment = dashboardComponents.fragment return components.url ?? self.dashboardURL } return Self.dashboardURL } private final class RedirectDiagnostics: NSObject, URLSessionTaskDelegate, @unchecked Sendable { private let cookieHeader: String var redirects: [String] = [] init(cookieHeader: String) { self.cookieHeader = cookieHeader } func urlSession( _: URLSession, task _: URLSessionTask, willPerformHTTPRedirection response: HTTPURLResponse, newRequest request: URLRequest, completionHandler: @escaping (URLRequest?) -> Void) { let from = AlibabaTokenPlanUsageFetcher.redactedURLDescription(response.url) let to = AlibabaTokenPlanUsageFetcher.redactedURLDescription(request.url) self.redirects.append("\(response.statusCode) \(from) -> \(to)") completionHandler(AlibabaTokenPlanUsageFetcher.redirectedRequest( response: response, request: request, cookieHeader: self.cookieHeader)) } } private static func redactedURLDescription(_ url: URL?) -> String { guard let url else { return "unknown" } var components = URLComponents(url: url, resolvingAgainstBaseURL: false) components?.query = nil components?.fragment = nil return components?.string ?? "\(url.scheme ?? "unknown")://\(url.host ?? "unknown")" } static func redirectedRequest( response: HTTPURLResponse, request: URLRequest, cookieHeader: String) -> URLRequest? { guard request.url?.scheme?.lowercased() == "https" else { return nil } var updated = request if self.shouldForwardRedirectCookies(from: response.url, to: request.url) { updated.setValue(cookieHeader, forHTTPHeaderField: "Cookie") } else { updated.setValue(nil, forHTTPHeaderField: "Cookie") } return updated } private static func shouldForwardRedirectCookies(from sourceURL: URL?, to targetURL: URL?) -> Bool { guard let sourceHost = sourceURL?.host?.lowercased(), let targetHost = targetURL?.host?.lowercased() else { return false } return sourceHost == targetHost } private static func throwIfErrorPayload(_ dictionary: [String: Any]) throws { if self.parseBool(dictionary["successResponse"]) == false { if let statusCode = self.findFirstInt(forKeys: ["statusCode", "status_code", "code"], in: dictionary), statusCode == 401 || statusCode == 403 { throw AlibabaTokenPlanUsageError.invalidCredentials } let code = self.findFirstString(forKeys: ["code", "status", "statusCode"], in: dictionary) let message = self.findFirstString(forKeys: ["message", "msg", "statusMessage"], in: dictionary) ?? code ?? "request was not successful" if self.isLoginOrTokenError(code: code, message: message) { throw AlibabaTokenPlanUsageError.loginRequired } throw AlibabaTokenPlanUsageError.apiError(message) } if self.findBoolValues(forKeys: ["Success", "success"], in: dictionary).contains(false) { let code = self.findFirstString(forKeys: ["Code", "code"], in: dictionary) let message = self.findFirstString(forKeys: ["Message", "message", "msg", "Code", "code"], in: dictionary) ?? "request was not successful" if self.isLoginOrTokenError(code: code, message: message) { throw AlibabaTokenPlanUsageError.loginRequired } throw AlibabaTokenPlanUsageError.apiError(message) } if let statusCode = self.findFirstInt(forKeys: ["statusCode", "status_code", "code"], in: dictionary), statusCode != 0, statusCode != 200 { let message = self.findFirstString( forKeys: ["statusMessage", "status_msg", "message", "msg"],