import Vapor private var eventLogHandle: FileHandle? private var registeredGuests = Set() private var declinedGuests = Set() private var guestListPath: URL! private var declinedListPath: URL! private 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 { log("Failed to save \(name) list, no data") return } do { try list.write(to: url) log("Saved \(name) list with \(set.count) entries") } catch { log("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 } log("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 configFile = URL(fileURLWithPath: app.directory.resourcesDirectory) .appendingPathComponent("paths.conf") let logPath = try String(contentsOf: configFile) .trimmingCharacters(in: .whitespaces) let logFile = URL(fileURLWithPath: logPath) let listDirectory = URL(fileURLWithPath: app.directory.publicDirectory) .appendingPathComponent("lists") try Log.set(logFile: logFile.path) let eventLog = listDirectory.appendingPathComponent("events.txt") guestListPath = listDirectory.appendingPathComponent("registered.txt") declinedListPath = listDirectory.appendingPathComponent("declined.txt") // Create the files try createFileIfNeeded(at: eventLog) try createFileIfNeeded(at: guestListPath) try createFileIfNeeded(at: declinedListPath) // Create handle to write events eventLogHandle = try FileHandle(forWritingTo: 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) log("[\(dateString)] Server started: \(registeredGuests.count) registered, \(declinedGuests.count) declined, event log open: \(eventLogHandle != nil)") }