Add session creation and lookup

This commit is contained in:
Savanni D'Gerinel 2024-12-27 14:02:43 -05:00
parent 1d400ce38b
commit d5f4b7cfa5
8 changed files with 339 additions and 104 deletions

View File

@ -1,11 +1,18 @@
CREATE TABLE users(
uuid TEXT PRIMARY KEY,
name TEXT,
name TEXT UNIQUE,
password TEXT,
admin BOOLEAN,
enabled BOOLEAN
);
CREATE TABLE sessions(
id TEXT PRIMARY KEY,
user_id TEXT,
FOREIGN KEY(user_id) REFERENCES users(uuid)
);
CREATE TABLE games(
uuid TEXT PRIMARY KEY,
name TEXT
@ -28,5 +35,3 @@ CREATE TABLE roles(
FOREIGN KEY(game_id) REFERENCES games(uuid)
);
INSERT INTO users VALUES ("admin", "admin", "", true, true);

View File

@ -10,7 +10,7 @@ use uuid::Uuid;
use crate::{
asset_db::{self, AssetId, Assets},
database::{CharacterId, Database, UserId},
database::{CharacterId, Database, SessionId, UserId},
types::{AppError, FatalError, Game, Message, Tabletop, User, RGB},
};
@ -61,7 +61,7 @@ impl Core {
pub async fn status(&self) -> ResultExt<Status, AppError, FatalError> {
let mut state = self.0.write().await;
let admin_user = return_error!(match state.db.user(UserId::from("admin")).await {
let admin_user = return_error!(match state.db.user(&UserId::from("admin")).await {
Ok(Some(admin_user)) => ok(admin_user),
Ok(None) => {
return ok(Status {
@ -106,6 +106,15 @@ impl Core {
}
}
pub async fn user_by_username(&self, username: &str) -> ResultExt<Option<User>, AppError, FatalError> {
let state = self.0.read().await;
match state.db.user_by_username(username).await {
Ok(Some(user_row)) => ok(Some(User::from(user_row))),
Ok(None) => ok(None),
Err(err) => fatal(err),
}
}
pub async fn list_users(&self) -> ResultExt<Vec<User>, AppError, FatalError> {
let users = self.0.write().await.db.users().await;
match users {
@ -206,7 +215,7 @@ impl Core {
password: String,
) -> ResultExt<(), AppError, FatalError> {
let mut state = self.0.write().await;
let user = match state.db.user(uuid.clone()).await {
let user = match state.db.user(&uuid).await {
Ok(Some(row)) => row,
Ok(None) => return error(AppError::NotFound(uuid.as_str().to_owned())),
Err(err) => return fatal(err),
@ -221,10 +230,13 @@ impl Core {
}
}
pub async fn auth(&self, username: String, password: String) -> ResultExt<UserId, AppError, FatalError> {
pub async fn auth(&self, username: &str, password: &str) -> ResultExt<SessionId, AppError, FatalError> {
let state = self.0.write().await;
match state.db.user_by_username(username).await {
Ok(Some(row)) if (row.password == password) => ok(row.id),
match state.db.user_by_username(&username).await {
Ok(Some(row)) if (row.password == password) => {
let session_id = state.db.create_session(row.id).await.unwrap();
ok(session_id)
}
Ok(_) => error(AppError::AuthFailed),
Err(err) => fatal(err),
}
@ -244,7 +256,7 @@ mod test {
database::{DbConn, DiskDb},
};
fn test_core() -> Core {
async fn test_core() -> Core {
let assets = MemoryAssets::new(vec![
(
AssetId::from("asset_1"),
@ -274,19 +286,21 @@ mod test {
]);
let memory_db: Option<PathBuf> = None;
let conn = DbConn::new(memory_db);
conn.save_user(None, "admin", "aoeu", true, true).await.unwrap();
conn.save_user(None, "gm_1", "aoeu", false, true).await.unwrap();
Core::new(assets, conn)
}
#[tokio::test]
async fn it_lists_available_images() {
let core = test_core();
let core = test_core().await;
let image_paths = core.available_images().await;
assert_eq!(image_paths.len(), 2);
}
#[tokio::test]
async fn it_retrieves_an_asset() {
let core = test_core();
let core = test_core().await;
assert_matches!(core.get_asset(AssetId::from("asset_1")).await, ResultExt::Ok((mime, data)) => {
assert_eq!(mime.type_(), mime::IMAGE);
assert_eq!(data, "abcdefg".as_bytes());
@ -295,7 +309,7 @@ mod test {
#[tokio::test]
async fn it_can_retrieve_the_default_tabletop() {
let core = test_core();
let core = test_core().await;
assert_matches!(core.tabletop().await, Tabletop{ background_color, background_image } => {
assert_eq!(background_color, DEFAULT_BACKGROUND_COLOR);
assert_eq!(background_image, None);
@ -304,7 +318,7 @@ mod test {
#[tokio::test]
async fn it_can_change_the_tabletop_background() {
let core = test_core();
let core = test_core().await;
assert_matches!(
core.set_background_image(AssetId::from("asset_1")).await,
ResultExt::Ok(())
@ -317,7 +331,7 @@ mod test {
#[tokio::test]
async fn it_sends_notices_to_clients_on_tabletop_change() {
let core = test_core();
let core = test_core().await;
let client_id = core.register_client().await;
let mut receiver = core.connect_client(client_id).await;
@ -336,4 +350,21 @@ mod test {
None => panic!("receiver did not get a message"),
}
}
#[tokio::test]
async fn it_creates_a_sessionid_on_successful_auth() {
let core = test_core().await;
match core.auth("admin", "aoeu").await {
ResultExt::Ok(session_id) => {
let st = core.0.read().await;
match st.db.session(session_id).await {
Ok(Some(user_row)) => assert_eq!(user_row.name, "admin"),
Ok(None) => panic!("no matching user row for the session id"),
Err(err) => panic!("{}", err),
}
},
ResultExt::Err(err) => panic!("{}", err),
ResultExt::Fatal(err) => panic!("{}", err),
}
}
}

View File

@ -24,11 +24,13 @@ lazy_static! {
#[derive(Debug)]
enum Request {
Charsheet(CharacterId),
CreateSession(UserId),
Games,
SaveUser(Option<UserId>, String, String, bool, bool),
Session(SessionId),
User(UserId),
UserByUsername(String),
Users,
SaveUser(Option<UserId>, String, String, bool, bool),
}
#[derive(Debug)]
@ -40,10 +42,12 @@ struct DatabaseRequest {
#[derive(Debug)]
enum DatabaseResponse {
Charsheet(Option<CharsheetRow>),
CreateSession(SessionId),
Games(Vec<GameRow>),
SaveUser(UserId),
Session(Option<UserRow>),
User(Option<UserRow>),
Users(Vec<UserRow>),
SaveUser(UserId),
}
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
@ -80,6 +84,40 @@ impl FromSql for UserId {
}
}
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct SessionId(String);
impl SessionId {
pub fn new() -> Self {
Self(format!("{}", Uuid::new_v4().hyphenated()))
}
pub fn as_str<'a>(&'a self) -> &'a str {
&self.0
}
}
impl From<&str> for SessionId {
fn from(s: &str) -> Self {
Self(s.to_owned())
}
}
impl From<String> for SessionId {
fn from(s: String) -> Self {
Self(s)
}
}
impl FromSql for SessionId {
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
match value {
ValueRef::Text(text) => Ok(Self::from(String::from_utf8(text.to_vec()).unwrap())),
_ => unimplemented!(),
}
}
}
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct GameId(String);
@ -177,14 +215,20 @@ pub struct CharsheetRow {
pub data: serde_json::Value,
}
#[derive(Clone, Debug)]
pub struct SessionRow {
id: SessionId,
user_id: SessionId,
}
#[async_trait]
pub trait Database: Send + Sync {
async fn user(&mut self, _: UserId) -> Result<Option<UserRow>, FatalError>;
async fn user(&mut self, _: &UserId) -> Result<Option<UserRow>, FatalError>;
async fn user_by_username(&self, _: String) -> Result<Option<UserRow>, FatalError>;
async fn user_by_username(&self, _: &str) -> Result<Option<UserRow>, FatalError>;
async fn save_user(
&mut self,
&self,
user_id: Option<UserId>,
name: &str,
password: &str,
@ -197,6 +241,10 @@ pub trait Database: Send + Sync {
async fn games(&mut self) -> Result<Vec<GameRow>, FatalError>;
async fn character(&mut self, id: CharacterId) -> Result<Option<CharsheetRow>, FatalError>;
async fn session(&self, id: SessionId) -> Result<Option<UserRow>, FatalError>;
async fn create_session(&self, id: UserId) -> Result<SessionId, FatalError>;
}
pub struct DiskDb {
@ -268,7 +316,7 @@ impl DiskDb {
Ok(DiskDb { conn })
}
fn user(&self, id: UserId) -> Result<Option<UserRow>, FatalError> {
fn user(&self, id: &UserId) -> Result<Option<UserRow>, FatalError> {
let mut stmt = self
.conn
.prepare("SELECT uuid, name, password, admin, enabled FROM users WHERE uuid=?")
@ -293,13 +341,13 @@ impl DiskDb {
}
}
fn user_by_username(&self, username: String) -> Result<Option<UserRow>, FatalError> {
fn user_by_username(&self, username: &str) -> Result<Option<UserRow>, FatalError> {
let mut stmt = self
.conn
.prepare("SELECT uuid, name, password, admin, enabled FROM users WHERE name=?")
.map_err(|err| FatalError::ConstructQueryFailure(format!("{}", err)))?;
let items: Vec<UserRow> = stmt
.query_map([username.as_str()], |row| {
.query_map([username], |row| {
Ok(UserRow {
id: row.get(0).unwrap(),
name: row.get(1).unwrap(),
@ -361,9 +409,7 @@ impl DiskDb {
Some(user_id) => {
let mut stmt = self
.conn
.prepare(
"UPDATE users SET name=?, password=?, admin=?, enabled=? WHERE uuid=?",
)
.prepare("UPDATE users SET name=?, password=?, admin=?, enabled=? WHERE uuid=?")
.map_err(|err| FatalError::ConstructQueryFailure(format!("{}", err)))?;
stmt.execute((name, password, admin, enabled, user_id.as_str()))
.unwrap();
@ -394,6 +440,51 @@ impl DiskDb {
}
}
fn session(&self, session_id: &SessionId) -> Result<Option<UserRow>, FatalError> {
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 = ?")
.map_err(|err| FatalError::ConstructQueryFailure(format!("{}", err)))?;
let items: Vec<UserRow> = stmt
.query_map([session_id.as_str()], |row| {
Ok(UserRow {
id: row.get(0).unwrap(),
name: row.get(1).unwrap(),
password: row.get(2).unwrap(),
admin: row.get(3).unwrap(),
enabled: row.get(4).unwrap(),
})
})
.unwrap()
.collect::<Result<Vec<UserRow>, rusqlite::Error>>()
.unwrap();
match &items[..] {
[] => Ok(None),
[item] => Ok(Some(item.clone())),
_ => Err(FatalError::NonUniqueDatabaseKey(
session_id.as_str().to_owned(),
)),
}
}
fn create_session(&self, user_id: &UserId) -> Result<SessionId, FatalError> {
match self.user(user_id) {
Ok(Some(_)) => {
let mut stmt = self
.conn
.prepare("INSERT INTO sessions VALUES (?, ?)")
.map_err(|err| FatalError::ConstructQueryFailure(format!("{}", err)))?;
let session_id = SessionId::new();
stmt.execute((session_id.as_str(), user_id.as_str()))
.unwrap();
Ok(session_id)
}
Ok(None) => Err(FatalError::DatabaseKeyMissing),
Err(err) => Err(err),
}
}
fn character(&self, id: CharacterId) -> Result<Option<CharsheetRow>, FatalError> {
let mut stmt = self
.conn
@ -456,7 +547,6 @@ async fn db_handler(db: DiskDb, requestor: Receiver<DatabaseRequest>) {
match req {
Request::Charsheet(id) => {
let sheet = db.character(id);
println!("sheet retrieved: {:?}", sheet);
match sheet {
Ok(sheet) => {
tx.send(DatabaseResponse::Charsheet(sheet)).await.unwrap();
@ -464,11 +554,17 @@ async fn db_handler(db: DiskDb, requestor: Receiver<DatabaseRequest>) {
_ => unimplemented!(),
}
}
Request::CreateSession(id) => {
let session_id = db.create_session(&id).unwrap();
tx.send(DatabaseResponse::CreateSession(session_id))
.await
.unwrap();
}
Request::Games => {
unimplemented!();
}
Request::User(uid) => {
let user = db.user(uid);
let user = db.user(&uid);
match user {
Ok(user) => {
tx.send(DatabaseResponse::User(user)).await.unwrap();
@ -477,14 +573,20 @@ async fn db_handler(db: DiskDb, requestor: Receiver<DatabaseRequest>) {
}
}
Request::UserByUsername(username) => {
let user = db.user_by_username(username);
let user = db.user_by_username(&username);
match user {
Ok(user) => tx.send(DatabaseResponse::User(user)).await.unwrap(),
err => panic!("{:?}", err),
}
}
Request::SaveUser(user_id, username, password, admin, enabled) => {
let user_id = db.save_user(user_id, username.as_ref(), password.as_ref(), admin, enabled);
let user_id = db.save_user(
user_id,
username.as_ref(),
password.as_ref(),
admin,
enabled,
);
match user_id {
Ok(user_id) => {
tx.send(DatabaseResponse::SaveUser(user_id)).await.unwrap();
@ -492,6 +594,13 @@ async fn db_handler(db: DiskDb, requestor: Receiver<DatabaseRequest>) {
err => panic!("{:?}", err),
}
}
Request::Session(session_id) => {
let user = db.session(&session_id);
match user {
Ok(user) => tx.send(DatabaseResponse::Session(user)).await.unwrap(),
err => panic!("{:?}", err),
}
}
Request::Users => {
let users = db.users();
match users {
@ -529,12 +638,12 @@ impl DbConn {
#[async_trait]
impl Database for DbConn {
async fn user(&mut self, uid: UserId) -> Result<Option<UserRow>, FatalError> {
async fn user(&mut self, uid: &UserId) -> Result<Option<UserRow>, FatalError> {
let (tx, rx) = bounded::<DatabaseResponse>(1);
let request = DatabaseRequest {
tx,
req: Request::User(uid),
req: Request::User(uid.clone()),
};
match self.conn.send(request).await {
@ -549,12 +658,12 @@ impl Database for DbConn {
}
}
async fn user_by_username(&self, username: String) -> Result<Option<UserRow>, FatalError> {
async fn user_by_username(&self, username: &str) -> Result<Option<UserRow>, FatalError> {
let (tx, rx) = bounded::<DatabaseResponse>(1);
let request = DatabaseRequest {
tx,
req: Request::UserByUsername(username),
req: Request::UserByUsername(username.to_owned()),
};
match self.conn.send(request).await {
@ -570,7 +679,7 @@ impl Database for DbConn {
}
async fn save_user(
&mut self,
&self,
user_id: Option<UserId>,
name: &str,
password: &str,
@ -661,6 +770,46 @@ impl Database for DbConn {
Err(_) => Err(FatalError::DatabaseConnectionLost),
}
}
async fn session(&self, id: SessionId) -> Result<Option<UserRow>, FatalError> {
let (tx, rx) = bounded::<DatabaseResponse>(1);
let request = DatabaseRequest {
tx,
req: Request::Session(id),
};
match self.conn.send(request).await {
Ok(()) => (),
Err(_) => return Err(FatalError::DatabaseConnectionLost),
};
match rx.recv().await {
Ok(DatabaseResponse::Session(row)) => Ok(row),
Ok(_) => Err(FatalError::MessageMismatch),
Err(_) => Err(FatalError::DatabaseConnectionLost),
}
}
async fn create_session(&self, id: UserId) -> Result<SessionId, FatalError> {
let (tx, rx) = bounded::<DatabaseResponse>(1);
let request = DatabaseRequest {
tx,
req: Request::CreateSession(id),
};
match self.conn.send(request).await {
Ok(()) => (),
Err(_) => return Err(FatalError::DatabaseConnectionLost),
};
match rx.recv().await {
Ok(DatabaseResponse::CreateSession(session_id)) => Ok(session_id),
Ok(_) => Err(FatalError::MessageMismatch),
Err(_) => Err(FatalError::DatabaseConnectionLost),
}
}
}
#[cfg(test)]
@ -668,7 +817,6 @@ mod test {
use std::path::PathBuf;
use cool_asserts::assert_matches;
use result_extended::ResultExt;
use super::*;
@ -678,7 +826,7 @@ mod test {
let no_path: Option<PathBuf> = None;
let db = DiskDb::new(no_path).unwrap();
db.save_user(None, "admin", "abcdefg", true, true);
db.save_user(None, "admin", "abcdefg", true, true).unwrap();
let game_id = db.save_game(None, "Candela").unwrap();
(db, game_id)
}
@ -699,9 +847,6 @@ mod test {
let memory_db: Option<PathBuf> = None;
let mut conn = DbConn::new(memory_db);
assert_matches!(
conn.character(CharacterId::from("1")).await,
Ok(None)
);
assert_matches!(conn.character(CharacterId::from("1")).await, Ok(None));
}
}

View File

@ -9,7 +9,11 @@ use warp::{
};
use crate::{
asset_db::AssetId, core::Core, handlers::{handle_auth, handle_available_images, handle_connect_websocket, handle_file, handle_get_charsheet, handle_get_users, handle_register_client, handle_server_status, handle_set_admin_password, handle_set_background_image, handle_unregister_client, RegisterRequest}
asset_db::AssetId,
core::Core,
handlers::{
handle_available_images, handle_check_password, handle_connect_websocket, handle_file, handle_get_charsheet, handle_register_client, handle_server_status, handle_set_background_image, handle_unregister_client, RegisterRequest
},
};
fn cors<H, M>(methods: Vec<M>, headers: Vec<H>) -> Builder
@ -23,6 +27,13 @@ where
.allow_headers(headers)
}
pub fn route_healthcheck() -> impl Filter<Extract = (impl Reply,), Error = warp::Rejection> + Clone
{
warp::path!("api" / "v1" / "healthcheck")
.and(warp::get())
.map(|| warp::reply::reply())
}
pub fn route_server_status(
core: Core,
) -> impl Filter<Extract = (impl Reply,), Error = warp::Rejection> + Clone {
@ -44,7 +55,6 @@ pub fn route_set_bg_image(
.with(cors::<HeaderName, Method>(vec![Method::PUT], vec![]))
}
pub fn route_image(
core: Core,
) -> impl Filter<Extract = (impl Reply,), Error = warp::Rejection> + Clone {
@ -76,7 +86,6 @@ pub fn route_register_client(
})
}
pub fn route_unregister_client(
core: Core,
) -> impl Filter<Extract = (impl Reply,), Error = warp::Rejection> + Clone {
@ -111,7 +120,7 @@ pub fn route_get_charsheet(
})
}
pub fn route_check_password(
pub fn route_authenticate(
core: Core,
) -> impl Filter<Extract = (impl Reply,), Error = warp::Rejection> + Clone {
warp::path!("api" / "v1" / "auth")
@ -119,7 +128,7 @@ pub fn route_check_password(
.and(warp::body::json())
.then({
let core = core.clone();
move |body| handle_auth(core.clone(), body)
move |body| handle_check_password(core.clone(), body)
})
.with(cors::<HeaderName, Method>(vec![Method::PUT], vec![]))
}

View File

@ -1,12 +1,12 @@
use warp::{
http::{header::CONTENT_TYPE, Method},
http::{header::CONTENT_TYPE, HeaderName, Method},
reply::Reply,
Filter,
};
use crate::{
core::Core,
handlers::{handle_get_users, handle_set_admin_password},
handlers::{handle_check_password, handle_get_users, handle_set_admin_password},
};
use super::cors;
@ -37,3 +37,71 @@ pub fn routes_user_management(
) -> impl Filter<Extract = (impl Reply,), Error = warp::Rejection> + Clone {
route_get_users(core.clone()).or(route_set_admin_password(core.clone()))
}
pub fn route_check_password(
core: Core,
) -> impl Filter<Extract = (impl Reply,), Error = warp::Rejection> + Clone {
warp::path!("api" / "v1" / "auth")
.and(warp::put())
.and(warp::body::json())
.then({
let core = core.clone();
move |body| handle_check_password(core.clone(), body)
})
.with(cors::<HeaderName, Method>(vec![Method::PUT], vec![]))
}
#[cfg(test)]
mod test {
use std::{collections::HashMap, path::PathBuf};
use result_extended::ResultExt;
use crate::{asset_db::mocks::MemoryAssets, database::{Database, DbConn, UserId}};
use super::*;
async fn setup() -> Core {
let asset_store = MemoryAssets::new(vec![]);
let memory_file: Option<PathBuf> = None;
let db = DbConn::new(memory_file);
db.save_user(None, "admin", "", true, true).await.unwrap();
Core::new(asset_store, db)
}
#[tokio::test]
async fn handle_check_password_should_return_a_valid_token() {
let core = setup().await;
match core.list_users().await {
ResultExt::Ok(users) => println!("{:?}", users),
ResultExt::Err(err) => panic!("{}", err),
ResultExt::Fatal(err) => panic!("{}", err),
}
match core.user_by_username("admin").await {
ResultExt::Ok(Some(user)) => {
let _ = core.set_password(UserId::from(user.id), "aoeu".to_owned()).await;
},
ResultExt::Ok(None) => panic!("expected user wasn't found"),
ResultExt::Err(err) => panic!("{}", err),
ResultExt::Fatal(err) => panic!("{}", err),
}
let filter = route_check_password(core);
let params: HashMap<String, String> = vec![
("username".to_owned(), "admin".to_owned()),
("password".to_owned(), "aoeu".to_owned()),
]
.into_iter()
.collect();
let resp = warp::test::request()
.method("PUT")
.path("/api/v1/auth")
.json(&params)
.reply(&filter)
.await;
println!("response: {}", resp.status());
assert!(resp.status().is_success());
println!("resp.body(): {}", String::from_utf8(resp.body().to_vec()).unwrap());
serde_json::from_slice::<String>(resp.body()).unwrap();
}
}

View File

@ -47,10 +47,13 @@ where
.status(StatusCode::NOT_FOUND)
.body(vec![])
.unwrap(),
ResultExt::Err(_) => Response::builder()
ResultExt::Err(err) => {
println!("request error: {:?}", err);
Response::builder()
.status(StatusCode::INTERNAL_SERVER_ERROR)
.body(vec![])
.unwrap(),
.unwrap()
}
ResultExt::Fatal(err) => {
panic!("Shutting down with fatal error: {:?}", err);
}
@ -262,35 +265,20 @@ pub async fn handle_set_admin_password(core: Core, password: String) -> impl Rep
pub struct AuthRequest {
username: String,
password: String,
}
pub async fn handle_auth(core: Core, auth_request: AuthRequest) -> impl Reply {
pub async fn handle_check_password(core: Core, auth_request: AuthRequest) -> impl Reply {
handler(async move {
let userid = return_error!(core.auth(auth_request.username, auth_request.password).await);
let session_id = return_error!(
core.auth(&auth_request.username, &auth_request.password)
.await
);
println!("handle_check_password: {:?}", session_id);
ok(Response::builder()
.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Methods", "*")
.header("Access-Control-Allow-Headers", "content-type")
.header("Content-Type", "application/json")
.body(serde_json::to_vec(&userid).unwrap())
.body(serde_json::to_vec(&session_id).unwrap())
.unwrap())
}).await
}
#[cfg(test)]
mod test {
use std::path::PathBuf;
use crate::{asset_db::mocks::MemoryAssets, database::DbConn};
use super::*;
fn setup() -> Core {
let asset_store = MemoryAssets::new(vec![]);
let memory_file: Option<PathBuf> = None;
let db = DbConn::new(memory_file);
Core::new(asset_store, db)
}
})
.await
}

View File

@ -7,13 +7,7 @@ use std::{
use asset_db::{AssetId, FsAssets};
use authdb::AuthError;
use database::DbConn;
use filters::{route_available_images, route_check_password, route_get_charsheet, route_image, route_register_client, route_server_status, route_set_bg_image, route_unregister_client, route_websocket, routes_user_management};
use handlers::{
handle_auth, handle_available_images, handle_connect_websocket, handle_file,
handle_get_charsheet, handle_get_users, handle_register_client, handle_server_status,
handle_set_admin_password, handle_set_background_image, handle_unregister_client,
RegisterRequest,
};
use filters::{route_authenticate, route_available_images, route_get_charsheet, route_healthcheck, route_image, route_register_client, route_server_status, route_set_bg_image, route_unregister_client, route_websocket, routes_user_management};
use warp::{
// header,
filters::{method, path},
@ -111,21 +105,13 @@ pub async fn main() {
let core = core::Core::new(FsAssets::new(PathBuf::from("/home/savanni/Pictures")), conn);
let unauthenticated_endpoints = route_healthcheck().or(route_authenticate(core.clone()));
let authenticated_endpoints = route_image(core.clone());
let filter = route_server_status(core.clone())
.or(route_register_client(core.clone()))
.or(route_unregister_client(core.clone()))
.or(route_websocket(core.clone()))
.or(route_image(core.clone()))
.or(route_available_images(core.clone()))
.or(route_set_bg_image(core.clone()))
.or(routes_user_management(core.clone()))
.or(route_get_charsheet(core.clone()))
.or(route_check_password(core.clone()))
let server = warp::serve(unauthenticated_endpoints
.or(authenticated_endpoints)
.with(warp::log("visions"))
.recover(handle_rejection);
let server = warp::serve(filter);
.recover(handle_rejection));
server
.run(SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 8001))
.await;

View File

@ -7,20 +7,23 @@ use crate::{asset_db::AssetId, database::UserRow};
#[derive(Debug, Error)]
pub enum FatalError {
#[error("Non-unique database key {0}")]
NonUniqueDatabaseKey(String),
#[error("Database migrations failed {0}")]
DatabaseMigrationFailure(String),
#[error("Failed to construct a query")]
ConstructQueryFailure(String),
#[error("Database connection lost")]
DatabaseConnectionLost,
#[error("Expected database key is missing")]
DatabaseKeyMissing,
#[error("Database migrations failed {0}")]
DatabaseMigrationFailure(String),
#[error("Unexpected response for message")]
MessageMismatch,
#[error("Non-unique database key {0}")]
NonUniqueDatabaseKey(String),
}
impl result_extended::FatalError for FatalError {}