import Vapor import Clairvoyant import ClairvoyantVapor import ClairvoyantBinaryCodable var deviceManager: DeviceManager! private var provider: VaporMetricProvider! private var asyncScheduler = MultiThreadedEventLoopGroup(numberOfThreads: 2) private let df: 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 configUrl = storageFolder.appendingPathComponent("config.json") let config = try Config(loadFrom: configUrl) let logFolder = config.logURL(possiblyRelativeTo: storageFolder) let monitor = MetricObserver(logFileFolder: logFolder, logMetricId: "sesame.log") MetricObserver.standard = monitor let status = Metric("sesame.status") try await status.update(.initializing) app.http.server.configuration.port = config.port let keyFile = storageFolder.appendingPathComponent(config.keyFileName) let (deviceKey, remoteKey) = try loadKeys(at: keyFile) deviceManager = DeviceManager(deviceKey: deviceKey, remoteKey: remoteKey, deviceTimeout: config.deviceTimeout, serverStatus: status) deviceManager.logger = app.logger routes(app) provider = .init(observer: monitor, accessManager: config.authenticationTokens) provider.asyncScheduler = asyncScheduler provider.registerRoutes(app) monitor.saveCurrentListOfMetricsToLogFolder() // Update the metric of the device and server status await deviceManager.updateDeviceConnectionMetrics() log("[\(df.string(from: Date()))] Server started") } public func shutdown() async { // Gracefully shut down by closing potentially open socket await deviceManager.removeDeviceConnection() do { try await asyncScheduler.shutdownGracefully() } catch { printAndFlush("Failed to shut down MultiThreadedEventLoopGroup: \(error)") } printAndFlush("[\(df.string(from: Date()))] Server shutdown") } 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 { fatalError("Invalid key data: Failed to convert hex to binary.") } guard key.count == SHA256.byteCount else { fatalError("Invalid key data: Length should be \(SHA256.byteCount), not \(key.count)") } return key } guard authContent.count == 2 else { fatalError("Invalid keys: Expected 2, found \(authContent.count)") } return (deviceKey: authContent[0], remoteKey: authContent[1]) } func log(_ message: String) { guard let observer = MetricObserver.standard else { printAndFlush(message) return } asyncScheduler.schedule { await observer.log(message) flushStdout() } }