From 52cb76d4c8b87b430f38aa239abf91febd87b5b1 Mon Sep 17 00:00:00 2001 From: Christoph Hagen Date: Tue, 31 Jan 2023 19:10:33 +0100 Subject: [PATCH] Read config from file --- .gitignore | 1 + Resources/config_example.json | 6 +++++ Sources/App/Config.swift | 35 ++++++++++++++++++++++++++--- Sources/App/DeviceManager.swift | 7 ++++-- Sources/App/configure.swift | 40 ++++++++++++++++++++------------- 5 files changed, 69 insertions(+), 20 deletions(-) create mode 100644 Resources/config_example.json diff --git a/.gitignore b/.gitignore index af57f7d..0b4ff20 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ Package.resolved .swiftpm .build +Resources/config.json diff --git a/Resources/config_example.json b/Resources/config_example.json new file mode 100644 index 0000000..dd5eef5 --- /dev/null +++ b/Resources/config_example.json @@ -0,0 +1,6 @@ +{ + "port": 6003, + "keyFileName": "keys", + "deviceTimeout": 20, + "authenticationTokens" : [], +} diff --git a/Sources/App/Config.swift b/Sources/App/Config.swift index efe47f3..10ce33e 100644 --- a/Sources/App/Config.swift +++ b/Sources/App/Config.swift @@ -3,11 +3,40 @@ import Foundation struct Config { /// The port where the server runs - static let port = 6003 + let port: Int /// The name of the file in the `Resources` folder containing the device authentication token - static let keyFileName = "keys" + let keyFileName: String /// The seconds to wait for a response from the device - static let deviceTimeout: Int64 = 20 + let deviceTimeout: Int64 + + /// The authentication tokens to use for monitoring of the service + let authenticationTokens: Set +} + +extension Config: Codable { + +} + +extension Config { + + init(loadFrom url: URL) throws { + guard FileManager.default.fileExists(atPath: url.path) else { + fatalError("No configuration file found") + } + let data: Data + do { + data = try Data(contentsOf: url) + } catch { + print("Failed to read config data: \(error)") + throw error + } + do { + self = try JSONDecoder().decode(Config.self, from: data) + } catch { + print("Failed to decode config data: \(error)") + throw error + } + } } diff --git a/Sources/App/DeviceManager.swift b/Sources/App/DeviceManager.swift index b2659d3..91d5492 100644 --- a/Sources/App/DeviceManager.swift +++ b/Sources/App/DeviceManager.swift @@ -17,6 +17,8 @@ final class DeviceManager { var deviceIsAuthenticated = false private var isOpeningNewConnection = false + + private let deviceTimeout: Int64 /// Indicator for device availability var deviceIsConnected: Bool { @@ -26,9 +28,10 @@ final class DeviceManager { /// A promise to finish the request once the device responds or times out private var requestInProgress: EventLoopPromise? - init(deviceKey: Data, remoteKey: Data) { + init(deviceKey: Data, remoteKey: Data, deviceTimeout: Int64) { self.deviceKey = deviceKey self.remoteKey = remoteKey + self.deviceTimeout = deviceTimeout } // MARK: API @@ -47,7 +50,7 @@ final class DeviceManager { } requestInProgress = eventLoop.makePromise(of: DeviceResponse.self) socket.send(message.bytes, promise: nil) - eventLoop.scheduleTask(in: .seconds(Config.deviceTimeout)) { [weak self] in + eventLoop.scheduleTask(in: .seconds(deviceTimeout)) { [weak self] in guard let promise = self?.requestInProgress else { return } diff --git a/Sources/App/configure.swift b/Sources/App/configure.swift index 60af647..6a2e9a1 100755 --- a/Sources/App/configure.swift +++ b/Sources/App/configure.swift @@ -9,11 +9,31 @@ enum ServerError: Error { // configures your application public func configure(_ app: Application) throws { - app.http.server.configuration.port = Config.port - let storageFolder = URL(fileURLWithPath: app.directory.resourcesDirectory) - let keyFile = storageFolder.appendingPathComponent(Config.keyFileName) - let authContent: [Data] = try String(contentsOf: keyFile) + let logFolder = storageFolder.appendingPathComponent("logs") + + + let configUrl = storageFolder.appendingPathComponent("config.json") + let config = try Config(loadFrom: configUrl) + + 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) + 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() + } + } +} + +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) } @@ -29,15 +49,5 @@ public func configure(_ app: Application) throws { guard authContent.count == 2 else { throw ServerError.invalidAuthenticationFileContent } - let deviceKey = authContent[0] - let remoteKey = authContent[1] - deviceManager = DeviceManager(deviceKey: deviceKey, remoteKey: remoteKey) - 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() - } - } + return (deviceKey: authContent[0], remoteKey: authContent[1]) }