import Vapor import Foundation /// The decoder to extract caps from JSON payloads given to the `cap` route. private let decoder = JSONDecoder() /// The date formatter to decode dates in requests private let dateFormatter: DateFormatter = { let df = DateFormatter() df.dateFormat = "yy-MM-dd-HH-mm-ss" return df }() extension CapServer { private func ensureOperability() throws { guard isOperational else { throw Abort(.noContent) } } func registerRoutes(with app: Application, authenticator: Authenticator) { app.get("version") { _ in try self.ensureOperability() return "\(self.classifierVersion)" } // Add or change a cap app.postCatching("cap") { request in try self.ensureOperability() try authenticator.authorize(request) let data = try request.getBodyData() let cap = try decoder.decode(Cap.self, from: data) try self.addOrUpdate(cap) } // Upload an image app.postCatching("images", ":n") { request in try self.ensureOperability() try authenticator.authorize(request) guard let cap = request.parameters.get("n", as: Int.self) else { log("Invalid parameter for cap") throw Abort(.badRequest) } let data = try request.getBodyData() try self.save(image: data, for: cap) } // Update the classifier app.on(.POST, "classifier", ":version", body: .collect(maxSize: "50mb")) { request -> HTTPStatus in guard let version = request.parameters.get("version", as: Int.self) else { log("Invalid parameter for version") throw Abort(.badRequest) } guard version > self.classifierVersion else { throw Abort(.alreadyReported) // 208 } try self.ensureOperability() try authenticator.authorize(request) let data = try request.getBodyData() try self.save(classifier: data, version: version) return .ok } // Update the trained classes app.postCatching("classes", ":date") { request in guard let dateString = request.parameters.get("date") else { log("Invalid parameter for date") throw Abort(.badRequest) } guard let date = dateFormatter.date(from: dateString) else { log("Invalid date specification") throw Abort(.badRequest) } try self.ensureOperability() try authenticator.authorize(request) let body = try request.getStringBody() self.updateTrainedClasses(content: body) self.removeAllEntriesInImageChangeList(before: date) } // Get the list of missing thumbnails app.get("thumbnails", "missing") { request in try self.ensureOperability() let missingThumbnails = self.getListOfMissingThumbnails() return missingThumbnails.map(String.init).joined(separator: ",") } // Upload the thumbnail of a cap app.postCatching("thumbnails", ":cap") { request in guard let cap = request.parameters.get("cap", as: Int.self) else { log("Invalid cap parameter for thumbnail upload") throw Abort(.badRequest) } try self.ensureOperability() try authenticator.authorize(request) let data = try request.getBodyData() self.saveThumbnail(data, for: cap) } // Delete the image of a cap app.postCatching("delete", ":cap", ":version") { request in guard let cap = request.parameters.get("cap", as: Int.self) else { log("Invalid cap parameter for image deletion") throw Abort(.badRequest) } guard let version = request.parameters.get("version", as: Int.self) else { log("Invalid version parameter for image deletion") throw Abort(.badRequest) } try self.ensureOperability() try authenticator.authorize(request) guard self.deleteImage(version: version, for: cap) else { throw Abort(.gone) } } } } private extension Request { func getBodyData() throws -> Data { guard let buffer = body.data else { log("Missing body data") throw CapError.invalidBody } return Data(buffer: buffer) } func getStringBody() throws -> String { let data = try getBodyData() guard let content = String(data: data, encoding: .utf8) else { log("Invalid string body") throw CapError.invalidBody } return content } }