Merge Conflicts (#41)
Some checks failed
Production Readiness / backend-contracts (push) Failing after 1m47s
Production Readiness / webos-typecheck (push) Successful in 1m57s
Production Readiness / ipad-parse (push) Successful in 1m32s

Co-authored-by: Sayan Datta <sayan@Sayans-MacBook-Air.local>
Reviewed-on: #41
This commit was merged in pull request #41.
This commit is contained in:
2026-04-28 11:32:56 +05:30
parent 61258978e1
commit 7ee51543d9
158 changed files with 23889 additions and 87196 deletions

View File

@@ -0,0 +1,302 @@
import Foundation
import Security
/// Central app configuration.
/// Build settings remain the fallback, but production installs should prefer
/// runtime configuration stored on-device.
enum AppConfig {
private static let runtimeBaseURLKey = "velocity.runtime.base_url"
private static let runtimeDreamWeaverBaseURLKey = "velocity.runtime.dream_weaver_base_url"
private static let runtimeDreamWeaverAPIKeyKey = "velocity.runtime.dream_weaver_api_key"
private static let runtimeEmailKey = "velocity.runtime.email"
private static let runtimePasswordKey = "velocity.runtime.password"
private static let runtimeBearerTokenKey = "velocity.runtime.bearer_token"
private static let runtimeAccessTokenKey = "velocity.runtime.access_token"
private static let runtimeAccessTokenExpiresAtKey = "velocity.runtime.access_token_expires_at"
private static let keychainService = "com.desineuron.velocity.ipad.session"
static func parsedValue(from infoDictionary: [String: Any]?, key: String) -> String? {
let raw = infoDictionary?[key] as? String
return sanitizedValue(raw, key: key)
}
static func isLiveConfigured(
bearerToken: String?,
email: String?,
password: String?
) -> Bool {
bearerToken != nil || (email != nil && password != nil)
}
static func authModeDescription(
bearerToken: String?,
email: String?,
password: String?
) -> String {
if bearerToken != nil {
return "Bearer token"
}
if email != nil && password != nil {
return "Email/password"
}
return "Credentials required"
}
private static func value(for key: String) -> String? {
parsedValue(from: Bundle.main.infoDictionary, key: key)
}
private static func sanitizedValue(_ raw: String?, key: String) -> String? {
guard let raw else {
return nil
}
let trimmed = raw.trimmingCharacters(in: .whitespacesAndNewlines)
if trimmed.isEmpty || trimmed == "$(\(key))" {
return nil
}
return trimmed
}
/// Base URL for the Velocity backend / gateway.
static var baseURL: String {
runtimeBaseURL ?? value(for: "BASE_URL") ?? "https://velocity.desineuron.in/api"
}
/// Dedicated Dream Weaver gateway endpoint when configured; otherwise
/// generation falls back to the main backend endpoint.
static var dreamWeaverBaseURL: String {
configuredDreamWeaverBaseURL ?? originBaseURL(from: baseURL)
}
static var usesDedicatedDreamWeaverBaseURL: Bool {
guard let configuredDreamWeaverBaseURL else {
return false
}
return configuredDreamWeaverBaseURL != baseURL
}
static var dreamWeaverAPIKey: String? {
runtimeDreamWeaverAPIKey ?? value(for: "DREAM_WEAVER_API_KEY")
}
static var apiEmail: String? {
runtimeEmail ?? value(for: "API_EMAIL")
}
static var apiPassword: String? {
runtimePassword ?? value(for: "API_PASSWORD")
}
static var apiBearerToken: String? {
runtimeBearerToken ?? value(for: "API_BEARER_TOKEN")
}
static var apiAccessToken: String? {
guard let expiresAt = runtimeAccessTokenExpiresAt, expiresAt > Date().addingTimeInterval(60) else {
try? clearStoredAccessToken()
return nil
}
return secret(account: runtimeAccessTokenKey)
}
static var isLiveConfigured: Bool {
isLiveConfigured(
bearerToken: apiBearerToken,
email: apiEmail,
password: apiPassword
)
}
static var authModeDescription: String {
authModeDescription(
bearerToken: apiBearerToken,
email: apiEmail,
password: apiPassword
)
}
static var hasStoredRuntimeConfiguration: Bool {
runtimeBaseURL != nil ||
runtimeDreamWeaverBaseURL != nil ||
runtimeEmail != nil ||
runtimePassword != nil ||
runtimeBearerToken != nil
}
static func currentSessionConfiguration() -> AppSessionConfiguration {
AppSessionConfiguration(
baseURL: baseURL,
dreamWeaverBaseURL: dreamWeaverBaseURL,
usesDedicatedDreamWeaverBaseURL: usesDedicatedDreamWeaverBaseURL,
hasDreamWeaverAPIKey: dreamWeaverAPIKey != nil,
email: apiEmail,
hasPassword: apiPassword != nil,
hasBearerToken: apiBearerToken != nil,
source: hasStoredRuntimeConfiguration ? .secureDeviceStorage : .buildConfiguration
)
}
static func saveRuntimeConfiguration(
baseURL: String,
dreamWeaverBaseURL: String?,
dreamWeaverAPIKey: String?,
email: String?,
password: String?,
bearerToken: String?
) throws {
UserDefaults.standard.set(baseURL, forKey: runtimeBaseURLKey)
if let dreamWeaverBaseURL {
UserDefaults.standard.set(dreamWeaverBaseURL, forKey: runtimeDreamWeaverBaseURLKey)
} else {
UserDefaults.standard.removeObject(forKey: runtimeDreamWeaverBaseURLKey)
}
if let email {
UserDefaults.standard.set(email, forKey: runtimeEmailKey)
} else {
UserDefaults.standard.removeObject(forKey: runtimeEmailKey)
}
try storeSecret(dreamWeaverAPIKey, account: runtimeDreamWeaverAPIKeyKey)
try storeSecret(password, account: runtimePasswordKey)
try storeSecret(bearerToken, account: runtimeBearerTokenKey)
try clearStoredAccessToken()
}
static func clearStoredRuntimeConfiguration() throws {
UserDefaults.standard.removeObject(forKey: runtimeBaseURLKey)
UserDefaults.standard.removeObject(forKey: runtimeDreamWeaverBaseURLKey)
UserDefaults.standard.removeObject(forKey: runtimeEmailKey)
try deleteSecret(account: runtimeDreamWeaverAPIKeyKey)
try deleteSecret(account: runtimePasswordKey)
try deleteSecret(account: runtimeBearerTokenKey)
try clearStoredAccessToken()
}
static func saveAccessToken(_ token: String, expiresIn: Int?) throws {
try storeSecret(token, account: runtimeAccessTokenKey)
let lifetime = TimeInterval(max(expiresIn ?? 28_800, 60))
UserDefaults.standard.set(Date().addingTimeInterval(lifetime).timeIntervalSince1970, forKey: runtimeAccessTokenExpiresAtKey)
}
static func clearStoredAccessToken() throws {
UserDefaults.standard.removeObject(forKey: runtimeAccessTokenExpiresAtKey)
try deleteSecret(account: runtimeAccessTokenKey)
}
private static var runtimeBaseURL: String? {
sanitizedValue(UserDefaults.standard.string(forKey: runtimeBaseURLKey), key: runtimeBaseURLKey)
}
private static var configuredDreamWeaverBaseURL: String? {
runtimeDreamWeaverBaseURL ?? value(for: "DREAM_WEAVER_BASE_URL")
}
private static var runtimeDreamWeaverBaseURL: String? {
sanitizedValue(
UserDefaults.standard.string(forKey: runtimeDreamWeaverBaseURLKey),
key: runtimeDreamWeaverBaseURLKey
)
}
private static var runtimeDreamWeaverAPIKey: String? {
secret(account: runtimeDreamWeaverAPIKeyKey)
}
private static var runtimeEmail: String? {
sanitizedValue(UserDefaults.standard.string(forKey: runtimeEmailKey), key: runtimeEmailKey)
}
private static var runtimePassword: String? {
secret(account: runtimePasswordKey)
}
private static var runtimeBearerToken: String? {
secret(account: runtimeBearerTokenKey)
}
private static var runtimeAccessTokenExpiresAt: Date? {
let rawValue = UserDefaults.standard.double(forKey: runtimeAccessTokenExpiresAtKey)
guard rawValue > 0 else {
return nil
}
return Date(timeIntervalSince1970: rawValue)
}
private static func originBaseURL(from rawValue: String) -> String {
guard var components = URLComponents(string: rawValue) else {
return rawValue
}
components.path = ""
components.query = nil
components.fragment = nil
return components.string ?? rawValue
}
private static func secret(account: String) -> String? {
let query: [CFString: Any] = [
kSecClass: kSecClassGenericPassword,
kSecAttrService: keychainService,
kSecAttrAccount: account,
kSecReturnData: true,
kSecMatchLimit: kSecMatchLimitOne
]
var result: AnyObject?
let status = SecItemCopyMatching(query as CFDictionary, &result)
guard status == errSecSuccess else {
return nil
}
guard let data = result as? Data else {
return nil
}
return String(data: data, encoding: .utf8)
}
private static func storeSecret(_ value: String?, account: String) throws {
if let value, let data = value.data(using: .utf8) {
let query: [CFString: Any] = [
kSecClass: kSecClassGenericPassword,
kSecAttrService: keychainService,
kSecAttrAccount: account
]
let attributes: [CFString: Any] = [
kSecValueData: data
]
let status = SecItemUpdate(query as CFDictionary, attributes as CFDictionary)
if status == errSecSuccess {
return
}
if status == errSecItemNotFound {
var addQuery = query
addQuery[kSecValueData] = data
let addStatus = SecItemAdd(addQuery as CFDictionary, nil)
guard addStatus == errSecSuccess else {
throw SessionPersistenceError.keychainWriteFailed(addStatus)
}
return
}
throw SessionPersistenceError.keychainWriteFailed(status)
}
try deleteSecret(account: account)
}
private static func deleteSecret(account: String) throws {
let query: [CFString: Any] = [
kSecClass: kSecClassGenericPassword,
kSecAttrService: keychainService,
kSecAttrAccount: account
]
let status = SecItemDelete(query as CFDictionary)
guard status == errSecSuccess || status == errSecItemNotFound else {
throw SessionPersistenceError.keychainDeleteFailed(status)
}
}
}

View File

@@ -0,0 +1,247 @@
import Foundation
enum SessionAuthMode: String, CaseIterable, Identifiable {
case emailPassword = "Email/password"
case bearerToken = "Bearer token"
var id: String { rawValue }
}
enum SessionConfigurationSource: String {
case buildConfiguration = "Build configuration"
case secureDeviceStorage = "Secure device storage"
}
struct AppSessionConfiguration: Equatable {
let baseURL: String
let dreamWeaverBaseURL: String
let usesDedicatedDreamWeaverBaseURL: Bool
let hasDreamWeaverAPIKey: Bool
let email: String?
let hasPassword: Bool
let hasBearerToken: Bool
let source: SessionConfigurationSource
var authMode: SessionAuthMode {
hasBearerToken ? .bearerToken : .emailPassword
}
var isConfigured: Bool {
hasBearerToken || (email != nil && hasPassword)
}
var authModeDescription: String {
if hasBearerToken {
return "Bearer token"
}
if email != nil && hasPassword {
return "Email/password"
}
return "Credentials required"
}
var operatorIdentity: String {
if let email, !email.isEmpty {
return email
}
if hasBearerToken {
return "Token authenticated operator"
}
return "Unconfigured operator"
}
var dreamWeaverEndpointModeDescription: String {
usesDedicatedDreamWeaverBaseURL ? "Dedicated gateway" : "Shared with backend"
}
var dreamWeaverAuthenticationDescription: String {
hasDreamWeaverAPIKey ? "API key configured" : "No gateway key configured"
}
}
struct SessionConfigurationDraft: Equatable {
var baseURL: String
var dreamWeaverBaseURL: String
var dreamWeaverAPIKey: String
var authMode: SessionAuthMode
var email: String
var password: String
var bearerToken: String
var existingDreamWeaverAPIKeyAvailable: Bool
var existingPasswordAvailable: Bool
var existingBearerTokenAvailable: Bool
var baselineEmail: String?
var trimmedBaseURL: String? {
Self.trimmedValue(baseURL)
}
var trimmedEmail: String? {
Self.trimmedValue(email)
}
var trimmedPassword: String? {
Self.trimmedValue(password)
}
var trimmedBearerToken: String? {
Self.trimmedValue(bearerToken)
}
var normalizedBaseURL: String? {
Self.normalizedHTTPSOrigin(from: trimmedBaseURL)
}
var trimmedDreamWeaverBaseURL: String? {
Self.trimmedValue(dreamWeaverBaseURL)
}
var normalizedDreamWeaverBaseURL: String? {
Self.normalizedHTTPSOrigin(from: trimmedDreamWeaverBaseURL)
}
var trimmedDreamWeaverAPIKey: String? {
Self.trimmedValue(dreamWeaverAPIKey)
}
func validationErrors() -> [String] {
var errors: [String] = []
guard let trimmedBaseURL else {
errors.append("Backend endpoint is required.")
return errors
}
guard URLComponents(string: trimmedBaseURL) != nil else {
errors.append("Backend endpoint must be a valid URL.")
return errors
}
guard normalizedBaseURL != nil else {
errors.append("Backend endpoint must be an HTTPS API base like https://velocity.desineuron.in/api.")
return errors
}
if let trimmedDreamWeaverBaseURL {
guard URLComponents(string: trimmedDreamWeaverBaseURL) != nil else {
errors.append("Dream Weaver endpoint must be a valid URL.")
return errors
}
guard normalizedDreamWeaverBaseURL != nil else {
errors.append("Dream Weaver endpoint must be an HTTPS origin like https://dreamweaver.desineuron.in.")
return errors
}
}
switch authMode {
case .emailPassword:
guard let trimmedEmail else {
errors.append("Operator email is required for email/password login.")
break
}
guard trimmedEmail.contains("@"), trimmedEmail.contains(".") else {
errors.append("Operator email must look like a valid email address.")
break
}
if trimmedPassword == nil &&
!(existingPasswordAvailable && trimmedEmail.caseInsensitiveCompare(baselineEmail ?? "") == .orderedSame) {
errors.append("Password is required for email/password login.")
}
case .bearerToken:
if trimmedBearerToken == nil && !existingBearerTokenAvailable {
errors.append("Bearer token is required when token auth is selected.")
}
}
return errors
}
func resolvedEmail(existingEmail: String?) -> String? {
guard authMode == .emailPassword else { return nil }
return trimmedEmail ?? existingEmail
}
func resolvedPassword(existingPassword: String?) -> String? {
guard authMode == .emailPassword else { return nil }
if let trimmedPassword {
return trimmedPassword
}
guard existingPasswordAvailable else {
return nil
}
guard trimmedEmail?.caseInsensitiveCompare(baselineEmail ?? "") == .orderedSame else {
return nil
}
return existingPassword
}
func resolvedBearerToken(existingToken: String?) -> String? {
guard authMode == .bearerToken else { return nil }
return trimmedBearerToken ?? (existingBearerTokenAvailable ? existingToken : nil)
}
func resolvedDreamWeaverBaseURL(normalizedBaseURL: String) -> String? {
normalizedDreamWeaverBaseURL
}
func resolvedDreamWeaverAPIKey(existingKey: String?) -> String? {
trimmedDreamWeaverAPIKey ?? (existingDreamWeaverAPIKeyAvailable ? existingKey : nil)
}
private static func trimmedValue(_ value: String?) -> String? {
guard let value else {
return nil
}
let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines)
return trimmed.isEmpty ? nil : trimmed
}
private static func normalizedHTTPSOrigin(from raw: String?) -> String? {
guard let raw else {
return nil
}
guard var components = URLComponents(string: raw) else {
return nil
}
guard let scheme = components.scheme?.lowercased(),
let host = components.host?.lowercased() else {
return nil
}
guard scheme == "https" else {
return nil
}
guard components.query == nil, components.fragment == nil else {
return nil
}
let path = components.path.trimmingCharacters(in: .whitespacesAndNewlines)
let normalizedPath = path == "/api/" ? "/api" : path
guard normalizedPath.isEmpty || normalizedPath == "/" || normalizedPath == "/api" else {
return nil
}
components.scheme = scheme
components.host = host
components.path = normalizedPath == "/api" ? "/api" : ""
components.query = nil
components.fragment = nil
return components.string
}
}
enum SessionPersistenceError: LocalizedError {
case keychainWriteFailed(OSStatus)
case keychainDeleteFailed(OSStatus)
var errorDescription: String? {
switch self {
case .keychainWriteFailed(let status):
return "Velocity could not save credentials securely on this iPad. Keychain status: \(status)."
case .keychainDeleteFailed(let status):
return "Velocity could not clear stored credentials from this iPad. Keychain status: \(status)."
}
}
}

