diff --git a/Sources/App/Management/SQLiteDatabase.swift b/Sources/App/Management/SQLiteDatabase.swift index c87a23e..0204848 100644 --- a/Sources/App/Management/SQLiteDatabase.swift +++ b/Sources/App/Management/SQLiteDatabase.swift @@ -32,18 +32,28 @@ final class SQLiteDatabase { self.mailConfiguration = mail } - func registerPlayer(named name: PlayerName, hash: PasswordHash, in database: Database) -> EventLoopFuture { - User.query(on: database).filter(\.$name == name).first() - .guard({ $0 == nil }, else: Abort(.conflict)).flatMap { _ in - let user = User(name: name, hash: hash) - return user.create(on: database).map { - // Create a new token and store it for the user - let token = SessionToken.newToken() - self.sessionTokenForPlayer[name] = token - self.playerNameForToken[token] = name - return token - } + func registerPlayer(named name: PlayerName, hash: PasswordHash, email: String?, in database: Database) async throws -> SessionToken { + if let email { + let user = try await User + .query(on: database) + .filter(\.$recoveryEmail == email) + .first() + if user != nil { + throw Abort(.conflict) } + } + if try await User.query(on: database).filter(\.$name == name).first() != nil { + throw Abort(.conflict) + } + let user = User(name: name, hash: hash, email: email) + try await user.create(on: database) + + // Create a new token and store it for the user + let token = SessionToken.newToken() + self.sessionTokenForPlayer[name] = token + self.playerNameForToken[token] = name + return token + } } private func sendEmail(name: PlayerName, email: String, token: String) { diff --git a/Sources/App/Model/User.swift b/Sources/App/Model/User.swift index ebc8807..2009f7d 100644 --- a/Sources/App/Model/User.swift +++ b/Sources/App/Model/User.swift @@ -63,11 +63,12 @@ final class User: Model { init() { } /// Creates a new user. - init(id: UUID? = nil, name: String, hash: String) { + init(id: UUID? = nil, name: String, hash: String, email: String? = nil) { self.id = id self.name = name self.passwordHash = hash self.points = 0 + self.recoveryEmail = email } } diff --git a/Sources/App/routes.swift b/Sources/App/routes.swift index e212d61..d6aecb4 100644 --- a/Sources/App/routes.swift +++ b/Sources/App/routes.swift @@ -50,20 +50,18 @@ func routes(_ app: Application) throws { - `424`: The password could not be hashed */ func registerPlayer(_ app: Application) { - app.post("player", "register") { req -> EventLoopFuture in - let name = try req.header(.name) - let password = try req.header(.password) - let mail = req.optionalHeader(.email)?.trimmed.nonEmpty + app.post("player", "register") { request async throws -> SessionToken in + let name = try request.header(.name) + let hash = try request.hashedPassword() // errors: 400, 424 + let mail = request.optionalHeader(.email)?.trimmed.nonEmpty - guard name.count < maximumPlayerNameLength, - password.count < maximumPasswordLength else { + guard name.count < maximumPlayerNameLength else { throw Abort(.notAcceptable) // 406 } - - let hash = try req.hashedPassword() // errors: 400, 424 // Can throw conflict (409) - return server.registerPlayer(named: name, hash: hash, in: req.db) + // if either the player exists, or the email is already in use + return try await server.registerPlayer(named: name, hash: hash, email: mail, in: request.db) } } /**