SHA256
Add HTTPS onboarding, origin policy, native recovery/settings, isolated web sign-in and file export. Portable production URL policy tests pass on Linux; all Swift sources parse. Xcode build and simulator validation remain required on macOS.
47 lines
2.1 KiB
Swift
47 lines
2.1 KiB
Swift
import Foundation
|
|
|
|
final class NoRedirect: NSObject, URLSessionTaskDelegate {
|
|
func urlSession(_ session: URLSession, task: URLSessionTask,
|
|
willPerformHTTPRedirection response: HTTPURLResponse,
|
|
newRequest request: URLRequest,
|
|
completionHandler: @escaping (URLRequest?) -> Void) {
|
|
completionHandler(nil)
|
|
}
|
|
}
|
|
|
|
enum ServerProbe {
|
|
static func check(_ server: ServerAddress) async throws {
|
|
let configuration = URLSessionConfiguration.ephemeral
|
|
configuration.httpCookieStorage = nil
|
|
configuration.urlCache = nil
|
|
configuration.timeoutIntervalForRequest = 10
|
|
configuration.timeoutIntervalForResource = 20
|
|
let session = URLSession(configuration: configuration, delegate: NoRedirect(), delegateQueue: nil)
|
|
defer { session.invalidateAndCancel() }
|
|
let version = try await json(session, server.url.appendingPathComponent("api/v1/version"))
|
|
guard version["app_name"] is String,
|
|
version["setup_required"] is Bool,
|
|
version["password_reset_enabled"] is Bool else { throw ConnectionError.incompatible }
|
|
let auth = try await json(session, server.url.appendingPathComponent("api/v1/auth/oidc/config"))
|
|
guard auth["enabled"] is Bool, auth["password_login_enabled"] is Bool else {
|
|
throw ConnectionError.incompatible
|
|
}
|
|
}
|
|
|
|
private static func json(_ session: URLSession, _ url: URL) async throws -> [String: Any] {
|
|
var request = URLRequest(url: url)
|
|
request.setValue("application/json", forHTTPHeaderField: "Accept")
|
|
let (stream, response) = try await session.bytes(for: request)
|
|
guard (response as? HTTPURLResponse)?.statusCode == 200 else { throw ConnectionError.unavailable }
|
|
var data = Data()
|
|
for try await byte in stream {
|
|
guard data.count < 65_536 else { throw ConnectionError.incompatible }
|
|
data.append(byte)
|
|
}
|
|
guard let result = try JSONSerialization.jsonObject(with: data) as? [String: Any] else {
|
|
throw ConnectionError.incompatible
|
|
}
|
|
return result
|
|
}
|
|
}
|