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 : " & " ) }
// . m a p { $ 0 . t r i m m i n g C h a r a c t e r s ( i n : . w h i t e s p a c e s A n d N e w l i n e s ) }
. 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 > {
2022-05-03 12:05:03 +02:00
do {
2021-11-11 08:58:36 +01:00
let users = try String ( contentsOf : url )
. split ( separator : " \n " )
. map { $0 . trimmingCharacters ( in : . whitespacesAndNewlines ) }
. filter { ! $0 . isEmpty }
2022-05-03 12:05:03 +02:00
log ( " Loaded list \( url . path ) ( \( users . count ) entries) " )
return . init ( users )
} catch {
log ( " Failed to load \( url . path ) : \( error ) " )
throw error
}
2021-11-11 08:58:36 +01:00
}
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
}
2022-05-03 12:05:03 +02:00
do {
try Data ( ) . write ( to : path )
} catch {
log ( " Failed to create \( path . path ) : \( error ) " )
throw error
}
2021-11-11 08:58:36 +01:00
}
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-05-03 12:05:03 +02:00
private func configureFromFile ( at configPath : URL , app : Application ) throws {
2022-03-29 15:47:20 +02:00
let config = try readConfig ( at : configPath )
. components ( separatedBy : " \n " )
. map { $0 . trimmingCharacters ( in : . whitespaces ) }
. filter { ! $0 . isEmpty }
2022-05-03 12:05:03 +02:00
guard config . count = = 3 else {
2022-03-29 15:47:20 +02:00
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
2022-05-03 12:05:03 +02:00
guard let port = Int ( config [ 2 ] ) else {
log ( " Invalid port ' \( config [ 2 ] ) ', using default " )
return
}
app . http . server . configuration . port = port
2022-03-29 15:47:20 +02:00
}
2021-10-02 21:45:32 +02:00
public func configure ( _ app : Application ) throws {
2021-11-11 09:43:08 +01:00
2022-02-10 17:16:28 +01:00
let configPath = URL ( fileURLWithPath : app . directory . resourcesDirectory )
2022-05-03 12:05:03 +02:00
. appendingPathComponent ( " config.conf " )
try configureFromFile ( at : configPath , app : app )
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
// C r e a t e t h e f i l e s
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 )
// C r e a t e h a n d l e t o w r i t e e v e n t s
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 {
// F a l l b a c k o n e a r l i e r v e r s i o n s
eventLogHandle ? . seekToEndOfFile ( )
}
registeredGuests = try loadList ( from : guestListPath )
declinedGuests = try loadList ( from : declinedListPath )
2021-10-02 21:45:32 +02:00
// r e g i s t e r r o u t e s
try routes ( app )
2021-11-11 08:58:36 +01:00
let date = Date ( )
let dateString = df . string ( from : date )
2022-05-03 12:05:03 +02:00
log ( " [ \( dateString ) ] Server started on port \( app . http . server . configuration . port ) : \( 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
}