2021-10-02 21:45:32 +02:00
|
|
|
import Vapor
|
|
|
|
|
2021-11-11 08:58:36 +01:00
|
|
|
private var eventLogHandle: FileHandle?
|
|
|
|
|
|
|
|
private var registeredGuests = Set<String>()
|
|
|
|
|
|
|
|
private var declinedGuests = Set<String>()
|
|
|
|
|
|
|
|
private var guestListPath: URL!
|
|
|
|
|
|
|
|
private var declinedListPath: URL!
|
|
|
|
|
2022-03-29 15:47:20 +02:00
|
|
|
private var maximumGuestCount = 100
|
|
|
|
|
2021-11-11 10:08:33 +01:00
|
|
|
private let df: DateFormatter = {
|
2021-11-11 08:58:36 +01:00
|
|
|
let df = DateFormatter()
|
|
|
|
df.dateFormat = "dd.MM. HH:mm"
|
|
|
|
return df
|
|
|
|
}()
|
|
|
|
|
2022-01-10 23:45:34 +01:00
|
|
|
func guestCount() -> Int {
|
|
|
|
registeredGuests
|
|
|
|
.reduce([]) { $0 + $1.components(separatedBy: "+") }
|
2022-03-29 15:47:20 +02:00
|
|
|
.reduce([]) { $0 + $1.components(separatedBy: " - ") }
|
|
|
|
.reduce([]) { $0 + $1.components(separatedBy: ",") }
|
|
|
|
.reduce([]) { $0 + $1.components(separatedBy: " und ") }
|
2022-01-10 23:45:34 +01:00
|
|
|
.reduce([]) { $0 + $1.components(separatedBy: "&") }
|
|
|
|
//.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
|
|
|
|
.count
|
|
|
|
}
|
|
|
|
|
2021-11-11 08:58:36 +01:00
|
|
|
|
|
|
|
func add(guest: String) -> String {
|
2022-03-29 15:47:20 +02:00
|
|
|
guard registeredGuests.count < maximumGuestCount else {
|
|
|
|
return "Too many requests"
|
|
|
|
}
|
2021-11-11 08:58:36 +01:00
|
|
|
registeredGuests.insert(guest)
|
|
|
|
declinedGuests.remove(guest)
|
|
|
|
defer { saveLists() }
|
|
|
|
return log(event: "\(guest) registered")
|
|
|
|
}
|
|
|
|
|
|
|
|
func remove(guest: String) -> String {
|
2022-03-29 15:47:20 +02:00
|
|
|
guard declinedGuests.count < maximumGuestCount else {
|
|
|
|
return "Too many requests"
|
|
|
|
}
|
2021-11-11 08:58:36 +01:00
|
|
|
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<String>, named name: String, to url: URL) {
|
|
|
|
guard let list = set.sorted().joined(separator: "\n").data(using: .utf8) else {
|
2021-11-11 09:43:08 +01:00
|
|
|
log("Failed to save \(name) list, no data")
|
2021-11-11 08:58:36 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
do {
|
|
|
|
try list.write(to: url)
|
2021-11-11 09:43:08 +01:00
|
|
|
log("Saved \(name) list with \(set.count) entries")
|
2021-11-11 08:58:36 +01:00
|
|
|
} catch {
|
2021-11-11 09:43:08 +01:00
|
|
|
log("Failed to write \(name) list: \(error)")
|
2021-11-11 08:58:36 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private func loadList(from url: URL) throws -> Set<String> {
|
|
|
|
let users = try String(contentsOf: url)
|
|
|
|
.split(separator: "\n")
|
|
|
|
.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
|
|
|
|
.filter { !$0.isEmpty }
|
2021-11-11 09:43:08 +01:00
|
|
|
log("Loaded list \(url.path) (\(users.count) entries)")
|
2021-11-11 08:58:36 +01:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2022-03-29 15:47:20 +02:00
|
|
|
private func readConfig(at path: URL) throws -> String {
|
2022-02-10 17:16:28 +01:00
|
|
|
do {
|
2022-03-29 15:47:20 +02:00
|
|
|
let content = try String(contentsOf: path)
|
2022-02-10 17:19:32 +01:00
|
|
|
.trimmingCharacters(in: .whitespacesAndNewlines)
|
2022-03-29 15:47:20 +02:00
|
|
|
return content
|
2022-02-10 17:16:28 +01:00
|
|
|
} catch {
|
|
|
|
log("Failed to read configuration file at \(path.path): \(error)")
|
|
|
|
throw error
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-29 15:47:20 +02:00
|
|
|
private func configureFromFile(at configPath: URL) throws {
|
|
|
|
let config = try readConfig(at: configPath)
|
|
|
|
.components(separatedBy: "\n")
|
|
|
|
.map { $0.trimmingCharacters(in: .whitespaces) }
|
|
|
|
.filter { !$0.isEmpty }
|
|
|
|
guard config.count == 2 else {
|
|
|
|
log("Invalid configuration file at \(configPath.path)")
|
|
|
|
throw FestivalError.invalidConfiguration
|
|
|
|
}
|
|
|
|
try Log.set(logFile: config[0])
|
|
|
|
guard let count = Int(config[1]) else {
|
|
|
|
log("Invalid maximum guest count '\(config[1])', using default")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
maximumGuestCount = count
|
|
|
|
}
|
|
|
|
|
2021-10-02 21:45:32 +02:00
|
|
|
public func configure(_ app: Application) throws {
|
2021-11-11 08:58:36 +01:00
|
|
|
app.http.server.configuration.port = 9001
|
2021-11-11 09:43:08 +01:00
|
|
|
|
2022-02-10 17:16:28 +01:00
|
|
|
let configPath = URL(fileURLWithPath: app.directory.resourcesDirectory)
|
2021-11-11 09:43:08 +01:00
|
|
|
.appendingPathComponent("paths.conf")
|
2022-03-29 15:47:20 +02:00
|
|
|
try configureFromFile(at: configPath)
|
|
|
|
|
2022-02-10 16:18:50 +01:00
|
|
|
let listDirectory = URL(fileURLWithPath: app.directory.publicDirectory)
|
|
|
|
.appendingPathComponent("lists")
|
2021-11-11 09:43:08 +01:00
|
|
|
let eventLog = listDirectory.appendingPathComponent("events.txt")
|
|
|
|
guestListPath = listDirectory.appendingPathComponent("registered.txt")
|
|
|
|
declinedListPath = listDirectory.appendingPathComponent("declined.txt")
|
2021-11-11 08:58:36 +01:00
|
|
|
|
|
|
|
// Create the files
|
2021-11-11 09:43:08 +01:00
|
|
|
try createFileIfNeeded(at: eventLog)
|
2021-11-11 08:58:36 +01:00
|
|
|
try createFileIfNeeded(at: guestListPath)
|
|
|
|
try createFileIfNeeded(at: declinedListPath)
|
|
|
|
|
|
|
|
// Create handle to write events
|
2021-11-11 09:43:08 +01:00
|
|
|
eventLogHandle = try FileHandle(forWritingTo: eventLog)
|
2021-11-11 08:58:36 +01:00
|
|
|
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)
|
|
|
|
|
2021-10-02 21:45:32 +02:00
|
|
|
// register routes
|
|
|
|
try routes(app)
|
2021-11-11 08:58:36 +01:00
|
|
|
|
|
|
|
let date = Date()
|
|
|
|
let dateString = df.string(from: date)
|
2021-11-11 09:43:08 +01:00
|
|
|
log("[\(dateString)] Server started: \(registeredGuests.count) registered, \(declinedGuests.count) declined, event log open: \(eventLogHandle != nil)")
|
2021-11-11 08:58:36 +01:00
|
|
|
|
2021-10-02 21:45:32 +02:00
|
|
|
}
|