import Vapor private var eventLogHandle: FileHandle? private var registeredGuests = Set() private var declinedGuests = Set() private var guestListPath: URL! private var declinedListPath: URL! let df: DateFormatter = { let df = DateFormatter() df.dateFormat = "dd.MM. HH:mm" return df }() func add(guest: String) -> String { registeredGuests.insert(guest) declinedGuests.remove(guest) defer { saveLists() } return log(event: "\(guest) registered") } func remove(guest: String) -> String { registeredGuests.remove(guest) declinedGuests.insert(guest) defer { saveLists() } return log(event: "\(guest) declined") } private func saveLists() { saveList(registeredGuests, named: "guest", to: guestListPath) saveList(declinedGuests, named: "declined", to: declinedListPath) } private func saveList(_ set: Set, named name: String, to url: URL) { guard let list = set.sorted().joined(separator: "\n").data(using: .utf8) else { print("Failed to save \(name) list, no data") return } do { try list.write(to: url) print("Saved \(name) list with \(set.count) entries") } catch { print("Failed to write \(name) list: \(error)") } } private func loadList(from url: URL) throws -> Set { let users = try String(contentsOf: url) .split(separator: "\n") .map { $0.trimmingCharacters(in: .whitespacesAndNewlines) } .filter { !$0.isEmpty } print("Loaded list \(url.path) (\(users.count) entries)") return .init(users) } private func log(event: String) -> String { guard let handle = eventLogHandle else { return "No handle" } let date = Date() let dateString = df.string(from: date) guard let entry = "[\(dateString)] \(event)\n".data(using: .utf8) else { return "Invalid name" } guard #available(macOS 10.15.4, *) else { handle.write(entry) handle.synchronizeFile() return "Success" } do { try handle.write(contentsOf: entry) handle.synchronizeFile() return "Success" } catch { return "Save failed" } } private func createFileIfNeeded(at path: URL) throws { guard !FileManager.default.fileExists(atPath: path.path) else { return } try Data().write(to: path) } // configures your application public func configure(_ app: Application) throws { // uncomment to serve files from /Public folder // app.middleware.use(FileMiddleware(publicDirectory: app.directory.publicDirectory)) app.http.server.configuration.port = 9001 // let eventLog = app.directory.publicDirectory + "events.txt" // let guestList = app.directory.publicDirectory + "registered.txt" // let declinedList = app.directory.publicDirectory + "declined.txt" let eventLog = "/blog/site/lists/events.txt" let guestList = "/blog/site/lists/registered.txt" let declinedList = "/blog/site/lists/declined.txt" let eventLogPath = URL(fileURLWithPath: eventLog) guestListPath = URL(fileURLWithPath: guestList) declinedListPath = URL(fileURLWithPath: declinedList) // Create the files try createFileIfNeeded(at: eventLogPath) try createFileIfNeeded(at: guestListPath) try createFileIfNeeded(at: declinedListPath) // Create handle to write events eventLogHandle = FileHandle(forWritingAtPath: eventLog) if #available(macOS 10.15.4, *) { try eventLogHandle?.seekToEnd() } else { // Fallback on earlier versions eventLogHandle?.seekToEndOfFile() } registeredGuests = try loadList(from: guestListPath) declinedGuests = try loadList(from: declinedListPath) // register routes try routes(app) let date = Date() let dateString = df.string(from: date) print("[\(dateString)] Server started: \(registeredGuests.count) registered, \(declinedGuests.count) declined, event log open: \(eventLogHandle != nil)") }