import Vapor extension PublicAPI { var path: PathComponent { .init(stringLiteral: rawValue) } var pathParameter: PathComponent { .parameter(rawValue) } } private func keyTransmission(_ req: Request) -> EventLoopFuture { guard let keyId = req.parameters.get(PublicAPI.postKeyIdParameter.rawValue, as: UInt16.self) else { return req.eventLoop.makeSucceededFuture(.invalidKeyIndex) } guard let body = req.body.data else { return req.eventLoop.makeSucceededFuture(.noBodyData) } guard body.readableBytes == KeyManagement.keySize else { return req.eventLoop.makeSucceededFuture(.invalidPayloadSize) } guard let key = body.getData(at: 0, length: KeyManagement.keySize) else { return req.eventLoop.makeSucceededFuture(.corruptkeyData) } return keyManager.sendKeyToDevice(key, keyId: keyId, on: req.eventLoop) } func routes(_ app: Application) throws { /** Get the connection status of the device. The response is a string of either "1" (connected) or "0" (disconnected) */ app.get(PublicAPI.getDeviceStatus.path) { req -> String in keyManager.deviceStatus } /** Get the response from the device. The response is a string of an integer `rawValue` of a `KeyResult` */ app.get(PublicAPI.getDeviceResponse.path) { req -> String in keyManager.deviceResponse } /** Post a request to remove the information about the last key transmission. - The request always succeeds and returns the string "Success" */ app.post(PublicAPI.clearKeyRequest.path) { req -> String in keyManager.clearClientRequest() return "Success" } /** Post a key to the device for unlocking. The corresponding integer key id for the key data must be contained in the url path. The request returns a string containing a `rawValue` of a `KeyPostResponse` A success of this method does not yet signal successful unlocking. The client should request the status by inquiring the device response. */ app.post(PublicAPI.postKey.path, PublicAPI.postKeyIdParameter.pathParameter) { req in keyTransmission(req).map { String($0.rawValue) } } /** Start a new websocket connection for the client to receive table updates from the server - Returns: Nothing - Note: The first (and only) message from the client over the connection must be a valid session token. */ app.webSocket(PublicAPI.socket.path) { req, socket in socket.onBinary { _, data in keyManager.processDeviceResponse(data) } socket.onText { _, text in keyManager.authenticateDevice(psk: text) } _ = socket.onClose.always { _ in keyManager.didCloseDeviceSocket() } keyManager.createNewDeviceConnection(socket) } }