Persist registrations on disk
This commit is contained in:
parent
e7b3ac3e9f
commit
7265fd0f0d
1
Resources/.gitkeep
Normal file
1
Resources/.gitkeep
Normal file
@ -0,0 +1 @@
|
|||||||
|
|
@ -7,10 +7,10 @@ final class Database {
|
|||||||
|
|
||||||
private let tables: TableManagement
|
private let tables: TableManagement
|
||||||
|
|
||||||
init() {
|
init(storageFolder: URL) throws {
|
||||||
self.players = PlayerManagement()
|
self.players = try PlayerManagement(storageFolder: storageFolder)
|
||||||
self.tables = TableManagement()
|
self.tables = TableManagement()
|
||||||
// TODO: Load server data from disk
|
// TODO: Load table data from disk
|
||||||
// TODO: Save data to disk
|
// TODO: Save data to disk
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,9 +15,75 @@ final class PlayerManagement {
|
|||||||
|
|
||||||
/// A reverse mapping between generated access tokens and player name
|
/// A reverse mapping between generated access tokens and player name
|
||||||
private var playerNameForToken = [SessionToken: PlayerName]()
|
private var playerNameForToken = [SessionToken: PlayerName]()
|
||||||
|
|
||||||
|
private let passwordFile: FileHandle
|
||||||
|
|
||||||
|
private let passwordFileUrl: URL
|
||||||
|
|
||||||
init() {
|
init(storageFolder: URL) throws {
|
||||||
|
let url = storageFolder.appendingPathComponent("passwords.txt")
|
||||||
|
|
||||||
|
if !FileManager.default.fileExists(atPath: url.path) {
|
||||||
|
try Data().write(to: url)
|
||||||
|
}
|
||||||
|
|
||||||
|
passwordFile = try FileHandle(forUpdating: url)
|
||||||
|
passwordFileUrl = url
|
||||||
|
|
||||||
|
if #available(macOS 10.15.4, *) {
|
||||||
|
guard let data = try passwordFile.readToEnd() else {
|
||||||
|
try passwordFile.seekToEnd()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try loadPasswords(data: data)
|
||||||
|
} else {
|
||||||
|
let data = passwordFile.readDataToEndOfFile()
|
||||||
|
try loadPasswords(data: data)
|
||||||
|
}
|
||||||
|
print("Loaded \(playerPasswordHashes.count) players")
|
||||||
|
}
|
||||||
|
|
||||||
|
private func loadPasswords(data: Data) throws {
|
||||||
|
String(data: data, encoding: .utf8)!
|
||||||
|
.components(separatedBy: "\n")
|
||||||
|
.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
|
||||||
|
.filter { $0 != "" }
|
||||||
|
.forEach { line in
|
||||||
|
let parts = line.components(separatedBy: ":")
|
||||||
|
// Token may contain the separator
|
||||||
|
guard parts.count >= 2 else {
|
||||||
|
print("Invalid line in password file")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let name = parts[0]
|
||||||
|
let token = parts.dropFirst().joined(separator: ":")
|
||||||
|
if token == "" {
|
||||||
|
playerPasswordHashes[name] = nil
|
||||||
|
} else {
|
||||||
|
playerPasswordHashes[name] = token
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func save(password: PasswordHash, forPlayer player: PlayerName) -> Bool {
|
||||||
|
let entry = player + ":" + password + "\n"
|
||||||
|
let data = entry.data(using: .utf8)!
|
||||||
|
do {
|
||||||
|
if #available(macOS 10.15.4, *) {
|
||||||
|
try passwordFile.write(contentsOf: data)
|
||||||
|
} else {
|
||||||
|
passwordFile.write(data)
|
||||||
|
}
|
||||||
|
try passwordFile.synchronize()
|
||||||
|
return true
|
||||||
|
} catch {
|
||||||
|
print("Failed to save password to disk: \(error)")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func deletePassword(forPlayer player: PlayerName) -> Bool {
|
||||||
|
save(password: "", forPlayer: player)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -48,6 +114,9 @@ final class PlayerManagement {
|
|||||||
guard !hasRegisteredPlayer(named: name) else {
|
guard !hasRegisteredPlayer(named: name) else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
guard save(password: hash, forPlayer: name) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
self.playerPasswordHashes[name] = hash
|
self.playerPasswordHashes[name] = hash
|
||||||
return startNewSessionForRegisteredPlayer(named: name)
|
return startNewSessionForRegisteredPlayer(named: name)
|
||||||
}
|
}
|
||||||
@ -58,6 +127,9 @@ final class PlayerManagement {
|
|||||||
- Returns: The session token of the current player, if one exists
|
- Returns: The session token of the current player, if one exists
|
||||||
*/
|
*/
|
||||||
func deletePlayer(named name: PlayerName) -> SessionToken? {
|
func deletePlayer(named name: PlayerName) -> SessionToken? {
|
||||||
|
guard deletePassword(forPlayer: name) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
playerPasswordHashes.removeValue(forKey: name)
|
playerPasswordHashes.removeValue(forKey: name)
|
||||||
guard let sessionToken = sessionTokenForPlayer.removeValue(forKey: name) else {
|
guard let sessionToken = sessionTokenForPlayer.removeValue(forKey: name) else {
|
||||||
return nil
|
return nil
|
||||||
|
@ -7,7 +7,9 @@ public func configure(_ app: Application) throws {
|
|||||||
// serve files from /Public folder
|
// serve files from /Public folder
|
||||||
app.middleware.use(FileMiddleware(publicDirectory: app.directory.publicDirectory))
|
app.middleware.use(FileMiddleware(publicDirectory: app.directory.publicDirectory))
|
||||||
|
|
||||||
database = Database()
|
let storageFolder = URL(fileURLWithPath: app.directory.resourcesDirectory)
|
||||||
|
database = try Database(storageFolder: storageFolder)
|
||||||
|
|
||||||
// register routes
|
// register routes
|
||||||
try routes(app)
|
try routes(app)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user