diff --git a/app/src/api.rs b/app/src/api.rs index a12ebec..bce8c0a 100644 --- a/app/src/api.rs +++ b/app/src/api.rs @@ -1,3 +1,5 @@ +//! Koreader Progress Sync API. + use poem::{ error::ResponseError, http::StatusCode, @@ -19,17 +21,20 @@ use crate::{ error_response, }; +/// Data for registering a new user. #[derive(Object, Deserialize)] struct RegisterRequest { username: String, password: String, } +/// Answer after registering a new user. #[derive(Object, Serialize)] struct UserCreated { username: String, } +/// Datafor pushing progress. #[derive(Debug, Clone, Object, Deserialize)] pub struct DocumentUpdate { pub device: String, @@ -39,6 +44,7 @@ pub struct DocumentUpdate { pub progress: String, } +/// Answer when pulling progress. #[derive(Debug, Clone, Object, Deserialize)] pub struct DocumentProgress { pub device: String, @@ -70,6 +76,9 @@ pub struct Api; #[OpenApi] impl Api { + /// Register a new user. + /// + /// If a user of that id already exist, return a conflict. #[oai(path = "/users/create", method = "post")] async fn register( &self, @@ -88,11 +97,13 @@ impl Api { } } + /// Return OK if a user authenticated successfully. #[oai(path = "/users/auth", method = "get", transform = "authorize")] async fn login(&self) -> Result> { Ok(payload::Response::new(()).status(StatusCode::OK)) } + /// Push new progress. #[oai(path = "/syncs/progress", method = "put", transform = "authorize")] async fn push_progress( &self, @@ -108,6 +119,7 @@ impl Api { Ok(payload::Response::new(()).status(StatusCode::OK)) } + /// Pull progress for document with id `doc_id`. #[oai( path = "/syncs/progress/:doc_id", method = "get", diff --git a/app/src/app_state.rs b/app/src/app_state.rs index 5c14d6d..c4543c3 100644 --- a/app/src/app_state.rs +++ b/app/src/app_state.rs @@ -1,6 +1,10 @@ +//! Global application state. + use crate::{cli::Config, db::Db}; pub struct AppState { + /// Application configuration. pub config: Config, + /// Database connection. pub db: Db, } diff --git a/app/src/auth_middleware.rs b/app/src/auth_middleware.rs index 9a59f12..ee0e149 100644 --- a/app/src/auth_middleware.rs +++ b/app/src/auth_middleware.rs @@ -1,3 +1,9 @@ +//! Poem middleware for koreader progress sync authentication. +//! +//! Authentication works with two headers: +//! - x-auth-user: username +//! - x-auth-key: md5 hashed password + use poem::{ error::ResponseError, http::StatusCode, Endpoint, EndpointExt, Error, Middleware, Request, Result, diff --git a/app/src/db.rs b/app/src/db.rs index 598183c..e87cdfe 100644 --- a/app/src/db.rs +++ b/app/src/db.rs @@ -1,3 +1,5 @@ +//! Data access and persistence. + use ::entity::{document, user}; use migration::{Migrator, MigratorTrait}; use sea_orm::{ @@ -16,6 +18,7 @@ pub enum DataStoreError { DatabaseError(#[from] DbErr), } +/// Document progress to insert/update. #[derive(Debug)] pub struct DocumentInsert { id: String, @@ -44,6 +47,7 @@ pub struct Db { } impl Db { + /// Add a new user and return an error if one with `id` already exists. pub async fn add_user(&self, id: &str, key: &str) -> Result { let user = user::ActiveModel { id: Set(id.to_owned()), @@ -53,10 +57,13 @@ impl Db { Ok(user.insert(&self.connection).await?) } + /// Get user with id `id` or None if they do not exist. pub async fn get_user(&self, id: &str) -> Result, DataStoreError> { Ok(user::Entity::find_by_id(id).one(&self.connection).await?) } + /// Get the progress for document with id `doc_id` from user with id `user_id` or None if that + /// combination does not exist. pub async fn get_position( &self, user_id: &str, @@ -69,6 +76,9 @@ impl Db { .await?) } + /// Update progress for document with id `doc_id` from user with id `user_id`. + /// + /// Set the progress timestamp to now. pub async fn update_position(&self, doc: &DocumentInsert) -> Result<(), DataStoreError> { let now = OffsetDateTime::now_utc(); let now = PrimitiveDateTime::new(now.date(), now.time()); @@ -93,6 +103,7 @@ impl Db { } } +/// Connect to the database and return the opened connectoin pool. pub async fn connect(connection_string: &str) -> Result { let connection: DatabaseConnection = Database::connect(connection_string).await?; Migrator::up(&connection, None).await?; diff --git a/app/src/error_response.rs b/app/src/error_response.rs index 2da937f..7127c91 100644 --- a/app/src/error_response.rs +++ b/app/src/error_response.rs @@ -1,3 +1,5 @@ +//! Error handling for the sync service. + use poem::{http::StatusCode, Body, Response}; use poem_openapi::Object; use serde::Serialize; @@ -11,6 +13,8 @@ struct ErrorResponse { message: String, } +/// Log `error` on the server side with a generated id and send back a sanitized error message with +/// the same id. pub fn create_and_log(error: impl Error, cb: F) -> Response where F: Fn() -> (String, StatusCode), diff --git a/app/src/lib.rs b/app/src/lib.rs index 352a3a5..712fe36 100644 --- a/app/src/lib.rs +++ b/app/src/lib.rs @@ -1,3 +1,5 @@ +//! Implementation of a koreader progress sync server. + use anyhow::Result; use api::Api; use app_state::AppState; @@ -18,6 +20,7 @@ pub mod cli; pub mod db; pub mod error_response; +/// Run the progress sync server. pub async fn run(args: &Config, db_url: &str) -> Result<()> { let db = db::connect(db_url).await?; let app_state = Arc::new(AppState {