import Vapor import Clairvoyant var deviceManager: DeviceManager! enum ServerError: Error { case invalidAuthenticationFileContent case invalidAuthenticationToken } private let dateFormatter: DateFormatter = { let df = DateFormatter() df.dateStyle = .short df.timeStyle = .short return df }() // configures your application public func configure(_ app: Application) async throws { let storageFolder = URL(fileURLWithPath: app.directory.resourcesDirectory) let logFolder = storageFolder.appendingPathComponent("logs") let accessManager = AccessTokenManager([]) let monitor = await MetricObserver( logFolder: logFolder, accessManager: accessManager, logMetricId: "sesame.log") MetricObserver.standard = monitor let status = try await Metric("sesame.status") try await status.update(.initializing) await monitor.registerRoutes(app) let configUrl = storageFolder.appendingPathComponent("config.json") let config = try Config(loadFrom: configUrl) config.authenticationTokens.map { $0.data(using: .utf8)! }.forEach(accessManager.add) app.http.server.configuration.port = config.port let keyFile = storageFolder.appendingPathComponent(config.keyFileName) let (deviceKey, remoteKey) = try loadKeys(at: keyFile) deviceManager = await DeviceManager( deviceKey: deviceKey, remoteKey: remoteKey, deviceTimeout: config.deviceTimeout) try routes(app) // Gracefully shut down by closing potentially open socket DispatchQueue.global(qos: .utility).asyncAfter(deadline: .now() + .seconds(5)) { _ = app.server.onShutdown.always { _ in deviceManager.removeDeviceConnection() } } try await status.update(.nominal) print("[\(dateFormatter.string(from: Date()))] Server started") } private func loadKeys(at url: URL) throws -> (deviceKey: Data, remoteKey: Data) { let authContent: [Data] = try String(contentsOf: url) .trimmingCharacters(in: .whitespacesAndNewlines) .components(separatedBy: "\n") .map { $0.trimmingCharacters(in: .whitespacesAndNewlines) } .map { guard let key = Data(fromHexEncodedString: $0) else { throw ServerError.invalidAuthenticationToken } guard key.count == SHA256.byteCount else { throw ServerError.invalidAuthenticationToken } return key } guard authContent.count == 2 else { throw ServerError.invalidAuthenticationFileContent } return (deviceKey: authContent[0], remoteKey: authContent[1]) } func log(_ message: String) { guard let observer = MetricObserver.standard else { print(message) return } Task { await observer.log(message) } }