Add route to send recovery email

This commit is contained in:
Christoph Hagen 2022-10-12 19:30:11 +02:00
parent 26adcc2868
commit 4e9675c284
2 changed files with 46 additions and 3 deletions

View File

@ -54,11 +54,35 @@ final class SQLiteDatabase {
self.playerNameForToken[token] = name self.playerNameForToken[token] = name
return token return token
} }
/**
Send a password reset email.
Possible errors:
- `404`: Player name or email not found.
*/
func sendPasswordResetEmailIfPossible(name: PlayerName, in database: Database) async throws {
guard let user = try await User.query(on: database).filter(\.$name == name).first() else {
throw Abort(.notFound)
}
guard let email = user.recoveryEmail else {
throw Abort(.notFound)
}
try await user.$resetRequest.load(on: database)
if let request = user.resetRequest {
request.renew()
try await request.save(on: database)
self.sendEmail(name: name, email: email, token: request.resetToken)
} else {
let reset = PasswordReset()
try await user.$resetRequest.create(reset, on: database)
self.sendEmail(name: name, email: email, token: reset.resetToken)
}
} }
private func sendEmail(name: PlayerName, email: String, token: String) { private func sendEmail(name: PlayerName, email: String, token: String) {
let recipient = Mail.User(name: name, email: email) let recipient = Mail.User(name: name, email: email)
let url = "\(mailConfiguration.serverDomain)/player/reset?token=\(token)" let url = "\(mailConfiguration.serverDomain)/recovery.html?token=\(token)"
let mail = Mail( let mail = Mail(
from: mailSender, from: mailSender,
to: [recipient], to: [recipient],
@ -70,7 +94,7 @@ final class SQLiteDatabase {
a reset of your account password has been requested for the Schafkopf Server at \(mailConfiguration.serverDomain). a reset of your account password has been requested for the Schafkopf Server at \(mailConfiguration.serverDomain).
To choose a new password, click the following link: To choose a new password, click the following link:
<a href="\(url)">\(url)</a> \(url)
The link will expire in \(mailConfiguration.tokenExpiryDuration) minutes. If you didn't request the password reset, you don't have to do anything. The link will expire in \(mailConfiguration.tokenExpiryDuration) minutes. If you didn't request the password reset, you don't have to do anything.
@ -80,7 +104,7 @@ final class SQLiteDatabase {
""" """
) )
self.smtp.send(mail) { (error) in smtp.send(mail) { (error) in
if let error = error { if let error = error {
print("Failed to send recovery email to \(email): \(error)") print("Failed to send recovery email to \(email): \(error)")
} }

View File

@ -64,6 +64,25 @@ func registerPlayer(_ app: Application) {
return try await server.registerPlayer(named: name, hash: hash, email: mail, in: request.db) return try await server.registerPlayer(named: name, hash: hash, email: mail, in: request.db)
} }
} }
/**
Request an email to reset the password of a player.
Headers:
- `name`: The player name
Possible responses:
- `200`: Success, email will be sent
- `400`: Missing name header
- `404`: Player name not found or no email registered
*/
func requestPlayerPasswordReset(_ app: Application) {
app.post("player", "password", "reset") { request async throws -> HTTPResponseStatus in
let name = try request.header(.name) // Error: 400
try await server.sendPasswordResetEmailIfPossible(name: name, in: request.db)
return .ok
}
}
/** /**
Delete a player. Delete a player.