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 }() private func authorize(_ request: Request) throws { guard let key = request.headers.first(name: "key") else { throw Abort(.badRequest) // 400 } guard server.hasAuthorization(for: key) else { throw Abort(.forbidden) // 403 } } func routes(_ app: Application) { app.get("version") { _ in "\(server.classifierVersion)" } // Add or change a cap app.postCatching("cap") { request in try authorize(request) let data = try request.getBodyData() let cap = try decoder.decode(Cap.self, from: data) try server.addOrUpdate(cap) } // Upload an image app.postCatching("images", ":n") { request in try 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 server.save(image: data, for: cap) } // Update the classifier app.on(.POST, "classifier", ":version", body: .collect(maxSize: "50mb")) { request -> HTTPStatus in try authorize(request) guard let version = request.parameters.get("version", as: Int.self) else { log("Invalid parameter for version") throw Abort(.badRequest) } guard version > server.classifierVersion else { throw Abort(.alreadyReported) // 208 } let data = try request.getBodyData() try server.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 authorize(request) let body = try request.getStringBody() server.updateTrainedClasses(content: body) server.removeAllEntriesInImageChangeList(before: date) } // Get the list of missing thumbnails app.get("thumbnails", "missing") { request in let missingThumbnails = server.getListOfMissingThumbnails() return missingThumbnails.map(String.init).joined(separator: ",") } 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) } let data = try request.getBodyData() server.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) } try authorize(request) guard let version = request.parameters.get("version", as: Int.self) else { log("Invalid version parameter for image deletion") throw Abort(.badRequest) } guard server.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 } }