Get games that the user is GMing in the user profile
This commit is contained in:
parent
5bb9f00a0d
commit
4dc7a50000
@ -10,8 +10,7 @@ use uuid::Uuid;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
asset_db::{self, AssetId, Assets},
|
asset_db::{self, AssetId, Assets},
|
||||||
database::{CharacterId, Database, GameId, SessionId, UserId},
|
database::{CharacterId, Database, GameId, SessionId, UserId}, types::{AppError, FatalError, Game, GameOverview, Message, Rgb, Tabletop, User, UserProfile},
|
||||||
types::{AppError, FatalError, Game, Message, Rgb, Tabletop, User},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const DEFAULT_BACKGROUND_COLOR: Rgb = Rgb {
|
const DEFAULT_BACKGROUND_COLOR: Rgb = Rgb {
|
||||||
@ -126,9 +125,20 @@ impl Core {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn user(&self, user_id: UserId) -> ResultExt<Option<User>, AppError, FatalError> {
|
pub async fn user(&self, user_id: UserId) -> ResultExt<Option<UserProfile>, AppError, FatalError> {
|
||||||
let users = return_error!(self.list_users().await);
|
let users = return_error!(self.list_users().await);
|
||||||
ok(users.into_iter().find(|user| user.id == user_id))
|
let games = return_error!(self.list_games().await);
|
||||||
|
let user = match users.into_iter().find(|user| user.id == user_id) {
|
||||||
|
Some(user) => user,
|
||||||
|
None => return ok(None),
|
||||||
|
};
|
||||||
|
let user_games = games.into_iter().filter(|g| g.gm == user.id).collect();
|
||||||
|
ok(Some(UserProfile {
|
||||||
|
id: user.id,
|
||||||
|
name: user.name,
|
||||||
|
games: user_games,
|
||||||
|
is_admin: user.admin,
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create_user(&self, username: &str) -> ResultExt<UserId, AppError, FatalError> {
|
pub async fn create_user(&self, username: &str) -> ResultExt<UserId, AppError, FatalError> {
|
||||||
@ -142,11 +152,11 @@ impl Core {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn list_games(&self) -> ResultExt<Vec<Game>, AppError, FatalError> {
|
pub async fn list_games(&self) -> ResultExt<Vec<GameOverview>, AppError, FatalError> {
|
||||||
let games = self.0.write().await.db.games().await;
|
let games = self.0.read().await.db.games().await;
|
||||||
match games {
|
match games {
|
||||||
// Ok(games) => ok(games.into_iter().map(|g| Game::from(g)).collect()),
|
// Ok(games) => ok(games.into_iter().map(|g| Game::from(g)).collect()),
|
||||||
Ok(_games) => unimplemented!(),
|
Ok(games) => ok(games.into_iter().map(GameOverview::from).collect()),
|
||||||
Err(err) => fatal(err),
|
Err(err) => fatal(err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,19 @@
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use async_std::channel::{bounded, Receiver, Sender};
|
use async_std::channel::Receiver;
|
||||||
use include_dir::{include_dir, Dir};
|
use include_dir::{include_dir, Dir};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use rusqlite::Connection;
|
use rusqlite::Connection;
|
||||||
use rusqlite_migration::Migrations;
|
use rusqlite_migration::Migrations;
|
||||||
|
|
||||||
use crate::{database::{DatabaseResponse, Request}, types::FatalError};
|
use crate::{
|
||||||
|
database::{DatabaseResponse, Request},
|
||||||
|
types::FatalError,
|
||||||
|
};
|
||||||
|
|
||||||
use super::{types::GameId, CharacterId, CharsheetRow, DatabaseRequest, SessionId, UserId, UserRow};
|
use super::{
|
||||||
|
types::GameId, CharacterId, CharsheetRow, DatabaseRequest, GameRow, SessionId, UserId, UserRow
|
||||||
|
};
|
||||||
|
|
||||||
static MIGRATIONS_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR/migrations");
|
static MIGRATIONS_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR/migrations");
|
||||||
|
|
||||||
@ -142,7 +147,13 @@ impl DiskDb {
|
|||||||
Ok(items)
|
Ok(items)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn save_game(&self, game_id: Option<GameId>, gm: &UserId, game_type: &str, name: &str) -> Result<GameId, FatalError> {
|
pub fn save_game(
|
||||||
|
&self,
|
||||||
|
game_id: Option<GameId>,
|
||||||
|
gm: &UserId,
|
||||||
|
game_type: &str,
|
||||||
|
name: &str,
|
||||||
|
) -> Result<GameId, FatalError> {
|
||||||
match game_id {
|
match game_id {
|
||||||
None => {
|
None => {
|
||||||
let game_id = GameId::new();
|
let game_id = GameId::new();
|
||||||
@ -150,7 +161,8 @@ impl DiskDb {
|
|||||||
.conn
|
.conn
|
||||||
.prepare("INSERT INTO games VALUES (?, ?, ?, ?)")
|
.prepare("INSERT INTO games VALUES (?, ?, ?, ?)")
|
||||||
.map_err(|err| FatalError::ConstructQueryFailure(format!("{}", err)))?;
|
.map_err(|err| FatalError::ConstructQueryFailure(format!("{}", err)))?;
|
||||||
stmt.execute((game_id.as_str(), gm.as_str(), game_type, name)).unwrap();
|
stmt.execute((game_id.as_str(), gm.as_str(), game_type, name))
|
||||||
|
.unwrap();
|
||||||
Ok(game_id)
|
Ok(game_id)
|
||||||
}
|
}
|
||||||
Some(game_id) => {
|
Some(game_id) => {
|
||||||
@ -158,12 +170,33 @@ impl DiskDb {
|
|||||||
.conn
|
.conn
|
||||||
.prepare("UPDATE games SET gm=? game_type=? name=? WHERE uuid=?")
|
.prepare("UPDATE games SET gm=? game_type=? name=? WHERE uuid=?")
|
||||||
.map_err(|err| FatalError::ConstructQueryFailure(format!("{}", err)))?;
|
.map_err(|err| FatalError::ConstructQueryFailure(format!("{}", err)))?;
|
||||||
stmt.execute((gm.as_str(), game_type, name, game_id.as_str())).unwrap();
|
stmt.execute((gm.as_str(), game_type, name, game_id.as_str()))
|
||||||
|
.unwrap();
|
||||||
Ok(game_id)
|
Ok(game_id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn games(&self) -> Result<Vec<GameRow>, FatalError> {
|
||||||
|
let mut stmt = self
|
||||||
|
.conn
|
||||||
|
.prepare("SELECT * FROM games")
|
||||||
|
.map_err(|err| FatalError::ConstructQueryFailure(format!("{}", err)))?;
|
||||||
|
let items = stmt
|
||||||
|
.query_map([], |row| {
|
||||||
|
Ok(GameRow {
|
||||||
|
id: row.get(0).unwrap(),
|
||||||
|
gm: row.get(1).unwrap(),
|
||||||
|
game_type: row.get(2).unwrap(),
|
||||||
|
name: row.get(3).unwrap(),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.unwrap()
|
||||||
|
.collect::<Result<Vec<GameRow>, rusqlite::Error>>()
|
||||||
|
.unwrap();
|
||||||
|
Ok(items)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn session(&self, session_id: &SessionId) -> Result<Option<UserRow>, FatalError> {
|
pub fn session(&self, session_id: &SessionId) -> Result<Option<UserRow>, FatalError> {
|
||||||
let mut stmt = self.conn
|
let mut stmt = self.conn
|
||||||
.prepare("SELECT u.uuid, u.name, u.password, u.admin, u.enabled FROM sessions s INNER JOIN users u ON u.uuid = s.user_id WHERE s.id = ?")
|
.prepare("SELECT u.uuid, u.name, u.password, u.admin, u.enabled FROM sessions s INNER JOIN users u ON u.uuid = s.user_id WHERE s.id = ?")
|
||||||
@ -274,7 +307,7 @@ pub async fn db_handler(db: DiskDb, requestor: Receiver<DatabaseRequest>) {
|
|||||||
Ok(sheet) => {
|
Ok(sheet) => {
|
||||||
tx.send(DatabaseResponse::Charsheet(sheet)).await.unwrap();
|
tx.send(DatabaseResponse::Charsheet(sheet)).await.unwrap();
|
||||||
}
|
}
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!("errors for Charsheet"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Request::CreateSession(id) => {
|
Request::CreateSession(id) => {
|
||||||
@ -284,15 +317,20 @@ pub async fn db_handler(db: DiskDb, requestor: Receiver<DatabaseRequest>) {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
Request::Games => {
|
Request::Games => {
|
||||||
unimplemented!();
|
match db.games() {
|
||||||
|
Ok(games) => tx.send(DatabaseResponse::Games(games)).await.unwrap(),
|
||||||
|
_ => unimplemented!("errors for Request::Games"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Request::Game(_game_id) => {
|
Request::Game(_game_id) => {
|
||||||
unimplemented!();
|
unimplemented!("Request::Game handler");
|
||||||
}
|
}
|
||||||
Request::SaveGame(game_id, user_id, game_type, game_name) => {
|
Request::SaveGame(game_id, user_id, game_type, game_name) => {
|
||||||
let game_id = db.save_game(game_id, &user_id, &game_type, &game_name);
|
let game_id = db.save_game(game_id, &user_id, &game_type, &game_name);
|
||||||
match game_id {
|
match game_id {
|
||||||
Ok(game_id) => { tx.send(DatabaseResponse::SaveGame(game_id)).await.unwrap(); }
|
Ok(game_id) => {
|
||||||
|
tx.send(DatabaseResponse::SaveGame(game_id)).await.unwrap();
|
||||||
|
}
|
||||||
err => panic!("{:?}", err),
|
err => panic!("{:?}", err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ enum DatabaseResponse {
|
|||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait Database: Send + Sync {
|
pub trait Database: Send + Sync {
|
||||||
async fn users(&mut self) -> Result<Vec<UserRow>, FatalError>;
|
async fn users(&self) -> Result<Vec<UserRow>, FatalError>;
|
||||||
|
|
||||||
async fn user(&self, _: &UserId) -> Result<Option<UserRow>, FatalError>;
|
async fn user(&self, _: &UserId) -> Result<Option<UserRow>, FatalError>;
|
||||||
|
|
||||||
@ -60,7 +60,7 @@ pub trait Database: Send + Sync {
|
|||||||
enabled: bool,
|
enabled: bool,
|
||||||
) -> Result<UserId, FatalError>;
|
) -> Result<UserId, FatalError>;
|
||||||
|
|
||||||
async fn games(&mut self) -> Result<Vec<GameRow>, FatalError>;
|
async fn games(&self) -> Result<Vec<GameRow>, FatalError>;
|
||||||
|
|
||||||
async fn game(&self, _: &GameId) -> Result<Option<GameRow>, FatalError>;
|
async fn game(&self, _: &GameId) -> Result<Option<GameRow>, FatalError>;
|
||||||
|
|
||||||
@ -72,7 +72,7 @@ pub trait Database: Send + Sync {
|
|||||||
game_name: &str,
|
game_name: &str,
|
||||||
) -> Result<GameId, FatalError>;
|
) -> Result<GameId, FatalError>;
|
||||||
|
|
||||||
async fn character(&mut self, id: &CharacterId) -> Result<Option<CharsheetRow>, FatalError>;
|
async fn character(&self, id: &CharacterId) -> Result<Option<CharsheetRow>, FatalError>;
|
||||||
|
|
||||||
async fn session(&self, id: &SessionId) -> Result<Option<UserRow>, FatalError>;
|
async fn session(&self, id: &SessionId) -> Result<Option<UserRow>, FatalError>;
|
||||||
|
|
||||||
@ -123,7 +123,7 @@ macro_rules! send_request {
|
|||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Database for DbConn {
|
impl Database for DbConn {
|
||||||
async fn users(&mut self) -> Result<Vec<UserRow>, FatalError> {
|
async fn users(&self) -> Result<Vec<UserRow>, FatalError> {
|
||||||
send_request!(self, Request::Users, DatabaseResponse::Users(lst) => Ok(lst))
|
send_request!(self, Request::Users, DatabaseResponse::Users(lst) => Ok(lst))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,7 +154,7 @@ impl Database for DbConn {
|
|||||||
DatabaseResponse::SaveUser(user_id) => Ok(user_id))
|
DatabaseResponse::SaveUser(user_id) => Ok(user_id))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn games(&mut self) -> Result<Vec<GameRow>, FatalError> {
|
async fn games(&self) -> Result<Vec<GameRow>, FatalError> {
|
||||||
send_request!(self, Request::Games, DatabaseResponse::Games(lst) => Ok(lst))
|
send_request!(self, Request::Games, DatabaseResponse::Games(lst) => Ok(lst))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,7 +172,7 @@ impl Database for DbConn {
|
|||||||
send_request!(self, Request::SaveGame(game_id, user_id.to_owned(), game_type.to_owned(), game_name.to_owned()), DatabaseResponse::SaveGame(game_id) => Ok(game_id))
|
send_request!(self, Request::SaveGame(game_id, user_id.to_owned(), game_type.to_owned(), game_name.to_owned()), DatabaseResponse::SaveGame(game_id) => Ok(game_id))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn character(&mut self, id: &CharacterId) -> Result<Option<CharsheetRow>, FatalError> {
|
async fn character(&self, id: &CharacterId) -> Result<Option<CharsheetRow>, FatalError> {
|
||||||
send_request!(self, Request::Charsheet(id.to_owned()), DatabaseResponse::Charsheet(row) => Ok(row))
|
send_request!(self, Request::Charsheet(id.to_owned()), DatabaseResponse::Charsheet(row) => Ok(row))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,44 +1,12 @@
|
|||||||
pub mod game_management;
|
mod game_management;
|
||||||
pub use game_management::CreateGameRequest;
|
mod user_management;
|
||||||
|
pub use game_management::*;
|
||||||
|
pub use user_management::*;
|
||||||
|
|
||||||
use std::future::Future;
|
use result_extended::ResultExt;
|
||||||
|
|
||||||
use axum::{
|
|
||||||
http::{HeaderMap, StatusCode},
|
|
||||||
Json,
|
|
||||||
};
|
|
||||||
use futures::{SinkExt, StreamExt};
|
|
||||||
use result_extended::{error, ok, return_error, ResultExt};
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use typeshare::typeshare;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::core::Core;
|
||||||
asset_db::AssetId,
|
|
||||||
core::Core,
|
|
||||||
database::{CharacterId, SessionId, UserId},
|
|
||||||
types::{AppError, FatalError, User},
|
|
||||||
};
|
|
||||||
|
|
||||||
async fn check_session(
|
|
||||||
core: &Core,
|
|
||||||
headers: HeaderMap,
|
|
||||||
) -> ResultExt<Option<User>, AppError, FatalError> {
|
|
||||||
match headers.get("Authorization") {
|
|
||||||
Some(token) => {
|
|
||||||
match token
|
|
||||||
.to_str()
|
|
||||||
.unwrap()
|
|
||||||
.split(" ")
|
|
||||||
.collect::<Vec<&str>>()
|
|
||||||
.as_slice()
|
|
||||||
{
|
|
||||||
[_schema, token] => core.session(&SessionId::from(token.to_owned())).await,
|
|
||||||
_ => error(AppError::BadRequest),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => ok(None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
|
||||||
pub struct HealthCheck {
|
pub struct HealthCheck {
|
||||||
@ -56,213 +24,7 @@ pub async fn healthcheck(core: Core) -> Vec<u8> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
|
||||||
#[typeshare]
|
|
||||||
pub struct AuthRequest {
|
|
||||||
pub username: String,
|
|
||||||
pub password: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn check_password(
|
|
||||||
core: Core,
|
|
||||||
req: Json<AuthRequest>,
|
|
||||||
) -> (StatusCode, Json<Option<SessionId>>) {
|
|
||||||
let Json(AuthRequest { username, password }) = req;
|
|
||||||
match core.auth(&username, &password).await {
|
|
||||||
ResultExt::Ok(session_id) => (StatusCode::OK, Json(Some(session_id))),
|
|
||||||
ResultExt::Err(_err) => (StatusCode::UNAUTHORIZED, Json(None)),
|
|
||||||
ResultExt::Fatal(err) => panic!("Fatal: {}", err),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn auth_required<F, A, Fut>(
|
|
||||||
core: Core,
|
|
||||||
headers: HeaderMap,
|
|
||||||
f: F,
|
|
||||||
) -> (StatusCode, Json<Option<A>>)
|
|
||||||
where
|
|
||||||
F: FnOnce(User) -> Fut,
|
|
||||||
Fut: Future<Output = (StatusCode, Json<Option<A>>)>,
|
|
||||||
{
|
|
||||||
match check_session(&core, headers).await {
|
|
||||||
ResultExt::Ok(Some(user)) => f(user).await,
|
|
||||||
ResultExt::Ok(None) => (StatusCode::UNAUTHORIZED, Json(None)),
|
|
||||||
ResultExt::Err(_err) => (StatusCode::BAD_REQUEST, Json(None)),
|
|
||||||
ResultExt::Fatal(err) => panic!("{}", err),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn admin_required<F, A, Fut>(
|
|
||||||
core: Core,
|
|
||||||
headers: HeaderMap,
|
|
||||||
f: F,
|
|
||||||
) -> (StatusCode, Json<Option<A>>)
|
|
||||||
where
|
|
||||||
F: FnOnce(User) -> Fut,
|
|
||||||
Fut: Future<Output = (StatusCode, Json<Option<A>>)>,
|
|
||||||
{
|
|
||||||
match check_session(&core, headers).await {
|
|
||||||
ResultExt::Ok(Some(user)) => {
|
|
||||||
if user.admin {
|
|
||||||
f(user).await
|
|
||||||
} else {
|
|
||||||
(StatusCode::FORBIDDEN, Json(None))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ResultExt::Ok(None) => (StatusCode::UNAUTHORIZED, Json(None)),
|
|
||||||
ResultExt::Err(_err) => (StatusCode::BAD_REQUEST, Json(None)),
|
|
||||||
ResultExt::Fatal(err) => panic!("{}", err),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
|
||||||
#[typeshare]
|
|
||||||
pub struct UserProfile {
|
|
||||||
pub userid: UserId,
|
|
||||||
pub username: String,
|
|
||||||
pub is_admin: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_user(
|
|
||||||
core: Core,
|
|
||||||
headers: HeaderMap,
|
|
||||||
user_id: Option<UserId>,
|
|
||||||
) -> (StatusCode, Json<Option<UserProfile>>) {
|
|
||||||
auth_required(core.clone(), headers, |user| async move {
|
|
||||||
match user_id {
|
|
||||||
Some(user_id) => match core.user(user_id).await {
|
|
||||||
ResultExt::Ok(Some(user)) => (
|
|
||||||
StatusCode::OK,
|
|
||||||
Json(Some(UserProfile {
|
|
||||||
userid: UserId::from(user.id),
|
|
||||||
username: user.name,
|
|
||||||
is_admin: user.admin,
|
|
||||||
})),
|
|
||||||
),
|
|
||||||
ResultExt::Ok(None) => (StatusCode::NOT_FOUND, Json(None)),
|
|
||||||
ResultExt::Err(_err) => (StatusCode::BAD_REQUEST, Json(None)),
|
|
||||||
ResultExt::Fatal(err) => panic!("{}", err),
|
|
||||||
},
|
|
||||||
None => (
|
|
||||||
StatusCode::OK,
|
|
||||||
Json(Some(UserProfile {
|
|
||||||
userid: UserId::from(user.id),
|
|
||||||
username: user.name,
|
|
||||||
is_admin: user.admin,
|
|
||||||
})),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
|
||||||
#[typeshare]
|
|
||||||
pub struct CreateUserRequest {
|
|
||||||
pub username: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn create_user(
|
|
||||||
core: Core,
|
|
||||||
headers: HeaderMap,
|
|
||||||
req: CreateUserRequest,
|
|
||||||
) -> (StatusCode, Json<Option<()>>) {
|
|
||||||
admin_required(core.clone(), headers, |_admin| async {
|
|
||||||
match core.create_user(&req.username).await {
|
|
||||||
ResultExt::Ok(_) => (StatusCode::OK, Json(None)),
|
|
||||||
ResultExt::Err(_err) => (StatusCode::BAD_REQUEST, Json(None)),
|
|
||||||
ResultExt::Fatal(fatal) => panic!("{}", fatal),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
|
||||||
#[typeshare]
|
|
||||||
pub struct SetPasswordRequest {
|
|
||||||
pub password_1: String,
|
|
||||||
pub password_2: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn set_password(
|
|
||||||
core: Core,
|
|
||||||
headers: HeaderMap,
|
|
||||||
req: SetPasswordRequest,
|
|
||||||
) -> (StatusCode, Json<Option<()>>) {
|
|
||||||
auth_required(core.clone(), headers, |user| async {
|
|
||||||
if req.password_1 == req.password_2 {
|
|
||||||
match core.set_password(user.id, req.password_1).await {
|
|
||||||
ResultExt::Ok(_) => (StatusCode::OK, Json(None)),
|
|
||||||
ResultExt::Err(_err) => (StatusCode::BAD_REQUEST, Json(None)),
|
|
||||||
ResultExt::Fatal(fatal) => panic!("{}", fatal),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
(StatusCode::BAD_REQUEST, Json(None))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
pub async fn handle_auth(
|
|
||||||
auth_ctx: &AuthDB,
|
|
||||||
auth_token: AuthToken,
|
|
||||||
) -> Result<http::Response<String>, Error> {
|
|
||||||
match auth_ctx.authenticate(auth_token).await {
|
|
||||||
Ok(Some(session)) => match serde_json::to_string(&session) {
|
|
||||||
Ok(session_token) => Response::builder()
|
|
||||||
.status(StatusCode::OK)
|
|
||||||
.body(session_token),
|
|
||||||
Err(_) => Response::builder()
|
|
||||||
.status(StatusCode::INTERNAL_SERVER_ERROR)
|
|
||||||
.body("".to_owned()),
|
|
||||||
},
|
|
||||||
Ok(None) => Response::builder()
|
|
||||||
.status(StatusCode::UNAUTHORIZED)
|
|
||||||
.body("".to_owned()),
|
|
||||||
Err(_) => Response::builder()
|
|
||||||
.status(StatusCode::INTERNAL_SERVER_ERROR)
|
|
||||||
.body("".to_owned()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
pub async fn handler<F>(f: F) -> impl Reply
|
|
||||||
where
|
|
||||||
F: Future<Output = ResultExt<Response<Vec<u8>>, AppError, FatalError>>,
|
|
||||||
{
|
|
||||||
match f.await {
|
|
||||||
ResultExt::Ok(response) => response,
|
|
||||||
ResultExt::Err(AppError::NotFound(_)) => Response::builder()
|
|
||||||
.status(StatusCode::NOT_FOUND)
|
|
||||||
.body(vec![])
|
|
||||||
.unwrap(),
|
|
||||||
ResultExt::Err(err) => {
|
|
||||||
println!("request error: {:?}", err);
|
|
||||||
Response::builder()
|
|
||||||
.status(StatusCode::INTERNAL_SERVER_ERROR)
|
|
||||||
.body(vec![])
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
ResultExt::Fatal(err) => {
|
|
||||||
panic!("Shutting down with fatal error: {:?}", err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn handle_server_status(core: Core) -> impl Reply {
|
|
||||||
handler(async move {
|
|
||||||
let status = return_error!(core.status().await);
|
|
||||||
ok(Response::builder()
|
|
||||||
.header("Access-Control-Allow-Origin", "*")
|
|
||||||
.header("Content-Type", "application/json")
|
|
||||||
.body(serde_json::to_vec(&status).unwrap())
|
|
||||||
.unwrap())
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn handle_file(core: Core, asset_id: AssetId) -> impl Reply {
|
pub async fn handle_file(core: Core, asset_id: AssetId) -> impl Reply {
|
||||||
handler(async move {
|
handler(async move {
|
||||||
let (mime, bytes) = return_error!(core.get_asset(asset_id).await);
|
let (mime, bytes) = return_error!(core.get_asset(asset_id).await);
|
||||||
@ -453,27 +215,4 @@ pub async fn handle_set_admin_password(core: Core, password: String) -> impl Rep
|
|||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
|
||||||
#[typeshare]
|
|
||||||
pub struct AuthRequest {
|
|
||||||
username: String,
|
|
||||||
password: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn handle_check_password(core: Core, auth_request: AuthRequest) -> impl Reply {
|
|
||||||
handler(async move {
|
|
||||||
let session_id = return_error!(
|
|
||||||
core.auth(&auth_request.username, &auth_request.password)
|
|
||||||
.await
|
|
||||||
);
|
|
||||||
println!("handle_check_password: {:?}", session_id);
|
|
||||||
|
|
||||||
ok(Response::builder()
|
|
||||||
.header("Content-Type", "application/json")
|
|
||||||
.body(serde_json::to_vec(&session_id).unwrap())
|
|
||||||
.unwrap())
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
*/
|
*/
|
||||||
|
@ -1,7 +1,17 @@
|
|||||||
|
use axum::{
|
||||||
|
http::{HeaderMap, StatusCode},
|
||||||
|
Json,
|
||||||
|
};
|
||||||
|
use futures::Future;
|
||||||
|
use result_extended::{error, ok, ResultExt};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use typeshare::typeshare;
|
||||||
|
|
||||||
use crate::database::UserId;
|
use crate::{
|
||||||
|
core::Core,
|
||||||
|
database::{SessionId, UserId},
|
||||||
|
types::{AppError, FatalError, User, UserProfile},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
#[typeshare]
|
#[typeshare]
|
||||||
@ -104,14 +114,7 @@ pub async fn get_user(
|
|||||||
auth_required(core.clone(), headers, |user| async move {
|
auth_required(core.clone(), headers, |user| async move {
|
||||||
match user_id {
|
match user_id {
|
||||||
Some(user_id) => match core.user(user_id).await {
|
Some(user_id) => match core.user(user_id).await {
|
||||||
ResultExt::Ok(Some(user)) => (
|
ResultExt::Ok(Some(user)) => (StatusCode::OK, Json(Some(user))),
|
||||||
StatusCode::OK,
|
|
||||||
Json(Some(UserProfile {
|
|
||||||
userid: UserId::from(user.id),
|
|
||||||
username: user.name,
|
|
||||||
is_admin: user.admin,
|
|
||||||
})),
|
|
||||||
),
|
|
||||||
ResultExt::Ok(None) => (StatusCode::NOT_FOUND, Json(None)),
|
ResultExt::Ok(None) => (StatusCode::NOT_FOUND, Json(None)),
|
||||||
ResultExt::Err(_err) => (StatusCode::BAD_REQUEST, Json(None)),
|
ResultExt::Err(_err) => (StatusCode::BAD_REQUEST, Json(None)),
|
||||||
ResultExt::Fatal(err) => panic!("{}", err),
|
ResultExt::Fatal(err) => panic!("{}", err),
|
||||||
@ -119,9 +122,10 @@ pub async fn get_user(
|
|||||||
None => (
|
None => (
|
||||||
StatusCode::OK,
|
StatusCode::OK,
|
||||||
Json(Some(UserProfile {
|
Json(Some(UserProfile {
|
||||||
userid: UserId::from(user.id),
|
id: UserId::from(user.id),
|
||||||
username: user.name,
|
name: user.name,
|
||||||
is_admin: user.admin,
|
is_admin: user.admin,
|
||||||
|
games: vec![],
|
||||||
})),
|
})),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
@ -162,5 +166,3 @@ pub async fn set_password(
|
|||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
use std::fmt;
|
|
||||||
|
|
||||||
use axum::{
|
use axum::{
|
||||||
extract::Path,
|
extract::Path,
|
||||||
http::{HeaderMap, StatusCode},
|
http::HeaderMap,
|
||||||
routing::{get, post, put},
|
routing::{get, post, put},
|
||||||
Json, Router,
|
Json, Router,
|
||||||
};
|
};
|
||||||
@ -11,7 +9,8 @@ use crate::{
|
|||||||
core::Core,
|
core::Core,
|
||||||
database::UserId,
|
database::UserId,
|
||||||
handlers::{
|
handlers::{
|
||||||
check_password, create_user, game_management::create_game, get_user, healthcheck, set_password, AuthRequest, CreateGameRequest, CreateUserRequest, SetPasswordRequest
|
check_password, create_game, create_user, get_user, healthcheck, set_password, AuthRequest,
|
||||||
|
CreateGameRequest, CreateUserRequest, SetPasswordRequest,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -92,7 +91,8 @@ mod test {
|
|||||||
asset_db::FsAssets,
|
asset_db::FsAssets,
|
||||||
core::Core,
|
core::Core,
|
||||||
database::{Database, DbConn, GameId, SessionId, UserId},
|
database::{Database, DbConn, GameId, SessionId, UserId},
|
||||||
handlers::{CreateGameRequest, UserProfile},
|
handlers::CreateGameRequest,
|
||||||
|
types::UserProfile,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn setup_without_admin() -> (Core, TestServer) {
|
fn setup_without_admin() -> (Core, TestServer) {
|
||||||
@ -230,8 +230,8 @@ mod test {
|
|||||||
response.assert_status_ok();
|
response.assert_status_ok();
|
||||||
let profile: Option<UserProfile> = response.json();
|
let profile: Option<UserProfile> = response.json();
|
||||||
let profile = profile.unwrap();
|
let profile = profile.unwrap();
|
||||||
assert_eq!(profile.userid, UserId::from("admin"));
|
assert_eq!(profile.id, UserId::from("admin"));
|
||||||
assert_eq!(profile.username, "admin");
|
assert_eq!(profile.name, "admin");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
@ -269,7 +269,7 @@ mod test {
|
|||||||
response.assert_status_ok();
|
response.assert_status_ok();
|
||||||
let profile: Option<UserProfile> = response.json();
|
let profile: Option<UserProfile> = response.json();
|
||||||
let profile = profile.unwrap();
|
let profile = profile.unwrap();
|
||||||
assert_eq!(profile.username, "savanni");
|
assert_eq!(profile.name, "savanni");
|
||||||
|
|
||||||
let response = server
|
let response = server
|
||||||
.get("/api/v1/user/admin")
|
.get("/api/v1/user/admin")
|
||||||
@ -278,7 +278,7 @@ mod test {
|
|||||||
response.assert_status_ok();
|
response.assert_status_ok();
|
||||||
let profile: Option<UserProfile> = response.json();
|
let profile: Option<UserProfile> = response.json();
|
||||||
let profile = profile.unwrap();
|
let profile = profile.unwrap();
|
||||||
assert_eq!(profile.username, "admin");
|
assert_eq!(profile.name, "admin");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
@ -300,7 +300,7 @@ mod test {
|
|||||||
.add_header("Authorization", format!("Bearer {}", session_id))
|
.add_header("Authorization", format!("Bearer {}", session_id))
|
||||||
.await;
|
.await;
|
||||||
let profile = response.json::<Option<UserProfile>>().unwrap();
|
let profile = response.json::<Option<UserProfile>>().unwrap();
|
||||||
assert_eq!(profile.username, "savanni");
|
assert_eq!(profile.name, "savanni");
|
||||||
|
|
||||||
let response = server
|
let response = server
|
||||||
.put("/api/v1/user/password")
|
.put("/api/v1/user/password")
|
||||||
|
@ -3,7 +3,10 @@ use serde::{Deserialize, Serialize};
|
|||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use typeshare::typeshare;
|
use typeshare::typeshare;
|
||||||
|
|
||||||
use crate::{asset_db::AssetId, database::{GameId, UserId, UserRow}};
|
use crate::{
|
||||||
|
asset_db::AssetId,
|
||||||
|
database::{GameId, GameRow, UserId, UserRow},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum FatalError {
|
pub enum FatalError {
|
||||||
@ -111,7 +114,8 @@ pub struct Player {
|
|||||||
pub struct Game {
|
pub struct Game {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub players: Vec<Player>,
|
pub gm: UserId,
|
||||||
|
pub players: Vec<UserId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
@ -133,7 +137,7 @@ pub enum Message {
|
|||||||
#[typeshare]
|
#[typeshare]
|
||||||
pub struct UserProfile {
|
pub struct UserProfile {
|
||||||
pub id: UserId,
|
pub id: UserId,
|
||||||
pub username: String,
|
pub name: String,
|
||||||
pub games: Vec<GameOverview>,
|
pub games: Vec<GameOverview>,
|
||||||
pub is_admin: bool,
|
pub is_admin: bool,
|
||||||
}
|
}
|
||||||
@ -144,6 +148,18 @@ pub struct GameOverview {
|
|||||||
pub id: GameId,
|
pub id: GameId,
|
||||||
pub game_type: String,
|
pub game_type: String,
|
||||||
pub game_name: String,
|
pub game_name: String,
|
||||||
|
pub gm: UserId,
|
||||||
|
pub players: Vec<UserId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<GameRow> for GameOverview {
|
||||||
|
fn from(row: GameRow) -> Self {
|
||||||
|
Self {
|
||||||
|
id: row.id,
|
||||||
|
gm: row.gm,
|
||||||
|
game_type: row.game_type,
|
||||||
|
game_name: row.name,
|
||||||
|
players: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user