158 lines
5.8 KiB
Swift
Executable File
158 lines
5.8 KiB
Swift
Executable File
import Vapor
|
|
import Foundation
|
|
|
|
/// The decoder to extract caps from JSON payloads given to the `cap` route.
|
|
private let decoder = JSONDecoder()
|
|
|
|
private let encoder = JSONEncoder()
|
|
|
|
/// 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(request: "/cap")
|
|
let cap = try decoder.decode(Cap.self, from: data)
|
|
try self.addOrUpdate(cap)
|
|
}
|
|
|
|
// Upload an image
|
|
app.postCatching("image", .parameter("cap")) { request in
|
|
try self.ensureOperability()
|
|
try authenticator.authorize(request)
|
|
guard let cap = request.parameters.get("cap", as: Int.self) else {
|
|
log("/images/:cap: Invalid 'cap' parameter")
|
|
throw Abort(.badRequest)
|
|
}
|
|
let data = try request.getBodyData(request: "/images/:cap")
|
|
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("/classifier/:version: 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(request: "/classifier/:version")
|
|
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("/classes/:date: Invalid 'date' parameter")
|
|
throw Abort(.badRequest)
|
|
}
|
|
guard let date = dateFormatter.date(from: dateString) else {
|
|
log("/classes/:date: Invalid 'date' specification")
|
|
throw Abort(.badRequest)
|
|
}
|
|
|
|
try self.ensureOperability()
|
|
try authenticator.authorize(request)
|
|
let body = try request.getStringBody(request: "/classes/:date")
|
|
|
|
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("/thumbnails/:cap: Invalid cap parameter for thumbnail upload")
|
|
throw Abort(.badRequest)
|
|
}
|
|
try self.ensureOperability()
|
|
try authenticator.authorize(request)
|
|
let data = try request.getBodyData(request: "/thumbnails/:cap")
|
|
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("/delete/:cap/:version: Invalid 'cap' parameter for image deletion")
|
|
throw Abort(.badRequest)
|
|
}
|
|
guard let version = request.parameters.get("version", as: Int.self) else {
|
|
log("/delete/:cap/:version: Invalid 'version' parameter for image deletion")
|
|
throw Abort(.badRequest)
|
|
}
|
|
|
|
try self.ensureOperability()
|
|
try authenticator.authorize(request)
|
|
guard let cap = self.deleteImage(version: version, for: cap) else {
|
|
throw Abort(.gone)
|
|
}
|
|
return try encoder.encode(cap)
|
|
}
|
|
|
|
// Delete a cap completely, with all images
|
|
app.postCatching("delete", ":cap") { request in
|
|
guard let cap = request.parameters.get("cap", as: Int.self) else {
|
|
log("/delete/:cap/: Invalid 'cap' parameter for image deletion")
|
|
throw Abort(.badRequest)
|
|
}
|
|
try self.ensureOperability()
|
|
try authenticator.authorize(request)
|
|
guard self.delete(cap: cap) else {
|
|
throw Abort(.gone)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private extension Request {
|
|
|
|
func getBodyData(request: String) throws -> Data {
|
|
guard let buffer = body.data else {
|
|
log("\(request): Missing body data")
|
|
throw CapError.invalidBody
|
|
}
|
|
return Data(buffer: buffer)
|
|
}
|
|
|
|
func getStringBody(request: String) throws -> String {
|
|
let data = try getBodyData(request: request)
|
|
guard let content = String(data: data, encoding: .utf8) else {
|
|
log("\(request): Invalid string body")
|
|
throw CapError.invalidBody
|
|
}
|
|
return content
|
|
}
|
|
}
|