import Foundation struct Config: Codable { /// The port where the server runs let port: Int /// The maximum size of the request body let maxBodySize: String /// 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? /// Serve files in the Public directory using Vapor let serveFiles: Bool /// Authentication tokens for remotes allowed to write let writers: [String] /** The folder where the data should be stored. If the folder is set to `nil`, then the `Resources` folder is used. */ let dataDirectory: String? func customDataDirectory(or publicDirectory: String) -> URL { guard let dataDirectory else { return URL(fileURLWithPath: publicDirectory) } return URL(fileURLWithPath: dataDirectory) } 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 Config { private static func file(in directory: URL) -> URL { directory.appendingPathComponent("config.json") } init(loadFrom directory: URL) { let configFileUrl = Config.file(in: directory) guard FileManager.default.fileExists(atPath: configFileUrl.path) else { print("No configuration found at \(configFileUrl.path)") exit(-1) } self.init(loadAt: configFileUrl) } private init(loadAt url: URL) { do { let configData = try Data(contentsOf: url) self = try JSONDecoder().decode(Config.self, from: configData) } catch { print("Failed to load configuration from \(url.path): \(error)") exit(-1) } } }