The app is FTStore Admin. Its own AGENTS.md already sets the vocabulary — "the FTStore Admin server" for web/, "the FTStore Admin app" for this one — and then most of the comments in it said "the console" anyway. Three user-visible strings went with it: the lock screen, the menu item that locks it, and the biometric prompt. The storage-notification footer named the console too. Their catalog keys move with them, in every locale the app ships. Not "the app": inside these files that already means FTStore on somebody's phone, so a blind swap produced lines like "Dashboard and app only, never the app". AdminConsoleView is AdminHomeView. The Console/ directory keeps its name: the history of Console/*.swift runs back through manager/FTStore/Admin/ and is documented as doing so, which is worth more than a path nobody reads as a name. Also fills a translation gap this turned up — the status-change footer in the feedback detail was untranslated.
84 lines
3.0 KiB
Swift
84 lines
3.0 KiB
Swift
//
|
|
// 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 `<base><path>`, 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))"
|
|
}()
|
|
}
|