import Vapor // MARK: Paths private let baseFolder = URL(fileURLWithPath: "/caps") private let logFile = baseFolder.appendingPathComponent("logs/server.log").path private let publicFolder = baseFolder.appendingPathComponent("Public") private let imageFolder = publicFolder.appendingPathComponent("images") private let nameFile = publicFolder.appendingPathComponent("names.txt") private let tempImageFile = publicFolder.appendingPathComponent("temp.jpg") // MARK: Variables private let fm = FileManager.default private var caps = [String]() // MARK: SQLite func loadCapNames() throws { let s = try String(contentsOf: nameFile) caps = s.trimmingCharacters(in: .whitespacesAndNewlines).components(separatedBy: "\n") log("\(caps.count) caps loaded") } // MARK: Helper private func folder(of cap: Int) -> URL { imageFolder.appendingPathComponent(String(format: "%04d", cap)) } private func file(of cap: Int, version: Int) -> URL { folder(of: cap).appendingPathComponent(String(format: "%04d-%02d.jpg", cap, version)) } private func countImages(in folder: URL) throws -> Int { try fm.contentsOfDirectory(at: folder, includingPropertiesForKeys: nil).filter({ $0.pathExtension == "jpg" }).count } private func count(of cap: Int) throws -> Int { let f = folder(of: cap) guard fm.fileExists(atPath: f.path) else { return 0 } return try countImages(in: f) } // MARK: Routes /// Register your application's routes here. /// /// [Learn More →](https://docs.vapor.codes/3.0/getting-started/structure/#routesswift) func routes(_ app: Application) throws { try Log.set(logFile: logFile) try loadCapNames() // Get the name of a cap app.getCatching("name", ":n") { request -> Data in guard let cap = request.parameters.get("n", as: Int.self) else { log("Invalid body data") throw Abort(.badRequest) } let index = cap - 1 guard index >= 0, index < caps.count else { log("Trying to get name for invalid cap \(cap) (\(caps.count) caps loaded)") throw CapError.unknownId } return caps[index].data(using: .utf8)! } // Set the name of a cap app.postCatching("name", ":n") { request in guard let cap = request.parameters.get("n", as: Int.self) else { log("Invalid parameter for cap") throw Abort(.badRequest) } let index = cap - 1 guard let buffer = request.body.data, let name = String(data: Data(buffer: buffer), encoding: .utf8) else { log("Invalid body data") throw CapError.invalidBody } guard index <= caps.count else { log("Trying to set name for cap \(cap), but only \(caps.count) caps exist") throw CapError.unknownId } if index == caps.count { caps.append(name) // Create image folder let url = folder(of: cap) if !fm.fileExists(atPath: url.path) { try fm.createDirectory(at: url, withIntermediateDirectories: false) } log("Added cap \(cap)") } else { caps[index] = name log("Set name for cap \(cap)") } try caps.joined(separator: "\n").data(using: .utf8)!.write(to: nameFile) } // Upload an image app.postCatching("images", "n") { request -> Data in guard let cap = request.parameters.get("n", as: Int.self) else { log("Invalid parameter for cap") throw Abort(.badRequest) } guard let buffer = request.body.data else { log("Invalid body data") throw CapError.invalidBody } let data = Data(buffer: buffer) let c = try count(of: cap) let f = file(of: cap, version: c) guard !fm.fileExists(atPath: f.path) else { log("Image \(c) for cap \(cap) already exists on disk") throw CapError.dataInconsistency } try data.write(to: f) return "\(c)".data(using: .utf8)! } // Get count of a cap app.getCatching("count", ":c") { request -> Data in guard let cap = request.parameters.get("c", as: Int.self) else { log("Invalid parameter for cap") throw Abort(.badRequest) } let c = try count(of: cap) return "\(c)".data(using: .utf8)! } // Get the count of all caps app.getCatching("counts") { request -> Data in Data(try (1...caps.count).map({ UInt8(try count(of: $0)) })) } // Set a different version as the main image app.getCatching("switch", ":n", ":v") { request in guard let cap = request.parameters.get("n", as: Int.self) else { log("Invalid parameter for cap") throw Abort(.badRequest) } guard let version = request.parameters.get("v", as: Int.self) else { log("Invalid parameter for cap version") throw Abort(.badRequest) } guard version > 0 else { log("Not switching cap \(cap) to image \(version)") return } let file1 = file(of: cap, version: 0) guard fm.fileExists(atPath: file1.path) else { log("No image 0 for cap \(cap)") throw CapError.invalidFile } let file2 = file(of: cap, version: version) guard fm.fileExists(atPath: file2.path) else { log("No image \(version) for cap \(cap)") throw CapError.invalidFile } try fm.moveItem(at: file1, to: tempImageFile) try fm.moveItem(at: file2, to: file1) try fm.moveItem(at: tempImageFile, to: file2) log("Switched cap \(cap) to version \(version)") } }