Compare commits

..

No commits in common. "ac22fcd4ebb883dbde85e74f0af955a56e88bef0" and "f4864127f84fa0bf2e69091530b966dc0770d517" have entirely different histories.

6 changed files with 64 additions and 53 deletions

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2023 Christoph Hagen
Copyright (c) <year> <copyright holders>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

View File

@ -13,14 +13,25 @@ let package = Package(
.package(url: "https://github.com/christophhagen/ClairvoyantBinaryCodable", from: "0.3.1"),
],
targets: [
.executableTarget(
.target(
name: "App",
dependencies: [
.product(name: "Vapor", package: "vapor"),
.product(name: "Clairvoyant", package: "Clairvoyant"),
.product(name: "ClairvoyantVapor", package: "ClairvoyantVapor"),
.product(name: "ClairvoyantBinaryCodable", package: "ClairvoyantBinaryCodable"),
],
swiftSettings: [
// Enable better optimizations when building in Release configuration. Despite the use of
// the `.unsafeFlags` construct required by SwiftPM, this flag is recommended for Release
// builds. See <https://github.com/swift-server/guides/blob/main/docs/building.md#building-for-production> for details.
.unsafeFlags(["-cross-module-optimization"], .when(configuration: .release))
]
)
),
.executableTarget(name: "Run", dependencies: [.target(name: "App")]),
.testTarget(name: "AppTests", dependencies: [
.target(name: "App"),
.product(name: "XCTVapor", package: "vapor"),
])
]
)

View File

@ -17,8 +17,23 @@ private let df: DateFormatter = {
return df
}()
enum ServerError: Error {
case invalidAuthenticationFileContent
case invalidAuthenticationToken
}
private func updateStatus(_ newStatus: ServerStatus) {
asyncScheduler.schedule {
do {
try await status.update(newStatus)
} catch {
print("Failed to update server status: \(error)")
}
}
}
// configures your application
public func configure(_ app: Application) async throws {
public func configure(_ app: Application) throws {
let storageFolder = URL(fileURLWithPath: app.directory.resourcesDirectory)
let configUrl = storageFolder.appendingPathComponent("config.json")
@ -30,7 +45,7 @@ public func configure(_ app: Application) async throws {
MetricObserver.standard = monitor
status = Metric<ServerStatus>("sesame.status")
try await status.update(.initializing)
updateStatus(.initializing)
app.http.server.configuration.port = config.port
@ -46,10 +61,12 @@ public func configure(_ app: Application) async throws {
provider.registerRoutes(app)
monitor.saveCurrentListOfMetricsToLogFolder()
try await status.update(.nominal)
updateStatus(.nominal)
// Update the metric of the device status to ensure that it is accurate
asyncScheduler.schedule {
await deviceManager.updateDeviceConnectionMetric()
}
log("[\(df.string(from: Date()))] Server started")
}
@ -74,15 +91,15 @@ private func loadKeys(at url: URL) throws -> (deviceKey: Data, remoteKey: Data)
.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
.map {
guard let key = Data(fromHexEncodedString: $0) else {
fatalError("Invalid key data: Failed to convert hex to binary.")
throw ServerError.invalidAuthenticationToken
}
guard key.count == SHA256.byteCount else {
fatalError("Invalid key data: Length should be \(SHA256.byteCount), not \(key.count)")
throw ServerError.invalidAuthenticationToken
}
return key
}
guard authContent.count == 2 else {
fatalError("Invalid keys: Expected 2, found \(authContent.count)")
throw ServerError.invalidAuthenticationFileContent
}
return (deviceKey: authContent[0], remoteKey: authContent[1])
}

View File

@ -1,43 +0,0 @@
import Vapor
import Dispatch
import Logging
/// This extension is temporary and can be removed once Vapor gets this support.
private extension Vapor.Application {
static let baseExecutionQueue = DispatchQueue(label: "vapor.codes.entrypoint")
func runFromAsyncMainEntrypoint() async throws {
try await withCheckedThrowingContinuation { continuation in
Vapor.Application.baseExecutionQueue.async { [self] in
do {
try self.run()
continuation.resume()
} catch {
continuation.resume(throwing: error)
}
}
}
}
}
@main
enum Entrypoint {
static func main() async throws {
var env = try Environment.detect()
try LoggingSystem.bootstrap(from: &env)
let app = Application(env)
defer {
shutdown()
app.shutdown()
}
do {
try await configure(app)
} catch {
app.logger.report(error: error)
throw error
}
try await app.runFromAsyncMainEntrypoint()
}
}

13
Sources/Run/main.swift Normal file
View File

@ -0,0 +1,13 @@
import App
import Vapor
var env = Environment.production //.detect()
try LoggingSystem.bootstrap(from: &env)
let app = Application(env)
defer {
shutdown()
app.shutdown()
}
try configure(app)
try app.run()

View File

@ -0,0 +1,13 @@
@testable import App
import XCTVapor
final class AppTests: XCTestCase {
func testEncodingUInt32() {
let input: UInt32 = 123
let data = input.encoded
let output = UInt32(data: data)
XCTAssertEqual(input, output)
}
}