2022-01-23 20:49:06 +01:00
|
|
|
import Vapor
|
2023-01-31 19:10:57 +01:00
|
|
|
import Clairvoyant
|
2023-09-07 14:13:28 +02:00
|
|
|
import ClairvoyantVapor
|
|
|
|
import ClairvoyantBinaryCodable
|
2022-01-23 20:49:06 +01:00
|
|
|
|
2022-04-07 23:53:25 +02:00
|
|
|
var deviceManager: DeviceManager!
|
2022-01-24 17:17:06 +01:00
|
|
|
|
2023-09-07 14:13:28 +02:00
|
|
|
private var provider: VaporMetricProvider!
|
2023-11-28 11:26:43 +01:00
|
|
|
private var status: Metric<ServerStatus>!
|
2023-09-07 14:13:28 +02:00
|
|
|
|
2023-11-27 18:17:00 +01:00
|
|
|
private var asyncScheduler = MultiThreadedEventLoopGroup(numberOfThreads: 2)
|
2023-10-01 19:26:31 +02:00
|
|
|
|
2023-11-10 15:08:33 +01:00
|
|
|
private let df: DateFormatter = {
|
|
|
|
let df = DateFormatter()
|
|
|
|
df.dateStyle = .short
|
|
|
|
df.timeStyle = .short
|
|
|
|
return df
|
|
|
|
}()
|
|
|
|
|
2022-05-01 13:12:16 +02:00
|
|
|
enum ServerError: Error {
|
|
|
|
case invalidAuthenticationFileContent
|
2022-05-01 13:28:06 +02:00
|
|
|
case invalidAuthenticationToken
|
2022-05-01 13:12:16 +02:00
|
|
|
}
|
|
|
|
|
2023-11-28 11:26:43 +01:00
|
|
|
private func updateStatus(_ newStatus: ServerStatus) {
|
|
|
|
asyncScheduler.schedule {
|
|
|
|
do {
|
|
|
|
try await status.update(newStatus)
|
|
|
|
} catch {
|
|
|
|
print("Failed to update server status: \(error)")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-23 20:49:06 +01:00
|
|
|
// configures your application
|
2023-11-08 10:24:50 +01:00
|
|
|
public func configure(_ app: Application) throws {
|
2022-01-24 17:17:06 +01:00
|
|
|
let storageFolder = URL(fileURLWithPath: app.directory.resourcesDirectory)
|
2023-11-22 11:48:50 +01:00
|
|
|
|
|
|
|
let configUrl = storageFolder.appendingPathComponent("config.json")
|
|
|
|
let config = try Config(loadFrom: configUrl)
|
|
|
|
|
|
|
|
let logFolder = config.logURL(possiblyRelativeTo: storageFolder)
|
2023-09-07 15:33:10 +02:00
|
|
|
|
2023-09-07 14:13:28 +02:00
|
|
|
let monitor = MetricObserver(logFileFolder: logFolder, logMetricId: "sesame.log")
|
2023-01-31 19:10:57 +01:00
|
|
|
MetricObserver.standard = monitor
|
|
|
|
|
2023-11-28 11:26:43 +01:00
|
|
|
status = Metric<ServerStatus>("sesame.status")
|
|
|
|
updateStatus(.initializing)
|
2023-01-31 19:10:33 +01:00
|
|
|
|
|
|
|
app.http.server.configuration.port = config.port
|
|
|
|
|
|
|
|
let keyFile = storageFolder.appendingPathComponent(config.keyFileName)
|
|
|
|
|
|
|
|
let (deviceKey, remoteKey) = try loadKeys(at: keyFile)
|
2023-11-10 15:01:37 +01:00
|
|
|
deviceManager = DeviceManager(deviceKey: deviceKey, remoteKey: remoteKey, deviceTimeout: config.deviceTimeout)
|
2023-02-17 00:09:51 +01:00
|
|
|
|
2023-01-31 19:10:33 +01:00
|
|
|
try routes(app)
|
2023-09-07 14:13:28 +02:00
|
|
|
|
|
|
|
provider = .init(observer: monitor, accessManager: config.authenticationTokens)
|
2023-10-01 19:26:31 +02:00
|
|
|
provider.asyncScheduler = asyncScheduler
|
2023-09-07 14:13:28 +02:00
|
|
|
provider.registerRoutes(app)
|
2023-11-28 08:46:26 +01:00
|
|
|
monitor.saveCurrentListOfMetricsToLogFolder()
|
2023-01-31 19:10:33 +01:00
|
|
|
|
2023-11-28 11:26:43 +01:00
|
|
|
updateStatus(.nominal)
|
|
|
|
|
|
|
|
// Update the metric of the device status to ensure that it is accurate
|
2023-11-08 10:24:50 +01:00
|
|
|
asyncScheduler.schedule {
|
2023-11-10 15:01:37 +01:00
|
|
|
await deviceManager.updateDeviceConnectionMetric()
|
2023-11-08 10:24:50 +01:00
|
|
|
}
|
|
|
|
|
2023-11-27 18:17:00 +01:00
|
|
|
log("[\(df.string(from: Date()))] Server started")
|
2023-01-31 19:10:33 +01:00
|
|
|
}
|
|
|
|
|
2023-11-27 18:17:00 +01:00
|
|
|
public func shutdown() {
|
2023-11-28 11:20:29 +01:00
|
|
|
print("[\(df.string(from: Date()))] Server shutdown")
|
|
|
|
asyncScheduler.schedule {
|
|
|
|
// Gracefully shut down by closing potentially open socket
|
|
|
|
await deviceManager.removeDeviceConnection()
|
2023-11-28 11:26:43 +01:00
|
|
|
do {
|
|
|
|
try await asyncScheduler.shutdownGracefully()
|
|
|
|
} catch {
|
|
|
|
print("Failed to shut down MultiThreadedEventLoopGroup: \(error)")
|
|
|
|
}
|
2023-11-28 11:20:29 +01:00
|
|
|
}
|
2023-11-27 18:17:00 +01:00
|
|
|
}
|
|
|
|
|
2023-01-31 19:10:33 +01:00
|
|
|
private func loadKeys(at url: URL) throws -> (deviceKey: Data, remoteKey: Data) {
|
|
|
|
let authContent: [Data] = try String(contentsOf: url)
|
2022-01-24 17:17:06 +01:00
|
|
|
.trimmingCharacters(in: .whitespacesAndNewlines)
|
2022-05-01 13:12:16 +02:00
|
|
|
.components(separatedBy: "\n")
|
|
|
|
.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
|
2022-05-01 13:28:06 +02:00
|
|
|
.map {
|
|
|
|
guard let key = Data(fromHexEncodedString: $0) else {
|
|
|
|
throw ServerError.invalidAuthenticationToken
|
|
|
|
}
|
|
|
|
guard key.count == SHA256.byteCount else {
|
|
|
|
throw ServerError.invalidAuthenticationToken
|
|
|
|
}
|
|
|
|
return key
|
|
|
|
}
|
2022-05-01 13:12:16 +02:00
|
|
|
guard authContent.count == 2 else {
|
|
|
|
throw ServerError.invalidAuthenticationFileContent
|
|
|
|
}
|
2023-01-31 19:10:33 +01:00
|
|
|
return (deviceKey: authContent[0], remoteKey: authContent[1])
|
2022-01-23 20:49:06 +01:00
|
|
|
}
|
2023-02-06 21:44:56 +01:00
|
|
|
|
|
|
|
func log(_ message: String) {
|
2023-02-17 00:09:51 +01:00
|
|
|
guard let observer = MetricObserver.standard else {
|
|
|
|
print(message)
|
|
|
|
return
|
|
|
|
}
|
2023-10-01 19:26:31 +02:00
|
|
|
asyncScheduler.schedule {
|
|
|
|
await observer.log(message)
|
|
|
|
}
|
2023-02-06 21:44:56 +01:00
|
|
|
}
|