import Routing 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) public func routes(_ router: Router) throws { try Log.set(logFile: logFile) try loadCapNames() // Get the name of a cap router.getCatching("name", Int.parameter) { request -> Data in let cap = try request.parameters.next(Int.self) guard cap < caps.count else { throw CapError.unknownId } return caps[cap].data(using: .utf8)! } // Set the name of a cap router.postCatching("name", Int.parameter) { request in let cap = try request.parameters.next(Int.self) guard let data = request.http.body.data, let name = String(data: data, encoding: .utf8) else { throw CapError.invalidBody } guard cap <= caps.count else { throw CapError.unknownId } if cap == 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) } } else { caps[cap] = name } try caps.joined(separator: "\n").data(using: .utf8)!.write(to: nameFile) } // Upload an image router.postCatching("images", Int.parameter) { request -> Data in let cap = try request.parameters.next(Int.self) guard let data = request.http.body.data else { throw CapError.invalidBody } let c = try count(of: cap) let f = file(of: cap, version: c) guard !fm.fileExists(atPath: f.path) else { throw CapError.dataInconsistency } try data.write(to: f) return "\(c)".data(using: .utf8)! } // Get count of a cap router.getCatching("count", Int.parameter) { request -> Data in let cap = try request.parameters.next(Int.self) let c = try count(of: cap) return "\(c)".data(using: .utf8)! } // Get the count of all caps router.getCatching("counts") { request -> Data in try (1...caps.count).map { UInt8(try count(of: $0)) }.convertToData() } // Set a different version as the main image router.getCatching("switch", Int.parameter, Int.parameter) { request in let cap = try request.parameters.next(Int.self) let version = try request.parameters.next(Int.self) guard version > 0 else { return } let file1 = file(of: cap, version: 0) guard fm.fileExists(atPath: file1.path) else { throw CapError.invalidFile } let file2 = file(of: cap, version: version) guard fm.fileExists(atPath: file2.path) else { throw CapError.invalidFile } try fm.moveItem(at: file1, to: tempImageFile) try fm.moveItem(at: file2, to: file1) try fm.moveItem(at: tempImageFile, to: file2) } }