2021-11-25 19:15:38 +01:00
|
|
|
import Vapor
|
|
|
|
|
2021-11-28 15:53:47 +01:00
|
|
|
/// The JSON encoder for responses
|
2021-11-27 11:59:13 +01:00
|
|
|
private let encoder = JSONEncoder()
|
|
|
|
|
2021-11-28 15:53:47 +01:00
|
|
|
/// The maximum length of a valid player name
|
|
|
|
private let maximumPlayerNameLength = 40
|
|
|
|
|
|
|
|
/// The maximum length of a valid password
|
|
|
|
private let maximumPasswordLength = 40
|
|
|
|
|
2021-12-03 18:03:29 +01:00
|
|
|
func encodeJSON<T>(_ response: T) throws -> String where T: Encodable {
|
|
|
|
let data = try encoder.encode(response)
|
|
|
|
return String(data: data, encoding: .utf8)!
|
|
|
|
}
|
2021-11-25 19:15:38 +01:00
|
|
|
|
2023-01-31 22:17:15 +01:00
|
|
|
func routes(_ app: Application) {
|
2022-10-11 11:51:00 +02:00
|
|
|
registerPlayer(app)
|
|
|
|
requestPlayerPasswordReset(app)
|
|
|
|
resetPlayerPasswordWithEmailToken(app)
|
|
|
|
deletePlayer(app)
|
|
|
|
loginPlayer(app)
|
|
|
|
resumeSession(app)
|
|
|
|
logoutPlayer(app)
|
|
|
|
getTableForPlayer(app)
|
|
|
|
openWebsocket(app)
|
|
|
|
createTable(app)
|
|
|
|
getPublicTables(app)
|
|
|
|
joinTable(app)
|
|
|
|
leaveTable(app)
|
|
|
|
performActionForPlayer(app)
|
|
|
|
playCard(app)
|
|
|
|
}
|
|
|
|
|
|
|
|
// MARK: Players & Sessions
|
|
|
|
|
|
|
|
/**
|
|
|
|
Create a new player.
|
|
|
|
|
|
|
|
Headers:
|
|
|
|
- `name`: The username of the player
|
|
|
|
- `password`: The password of the player
|
|
|
|
- `email`: Optional email address for password reset
|
|
|
|
|
|
|
|
Possible responses:
|
|
|
|
- `200`: On success, with the session token for the registered user in the reponse body
|
|
|
|
- `400`: Missing name or password
|
|
|
|
- `406`: Password or name too long
|
|
|
|
- `409`: A player with the same name already exists
|
|
|
|
- `424`: The password could not be hashed
|
|
|
|
*/
|
|
|
|
func registerPlayer(_ app: Application) {
|
2022-10-12 14:34:43 +02:00
|
|
|
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
|
2022-10-11 11:51:00 +02:00
|
|
|
|
2022-10-12 14:34:43 +02:00
|
|
|
guard name.count < maximumPlayerNameLength else {
|
2022-10-11 12:08:44 +02:00
|
|
|
throw Abort(.notAcceptable) // 406
|
2021-11-28 15:53:47 +01:00
|
|
|
}
|
2022-10-11 12:08:44 +02:00
|
|
|
|
2021-12-22 22:13:09 +01:00
|
|
|
// Can throw conflict (409)
|
2022-10-12 14:34:43 +02:00
|
|
|
// 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)
|
2021-11-27 11:59:13 +01:00
|
|
|
}
|
2022-10-11 11:51:00 +02:00
|
|
|
}
|
2022-10-12 19:30:11 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
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
|
2022-10-12 19:43:16 +02:00
|
|
|
- `417`: Player name not found or no email registered
|
2022-10-12 19:30:11 +02:00
|
|
|
*/
|
|
|
|
func requestPlayerPasswordReset(_ app: Application) {
|
|
|
|
app.post("player", "password", "reset") { request async throws -> HTTPResponseStatus in
|
|
|
|
let name = try request.header(.name) // Error: 400
|
2022-12-18 17:19:19 +01:00
|
|
|
try await server.sendPasswordResetEmailIfPossible(name: name, request: request) // 417
|
2022-10-12 19:30:11 +02:00
|
|
|
return .ok
|
|
|
|
}
|
|
|
|
}
|
2022-10-12 19:30:43 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
Use a token from a password reset email to change the password.
|
|
|
|
|
|
|
|
Headers:
|
|
|
|
- `token`: The one-time recovery token
|
|
|
|
- `password`: The new password for the user
|
|
|
|
|
|
|
|
Possible responses:
|
|
|
|
- `200`: Success, password changed
|
|
|
|
- `400`: Missing token or password header
|
2022-10-12 19:43:16 +02:00
|
|
|
- `417`: Player name not found or no email registered
|
2022-10-12 19:30:43 +02:00
|
|
|
- `424`: Password could not be hashed
|
|
|
|
*/
|
|
|
|
func resetPlayerPasswordWithEmailToken(_ app: Application) {
|
2022-10-12 21:53:16 +02:00
|
|
|
app.post("player", "password", "new") { req async throws -> HTTPResponseStatus in
|
2022-10-12 19:43:16 +02:00
|
|
|
let token = try req.header(.token) // 400
|
2022-10-12 19:30:43 +02:00
|
|
|
let hash = try req.hashedPassword() // errors: 400, 424
|
2022-10-12 19:43:16 +02:00
|
|
|
try await server.updatePassword(password: hash, forResetToken: token, in: req.db) // 417
|
2022-10-12 19:30:43 +02:00
|
|
|
return .ok
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-11 11:51:00 +02:00
|
|
|
/**
|
|
|
|
Delete a player.
|
|
|
|
|
2022-10-12 21:53:16 +02:00
|
|
|
**Headers**
|
|
|
|
- `name`: The name of the player
|
|
|
|
- `password`: The password of the player
|
2022-10-12 19:43:16 +02:00
|
|
|
|
2022-10-12 21:53:16 +02:00
|
|
|
**Possible errors**
|
2022-10-12 19:43:16 +02:00
|
|
|
- `400`: Missing name or password
|
|
|
|
- `401`: The password or user name is invalid
|
|
|
|
- `424`: The password could not be hashed
|
2022-10-11 11:51:00 +02:00
|
|
|
*/
|
|
|
|
func deletePlayer(_ app: Application) {
|
2022-10-12 21:53:16 +02:00
|
|
|
app.post("player", "delete") { request async throws -> HTTPResponseStatus in
|
|
|
|
let name = try request.header(.name) // 400
|
|
|
|
let password = try request.header(.password) // 400
|
|
|
|
|
2022-10-12 19:28:28 +02:00
|
|
|
let hash = try await server.passwordHashForExistingPlayer(named: name, in: request.db)
|
|
|
|
guard try request.password.verify(password, created: hash) else {
|
2022-10-12 19:43:16 +02:00
|
|
|
return .unauthorized // 401
|
2022-10-11 12:07:41 +02:00
|
|
|
}
|
2022-10-12 19:28:28 +02:00
|
|
|
try await server.deletePlayer(named: name, in: request.db)
|
|
|
|
return .ok
|
2021-11-27 11:59:13 +01:00
|
|
|
}
|
2022-10-11 11:51:00 +02:00
|
|
|
}
|
|
|
|
|
2022-10-12 19:43:16 +02:00
|
|
|
/**
|
|
|
|
Log in as an existing player.
|
|
|
|
|
2022-10-12 21:53:16 +02:00
|
|
|
**Headers**
|
|
|
|
- `name`: The name of the player
|
|
|
|
- `password`: The password of the player
|
|
|
|
|
|
|
|
**Possible errors**
|
2022-10-12 19:43:16 +02:00
|
|
|
- `400`: Missing name or password
|
|
|
|
- `401`: The password or user name is invalid
|
|
|
|
- `424`: The password could not be hashed
|
|
|
|
|
2022-10-12 21:53:16 +02:00
|
|
|
**Response**
|
|
|
|
- `body`: The session token for the user
|
2022-10-12 19:43:16 +02:00
|
|
|
*/
|
2022-10-11 11:51:00 +02:00
|
|
|
func loginPlayer(_ app: Application) {
|
2022-10-12 21:53:16 +02:00
|
|
|
app.post("player", "login") { request async throws -> String in
|
|
|
|
let name = try request.header(.name) // 400
|
|
|
|
let password = try request.header(.password) // 400
|
|
|
|
|
2022-10-12 19:28:28 +02:00
|
|
|
let hash = try await server.passwordHashForExistingPlayer(named: name, in: request.db)
|
|
|
|
guard try request.password.verify(password, created: hash) else {
|
2022-10-12 19:43:16 +02:00
|
|
|
throw Abort(.unauthorized) // 401
|
2022-10-12 19:28:28 +02:00
|
|
|
}
|
2023-11-01 11:56:02 +01:00
|
|
|
return await server.startNewSessionForRegisteredPlayer(named: name)
|
2021-11-27 11:59:13 +01:00
|
|
|
}
|
2022-10-11 11:51:00 +02:00
|
|
|
}
|
|
|
|
|
2022-10-12 21:53:16 +02:00
|
|
|
/**
|
|
|
|
Log in using a session token.
|
|
|
|
|
|
|
|
**Headers**
|
|
|
|
- `token`: The session token of the player
|
|
|
|
|
|
|
|
**Possible errors**
|
|
|
|
- `400`: Missing token
|
|
|
|
- `401`: The token is invalid
|
|
|
|
|
|
|
|
**Response**
|
|
|
|
- `body`: The player name associated with the session token
|
|
|
|
*/
|
2022-10-11 11:51:00 +02:00
|
|
|
func resumeSession(_ app: Application) {
|
2022-10-12 21:53:16 +02:00
|
|
|
app.post("player", "resume") { request -> String in
|
|
|
|
let token = try request.header(.token)
|
|
|
|
|
2023-11-01 11:56:02 +01:00
|
|
|
guard let player = await server.registeredPlayerExists(withSessionToken: token) else {
|
2021-11-28 15:53:47 +01:00
|
|
|
throw Abort(.unauthorized) // 401
|
|
|
|
}
|
|
|
|
return player
|
|
|
|
}
|
2022-10-11 11:51:00 +02:00
|
|
|
}
|
|
|
|
|
2022-10-12 21:53:16 +02:00
|
|
|
/**
|
|
|
|
Log out.
|
|
|
|
|
|
|
|
**Headers**
|
|
|
|
- `token`: The session token of the player
|
|
|
|
|
|
|
|
**Possible errors**
|
|
|
|
- `400`: Missing token
|
|
|
|
|
|
|
|
- Note: The request always succeeds when correctly formed, even for invalid and expired tokens
|
|
|
|
*/
|
2022-10-11 11:51:00 +02:00
|
|
|
func logoutPlayer(_ app: Application) {
|
2022-10-12 21:53:16 +02:00
|
|
|
app.post("player", "logout") { request -> HTTPResponseStatus in
|
|
|
|
let token = try request.header(.token)
|
|
|
|
|
2023-11-01 11:52:04 +01:00
|
|
|
await server.endSession(forSessionToken: token)
|
2022-10-12 21:53:16 +02:00
|
|
|
return .ok
|
2021-11-28 15:53:47 +01:00
|
|
|
}
|
2022-10-11 11:51:00 +02:00
|
|
|
}
|
|
|
|
|
2022-10-12 21:53:16 +02:00
|
|
|
/**
|
|
|
|
Get the current table of the player, if one exists.
|
|
|
|
|
|
|
|
**Headers**
|
|
|
|
- `token`: The session token of the player
|
|
|
|
|
|
|
|
**Possible errors**
|
|
|
|
- `400`: Missing token
|
|
|
|
- `401`: The token is invalid
|
|
|
|
|
|
|
|
**Response**
|
|
|
|
- `body`: The table info, or an empty string
|
|
|
|
*/
|
2022-10-11 11:51:00 +02:00
|
|
|
func getTableForPlayer(_ app: Application) {
|
2022-10-12 21:53:16 +02:00
|
|
|
app.post("player", "table") { request -> String in
|
|
|
|
let token = try request.header(.token)
|
|
|
|
|
2023-11-01 11:56:02 +01:00
|
|
|
guard let player = await server.registeredPlayerExists(withSessionToken: token) else {
|
2021-11-29 11:54:50 +01:00
|
|
|
throw Abort(.unauthorized) // 401
|
|
|
|
}
|
2023-11-01 11:56:02 +01:00
|
|
|
guard let info = await server.currentTableOfPlayer(named: player) else {
|
2021-12-03 18:03:29 +01:00
|
|
|
return ""
|
|
|
|
}
|
|
|
|
return try encodeJSON(info)
|
2021-11-29 11:54:50 +01:00
|
|
|
}
|
2022-10-11 11:51:00 +02:00
|
|
|
}
|
|
|
|
|
2022-10-12 21:53:16 +02:00
|
|
|
/**
|
|
|
|
Start a new websocket connection for the client to receive table updates from the server
|
|
|
|
|
|
|
|
- Note: The first (and only) message from the client over the connection must be a valid session token.
|
|
|
|
*/
|
2022-10-11 11:51:00 +02:00
|
|
|
func openWebsocket(_ app: Application) {
|
2021-11-28 15:53:47 +01:00
|
|
|
app.webSocket("session", "start") { req, socket in
|
|
|
|
socket.onText { socket, text in
|
2023-11-01 11:52:04 +01:00
|
|
|
guard await server.startSession(socket: socket, sessionToken: text) else {
|
|
|
|
try? await socket.close()
|
2021-11-28 15:53:47 +01:00
|
|
|
return
|
|
|
|
}
|
2021-11-27 11:59:13 +01:00
|
|
|
}
|
2021-11-28 15:53:47 +01:00
|
|
|
}
|
2022-10-11 11:51:00 +02:00
|
|
|
}
|
|
|
|
|
2022-10-12 21:53:16 +02:00
|
|
|
// MARK: Tables
|
|
|
|
|
|
|
|
/**
|
|
|
|
Create a new table.
|
|
|
|
|
|
|
|
**Headers**
|
|
|
|
- `name`: The name of the table
|
|
|
|
- `token`: The session token of the player
|
|
|
|
- `visibility`: The visibility of the table (`private` or `public`)
|
|
|
|
|
|
|
|
**Errors**
|
|
|
|
- `400`: Missing token, table name or invalid visibility
|
|
|
|
- `401`: The session token is invalid
|
|
|
|
|
|
|
|
**Response**
|
|
|
|
- `body`: The table id
|
|
|
|
*/
|
2022-10-11 11:51:00 +02:00
|
|
|
func createTable(_ app: Application) {
|
2022-10-12 21:53:16 +02:00
|
|
|
app.post("table", "create") { request -> String in
|
|
|
|
let tableName = try request.header(.name)
|
|
|
|
let token = try request.header(.token)
|
|
|
|
let visibility = try request.header(.visibility)
|
|
|
|
|
2021-12-03 18:03:29 +01:00
|
|
|
let isPublic: Bool
|
2021-11-27 11:59:13 +01:00
|
|
|
if visibility == "private" {
|
2021-12-03 18:03:29 +01:00
|
|
|
isPublic = false
|
2021-11-27 11:59:13 +01:00
|
|
|
} else if visibility == "public" {
|
2021-12-03 18:03:29 +01:00
|
|
|
isPublic = true
|
2021-11-27 11:59:13 +01:00
|
|
|
} else {
|
2021-11-28 15:53:47 +01:00
|
|
|
throw Abort(.badRequest) // 400
|
2021-11-27 11:59:13 +01:00
|
|
|
}
|
2021-12-03 18:03:29 +01:00
|
|
|
|
2023-11-01 11:56:02 +01:00
|
|
|
guard let player = await server.registeredPlayerExists(withSessionToken: token) else {
|
2021-11-28 15:53:47 +01:00
|
|
|
throw Abort(.unauthorized) // 401
|
2021-11-27 11:59:13 +01:00
|
|
|
}
|
2022-10-12 19:28:28 +02:00
|
|
|
let result = try await server.createTable(named: tableName, player: player, isPublic: isPublic, in: request.db)
|
|
|
|
return try encodeJSON(result)
|
2021-11-27 11:59:13 +01:00
|
|
|
}
|
2022-10-11 11:51:00 +02:00
|
|
|
}
|
|
|
|
|
2022-10-12 19:43:16 +02:00
|
|
|
/**
|
|
|
|
List the public tables.
|
|
|
|
|
|
|
|
**Headers**
|
2022-10-12 21:53:16 +02:00
|
|
|
- `token`: The session token of the player
|
2022-10-12 19:43:16 +02:00
|
|
|
|
|
|
|
**Possible errors**
|
|
|
|
- `400`: Missing token
|
|
|
|
- `401`: The session token is invalid
|
2022-10-12 21:53:16 +02:00
|
|
|
|
|
|
|
**Response**
|
|
|
|
- `body`: A JSON object with a list of public tables (id, name, player list)
|
2022-10-12 19:43:16 +02:00
|
|
|
*/
|
2022-10-11 11:51:00 +02:00
|
|
|
func getPublicTables(_ app: Application) {
|
2022-10-12 21:53:16 +02:00
|
|
|
app.post("tables", "public") { request -> String in
|
|
|
|
let token = try request.header(.token)
|
|
|
|
|
2023-11-01 11:56:02 +01:00
|
|
|
guard await server.isValid(sessionToken: token) else {
|
2022-10-12 19:43:16 +02:00
|
|
|
throw Abort(.unauthorized) // 401
|
2021-11-27 11:59:13 +01:00
|
|
|
}
|
2023-11-01 11:56:02 +01:00
|
|
|
let list = await server.getPublicTableInfos()
|
2021-12-03 18:03:29 +01:00
|
|
|
return try encodeJSON(list)
|
2021-11-27 11:59:13 +01:00
|
|
|
}
|
2022-10-11 11:51:00 +02:00
|
|
|
}
|
|
|
|
|
2022-10-12 21:53:16 +02:00
|
|
|
/**
|
|
|
|
Join a table.
|
2022-10-11 11:51:00 +02:00
|
|
|
|
2022-10-12 21:53:16 +02:00
|
|
|
**Headers**
|
|
|
|
- `name`: The table id
|
|
|
|
- `token`: The session token of the player
|
|
|
|
|
|
|
|
**Possible errors**
|
|
|
|
- `400`: Missing token
|
|
|
|
- `401`: The session token is invalid
|
|
|
|
- `403`: The player already sits at another table
|
|
|
|
- `410`: The table id doesn't exist
|
|
|
|
- `417`: The table is already full and can't be joined
|
|
|
|
*/
|
2022-10-11 11:51:00 +02:00
|
|
|
func joinTable(_ app: Application) {
|
2022-10-12 21:53:16 +02:00
|
|
|
app.post("table", "join") { request -> String in
|
|
|
|
let string = try request.header(.name)
|
|
|
|
let token = try request.header(.token)
|
|
|
|
guard let table = UUID(uuidString: string) else {
|
2022-10-11 12:07:41 +02:00
|
|
|
throw Abort(.badRequest)
|
|
|
|
}
|
2022-10-12 19:28:28 +02:00
|
|
|
let result = try await server.join(tableId: table, playerToken: token, in: request.db)
|
|
|
|
return try encodeJSON(result)
|
2021-11-25 19:15:38 +01:00
|
|
|
}
|
2022-10-11 11:51:00 +02:00
|
|
|
}
|
|
|
|
|
2022-10-12 21:53:16 +02:00
|
|
|
/**
|
|
|
|
Leave the current table.
|
|
|
|
|
|
|
|
**Headers**
|
|
|
|
- `token`: The session token of the player
|
|
|
|
|
|
|
|
**Possible errors**
|
|
|
|
- `400`: Missing token
|
|
|
|
- `401`: The session token is invalid
|
|
|
|
*/
|
2022-10-11 11:51:00 +02:00
|
|
|
func leaveTable(_ app: Application) {
|
2022-10-12 19:28:28 +02:00
|
|
|
app.post("table", "leave") { request -> HTTPResponseStatus in
|
2022-10-12 21:53:16 +02:00
|
|
|
let token = try request.header(.token)
|
|
|
|
|
2022-10-12 19:28:28 +02:00
|
|
|
try await server.leaveTable(playerToken: token, in: request.db)
|
|
|
|
return .ok
|
2021-11-30 11:56:51 +01:00
|
|
|
}
|
2022-10-11 11:51:00 +02:00
|
|
|
}
|
|
|
|
|
2022-10-12 21:53:16 +02:00
|
|
|
/**
|
|
|
|
Perform an action, either a game selection or a player action.
|
|
|
|
|
|
|
|
**Headers**
|
|
|
|
- `token`: The session token of the player
|
|
|
|
- `action`: The action to perform
|
|
|
|
|
|
|
|
**Possible errors**
|
|
|
|
- `400`: Missing token or action
|
|
|
|
- `401`: The session token is invalid
|
|
|
|
- `412`: The action is not allowed
|
|
|
|
*/
|
2022-10-11 11:51:00 +02:00
|
|
|
func performActionForPlayer(_ app: Application) {
|
2022-10-12 21:53:16 +02:00
|
|
|
app.post("player", "action") { request -> HTTPResponseStatus in
|
|
|
|
let token = try request.header(.token)
|
|
|
|
let actionString = try request.header(.action)
|
|
|
|
|
2021-12-06 18:28:35 +01:00
|
|
|
let result: PlayerActionResult
|
2021-12-09 11:10:20 +01:00
|
|
|
if let action = PlayerAction(rawValue: actionString) {
|
2023-11-01 11:56:02 +01:00
|
|
|
result = await server.performAction(playerToken: token, action: action)
|
2021-12-06 18:28:35 +01:00
|
|
|
} else if let game = GameType(rawValue: actionString) {
|
2023-11-01 11:56:02 +01:00
|
|
|
result = await server.select(game: game, playerToken: token)
|
2021-12-06 18:28:35 +01:00
|
|
|
} else {
|
|
|
|
throw Abort(.badRequest)
|
|
|
|
}
|
|
|
|
switch result {
|
2021-12-01 22:49:54 +01:00
|
|
|
case .success:
|
2022-10-12 21:53:16 +02:00
|
|
|
return .ok
|
2021-12-01 22:49:54 +01:00
|
|
|
case .invalidToken:
|
2022-10-12 21:53:16 +02:00
|
|
|
return .unauthorized // 401
|
2021-12-06 18:28:35 +01:00
|
|
|
case .noTableJoined:
|
2022-10-12 21:53:16 +02:00
|
|
|
return .preconditionFailed // 412
|
2021-12-06 18:28:35 +01:00
|
|
|
case .tableNotFull:
|
2022-10-12 21:53:16 +02:00
|
|
|
return .preconditionFailed // 412
|
2021-12-06 18:28:35 +01:00
|
|
|
case .tableStateInvalid:
|
2022-10-12 21:53:16 +02:00
|
|
|
return .preconditionFailed // 412
|
2021-12-09 11:10:20 +01:00
|
|
|
case .invalidCard:
|
2022-10-12 21:53:16 +02:00
|
|
|
return .preconditionFailed // 412
|
2021-12-01 22:49:54 +01:00
|
|
|
}
|
|
|
|
}
|
2022-10-11 11:51:00 +02:00
|
|
|
}
|
2021-12-06 11:43:30 +01:00
|
|
|
|
2022-10-12 21:53:16 +02:00
|
|
|
/**
|
|
|
|
Play a card as the active player.
|
|
|
|
|
|
|
|
**Headers**
|
|
|
|
- `token`: The session token of the player
|
|
|
|
- `action`: The id of the card to play
|
|
|
|
|
|
|
|
**Possible errors**
|
|
|
|
- `400`: Missing token or card id
|
|
|
|
- `401`: The session token is invalid
|
|
|
|
- `412`: The action is not allowed
|
|
|
|
*/
|
2022-10-11 11:51:00 +02:00
|
|
|
func playCard(_ app: Application) {
|
2022-10-18 11:40:08 +02:00
|
|
|
app.post("player", "card") { request async throws -> HTTPResponseStatus in
|
2022-10-12 21:53:16 +02:00
|
|
|
let token = try request.header(.token)
|
|
|
|
let cardId = try request.header(.action)
|
|
|
|
guard let card = Card(id: cardId) else {
|
|
|
|
throw Abort(.badRequest)
|
|
|
|
}
|
|
|
|
|
2022-10-18 11:40:08 +02:00
|
|
|
switch try await server.play(card: card, playerToken: token, in: request.db) {
|
2021-12-06 11:43:30 +01:00
|
|
|
case .success:
|
2022-10-12 21:53:16 +02:00
|
|
|
return .ok
|
2021-12-06 11:43:30 +01:00
|
|
|
case .invalidToken:
|
2022-10-12 21:53:16 +02:00
|
|
|
return .unauthorized // 401
|
2021-12-06 11:43:30 +01:00
|
|
|
case .noTableJoined:
|
2022-10-12 21:53:16 +02:00
|
|
|
return .preconditionFailed // 412
|
2021-12-09 11:10:20 +01:00
|
|
|
case .tableStateInvalid:
|
2022-10-12 21:53:16 +02:00
|
|
|
return .preconditionFailed // 412
|
2021-12-06 11:43:30 +01:00
|
|
|
case .invalidCard:
|
2022-10-12 21:53:16 +02:00
|
|
|
return .preconditionFailed // 412
|
2021-12-09 11:10:20 +01:00
|
|
|
case .tableNotFull:
|
2022-10-12 21:53:16 +02:00
|
|
|
return .preconditionFailed // 412
|
2021-12-06 11:43:30 +01:00
|
|
|
}
|
|
|
|
}
|
2021-11-25 19:15:38 +01:00
|
|
|
}
|