// // AdminConfig.swift // FTStore Admin // // Where this app talks to the FTStore Admin server. The build-time default // is injected via Info.plist and can be overridden at runtime. // // This is the *only* baked-in value in the whole app, and it is not a secret — // it is the same public domain FTStore already uses as its default source. No // token, no API key, no privileged path ships here; the server session the // operator signs in for is the entire security model. // import Foundation enum AdminConfig { private static let _baseURLOverrideKey = "admin.ftstoreAdminBaseURL" /// The FTStore Admin address, baked in so the app works out of the box /// without the operator configuring anything. static let fallbackBaseURL = "https://ftstore.app" /// Build-time default base URL: an Info.plist value (`FTStoreAdminBaseURL` ← /// `$(FTSTORE_ADMIN_BASE_URL)`) if one was injected at build time, else /// `fallbackBaseURL`. static var defaultBaseURL: String { let injected = ((Bundle.main.object(forInfoDictionaryKey: "FTStoreAdminBaseURL") as? String) ?? "") .trimmingCharacters(in: .whitespacesAndNewlines) return injected.isEmpty ? fallbackBaseURL : injected } /// Effective FTStore Admin base URL: a runtime override if one was set, else /// the build-time default. static var baseURL: String { get { if let override = UserDefaults.standard.string(forKey: _baseURLOverrideKey)? .trimmingCharacters(in: .whitespacesAndNewlines), !override.isEmpty { return override } return defaultBaseURL } set { UserDefaults.standard.set( newValue.trimmingCharacters(in: .whitespacesAndNewlines), forKey: _baseURLOverrideKey ) } } /// True once we have a usable base URL. static var isConfigured: Bool { guard let url = URL(string: baseURL) else { return false } return url.scheme == "https" || url.scheme == "http" } /// Build ``, tolerating trailing slashes on the base. static func endpointURL(path: String) -> URL? { var base = baseURL while base.hasSuffix("/") { base.removeLast() } guard !base.isEmpty else { return nil } let suffix = path.hasPrefix("/") ? path : "/" + path return URL(string: base + suffix) } /// Marketing version, for the About row and the User-Agent. static var appVersion: String { (Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) ?? "1.0" } /// Identifies FTStore Admin to the server. Deliberately *not* one of the /// `FTStore/…` / `FTStore-Manager/…` agents `web/src/lib/app-ua.ts` gates the /// feed and the self-update proxy on: FTStore Admin only calls session- /// authenticated `/api/*` routes, which are protected by the account rather /// than by a public string. Keeping it distinct means widening that gate is /// never accidentally required to make FTStore Admin work. static let userAgent: String = { let bundleId = Bundle.main.bundleIdentifier ?? "app.ftstore.admin" return "FTStore-Admin/\(appVersion) (\(bundleId))" }() }