import Foundation struct Configuration { let serverPort: Int let mail: EMail? /** Use a database file and reduce logging. If this property is set to `false`, then an in-memory database is used with increased logging. */ let production: Bool struct EMail { /// The url to the root of the server let serverDomain: String /// SMTP server address let emailHostname: String /// username to login let email: String /// password to login let password: String /// The number of minutes until a password reset token is no longer valid let tokenExpiryDuration: Int } /// The authentication tokens to access the metrics let monitoringTokens: Set /// The path to the folder where the metric logs are stored /// /// If no path is provided, then a folder `logs` in the resources directory is created /// If the path is relative, then it is assumed relative to the resources directory let logPath: String? func logURL(possiblyRelativeTo resourcesDirectory: URL) -> URL { guard let logPath else { return resourcesDirectory.appendingPathComponent("logs") } guard !logPath.hasPrefix("/") else { return .init(fileURLWithPath: logPath) } return resourcesDirectory.appendingPathComponent(logPath) } } extension Configuration { init(loadFromUrl url: URL) throws { let data: Data do { data = try Data(contentsOf: url) } catch { log("Failed to load configuration data from \(url.path): \(error)") throw error } try self.init(data: data) } init(data: Data) throws { do { self = try JSONDecoder().decode(Configuration.self, from: data) } catch { log("Failed to decode configuration: \(error)") throw error } } } extension Configuration.EMail: Codable { } extension Configuration: Codable { }