View File

@@ -0,0 +1,204 @@
import Foundation
import Observation
@MainActor
@Observable
final class SessionStore {
static let shared = SessionStore()
private init() {
reloadFromPersistedConfiguration()
}
var currentConfiguration = AppConfig.currentSessionConfiguration()
var draftBaseURL = ""
var draftDreamWeaverBaseURL = ""
var draftDreamWeaverAPIKey = ""
var draftAuthMode: SessionAuthMode = .emailPassword
var draftEmail = ""
var draftPassword = ""
var draftBearerToken = ""
var isSaving = false
var statusMessage: String?
var errorMessage: String?
private var existingPasswordAvailable = false
private var existingBearerTokenAvailable = false
private var existingDreamWeaverAPIKeyAvailable = false
private var baselineEmail: String?
var isConfigured: Bool {
currentConfiguration.isConfigured
}
var authModeDescription: String {
currentConfiguration.authModeDescription
}
var operatorIdentity: String {
currentConfiguration.operatorIdentity
}
var endpointDisplay: String {
currentConfiguration.baseURL
}
var dreamWeaverEndpointDisplay: String {
currentConfiguration.dreamWeaverBaseURL
}
var dreamWeaverEndpointModeDescription: String {
currentConfiguration.dreamWeaverEndpointModeDescription
}
var dreamWeaverAuthenticationDescription: String {
currentConfiguration.dreamWeaverAuthenticationDescription
}
var configurationSourceDescription: String {
currentConfiguration.source.rawValue
}
var isUsingStoredRuntimeConfiguration: Bool {
currentConfiguration.source == .secureDeviceStorage
}
var hasUnsavedChanges: Bool {
draftBaseURL != currentConfiguration.baseURL ||
draftDreamWeaverBaseURL != persistedDreamWeaverDraftValue ||
!draftDreamWeaverAPIKey.isEmpty ||
draftAuthMode != currentConfiguration.authMode ||
draftEmail != (currentConfiguration.email ?? "") ||
!draftPassword.isEmpty ||
!draftBearerToken.isEmpty
}
func reloadFromPersistedConfiguration() {
currentConfiguration = AppConfig.currentSessionConfiguration()
draftBaseURL = currentConfiguration.baseURL
draftDreamWeaverBaseURL = persistedDreamWeaverDraftValue
draftDreamWeaverAPIKey = ""
draftAuthMode = currentConfiguration.authMode
draftEmail = currentConfiguration.email ?? ""
draftPassword = ""
draftBearerToken = ""
existingDreamWeaverAPIKeyAvailable = currentConfiguration.hasDreamWeaverAPIKey
existingPasswordAvailable = currentConfiguration.hasPassword
existingBearerTokenAvailable = currentConfiguration.hasBearerToken
baselineEmail = currentConfiguration.email
}
func discardDraftChanges() {
errorMessage = nil
statusMessage = nil
reloadFromPersistedConfiguration()
}
func saveDraft() async {
errorMessage = nil
statusMessage = nil
let draft = SessionConfigurationDraft(
baseURL: draftBaseURL,
dreamWeaverBaseURL: draftDreamWeaverBaseURL,
dreamWeaverAPIKey: draftDreamWeaverAPIKey,
authMode: draftAuthMode,
email: draftEmail,
password: draftPassword,
bearerToken: draftBearerToken,
existingDreamWeaverAPIKeyAvailable: existingDreamWeaverAPIKeyAvailable,
existingPasswordAvailable: existingPasswordAvailable,
existingBearerTokenAvailable: existingBearerTokenAvailable,
baselineEmail: baselineEmail
)
let errors = draft.validationErrors()
guard errors.isEmpty else {
errorMessage = errors.joined(separator: " ")
return
}
guard let normalizedBaseURL = draft.normalizedBaseURL else {
errorMessage = "Backend endpoint must be a valid HTTPS origin."
return
}
isSaving = true
do {
try AppConfig.saveRuntimeConfiguration(
baseURL: normalizedBaseURL,
dreamWeaverBaseURL: draft.resolvedDreamWeaverBaseURL(normalizedBaseURL: normalizedBaseURL),
dreamWeaverAPIKey: draft.resolvedDreamWeaverAPIKey(existingKey: AppConfig.dreamWeaverAPIKey),
email: draft.resolvedEmail(existingEmail: currentConfiguration.email),
password: draft.resolvedPassword(existingPassword: AppConfig.apiPassword),
bearerToken: draft.resolvedBearerToken(existingToken: AppConfig.apiBearerToken)
)
await VelocityAPIClient.shared.resetSession()
AppStore.shared.resetLiveData()
reloadFromPersistedConfiguration()
await AppStore.shared.refresh()
let dreamWeaverHealthy = await ComfyClient.shared.checkHealth()
statusMessage = verificationStatusMessage(
successPrefix: "Configuration saved.",
backendRefreshError: AppStore.shared.errorMessage,
dreamWeaverHealthy: dreamWeaverHealthy
)
} catch {
errorMessage = error.localizedDescription
}
isSaving = false
}
func clearStoredConfiguration() async {
errorMessage = nil
statusMessage = nil
isSaving = true
do {
try AppConfig.clearStoredRuntimeConfiguration()
await VelocityAPIClient.shared.resetSession()
AppStore.shared.resetLiveData()
reloadFromPersistedConfiguration()
if currentConfiguration.isConfigured {
await AppStore.shared.refresh()
let dreamWeaverHealthy = await ComfyClient.shared.checkHealth()
statusMessage = verificationStatusMessage(
successPrefix: "Stored override cleared. Velocity is now using the build configuration.",
backendRefreshError: AppStore.shared.errorMessage,
dreamWeaverHealthy: dreamWeaverHealthy
)
} else {
statusMessage = "Stored session cleared. This iPad now requires runtime configuration before live data can load."
}
} catch {
errorMessage = error.localizedDescription
}
isSaving = false
}
private var persistedDreamWeaverDraftValue: String {
currentConfiguration.usesDedicatedDreamWeaverBaseURL ? currentConfiguration.dreamWeaverBaseURL : ""
}
private func verificationStatusMessage(
successPrefix: String,
backendRefreshError: String?,
dreamWeaverHealthy: Bool
) -> String {
switch (backendRefreshError, dreamWeaverHealthy) {
case (nil, true):
return "\(successPrefix) Core backend refresh and Dream Weaver gateway probe both succeeded."
case (let backendRefreshError?, true):
return "\(successPrefix) Core backend refresh failed: \(backendRefreshError) Dream Weaver gateway probe succeeded."
case (nil, false):
return "\(successPrefix) Core backend refresh succeeded, but the Dream Weaver gateway probe failed. Verify the dedicated generation endpoint and routing."
case (let backendRefreshError?, false):
return "\(successPrefix) Core backend refresh failed: \(backendRefreshError) Dream Weaver gateway probe also failed."
}
}
}