some quick documentation

This commit is contained in:
Sebastian Hugentobler 2024-07-10 14:48:42 +02:00
parent 8d297920fb
commit 87cfccb4f7
Signed by: shu
GPG Key ID: BB32CF3CA052C2F0
6 changed files with 40 additions and 0 deletions

View File

@ -1,3 +1,5 @@
//! Koreader Progress Sync API.
use poem::{ use poem::{
error::ResponseError, error::ResponseError,
http::StatusCode, http::StatusCode,
@ -19,17 +21,20 @@ use crate::{
error_response, error_response,
}; };
/// Data for registering a new user.
#[derive(Object, Deserialize)] #[derive(Object, Deserialize)]
struct RegisterRequest { struct RegisterRequest {
username: String, username: String,
password: String, password: String,
} }
/// Answer after registering a new user.
#[derive(Object, Serialize)] #[derive(Object, Serialize)]
struct UserCreated { struct UserCreated {
username: String, username: String,
} }
/// Datafor pushing progress.
#[derive(Debug, Clone, Object, Deserialize)] #[derive(Debug, Clone, Object, Deserialize)]
pub struct DocumentUpdate { pub struct DocumentUpdate {
pub device: String, pub device: String,
@ -39,6 +44,7 @@ pub struct DocumentUpdate {
pub progress: String, pub progress: String,
} }
/// Answer when pulling progress.
#[derive(Debug, Clone, Object, Deserialize)] #[derive(Debug, Clone, Object, Deserialize)]
pub struct DocumentProgress { pub struct DocumentProgress {
pub device: String, pub device: String,
@ -70,6 +76,9 @@ pub struct Api;
#[OpenApi] #[OpenApi]
impl Api { impl Api {
/// Register a new user.
///
/// If a user of that id already exist, return a conflict.
#[oai(path = "/users/create", method = "post")] #[oai(path = "/users/create", method = "post")]
async fn register( async fn register(
&self, &self,
@ -88,11 +97,13 @@ impl Api {
} }
} }
/// Return OK if a user authenticated successfully.
#[oai(path = "/users/auth", method = "get", transform = "authorize")] #[oai(path = "/users/auth", method = "get", transform = "authorize")]
async fn login(&self) -> Result<payload::Response<()>> { async fn login(&self) -> Result<payload::Response<()>> {
Ok(payload::Response::new(()).status(StatusCode::OK)) Ok(payload::Response::new(()).status(StatusCode::OK))
} }
/// Push new progress.
#[oai(path = "/syncs/progress", method = "put", transform = "authorize")] #[oai(path = "/syncs/progress", method = "put", transform = "authorize")]
async fn push_progress( async fn push_progress(
&self, &self,
@ -108,6 +119,7 @@ impl Api {
Ok(payload::Response::new(()).status(StatusCode::OK)) Ok(payload::Response::new(()).status(StatusCode::OK))
} }
/// Pull progress for document with id `doc_id`.
#[oai( #[oai(
path = "/syncs/progress/:doc_id", path = "/syncs/progress/:doc_id",
method = "get", method = "get",

View File

@ -1,6 +1,10 @@
//! Global application state.
use crate::{cli::Config, db::Db}; use crate::{cli::Config, db::Db};
pub struct AppState { pub struct AppState {
/// Application configuration.
pub config: Config, pub config: Config,
/// Database connection.
pub db: Db, pub db: Db,
} }

View File

@ -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::{ use poem::{
error::ResponseError, http::StatusCode, Endpoint, EndpointExt, Error, Middleware, Request, error::ResponseError, http::StatusCode, Endpoint, EndpointExt, Error, Middleware, Request,
Result, Result,

View File

@ -1,3 +1,5 @@
//! Data access and persistence.
use ::entity::{document, user}; use ::entity::{document, user};
use migration::{Migrator, MigratorTrait}; use migration::{Migrator, MigratorTrait};
use sea_orm::{ use sea_orm::{
@ -16,6 +18,7 @@ pub enum DataStoreError {
DatabaseError(#[from] DbErr), DatabaseError(#[from] DbErr),
} }
/// Document progress to insert/update.
#[derive(Debug)] #[derive(Debug)]
pub struct DocumentInsert { pub struct DocumentInsert {
id: String, id: String,
@ -44,6 +47,7 @@ pub struct Db {
} }
impl 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<user::Model, DataStoreError> { pub async fn add_user(&self, id: &str, key: &str) -> Result<user::Model, DataStoreError> {
let user = user::ActiveModel { let user = user::ActiveModel {
id: Set(id.to_owned()), id: Set(id.to_owned()),
@ -53,10 +57,13 @@ impl Db {
Ok(user.insert(&self.connection).await?) 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<Option<user::Model>, DataStoreError> { pub async fn get_user(&self, id: &str) -> Result<Option<user::Model>, DataStoreError> {
Ok(user::Entity::find_by_id(id).one(&self.connection).await?) 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( pub async fn get_position(
&self, &self,
user_id: &str, user_id: &str,
@ -69,6 +76,9 @@ impl Db {
.await?) .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> { pub async fn update_position(&self, doc: &DocumentInsert) -> Result<(), DataStoreError> {
let now = OffsetDateTime::now_utc(); let now = OffsetDateTime::now_utc();
let now = PrimitiveDateTime::new(now.date(), now.time()); 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<Db, DataStoreError> { pub async fn connect(connection_string: &str) -> Result<Db, DataStoreError> {
let connection: DatabaseConnection = Database::connect(connection_string).await?; let connection: DatabaseConnection = Database::connect(connection_string).await?;
Migrator::up(&connection, None).await?; Migrator::up(&connection, None).await?;

View File

@ -1,3 +1,5 @@
//! Error handling for the sync service.
use poem::{http::StatusCode, Body, Response}; use poem::{http::StatusCode, Body, Response};
use poem_openapi::Object; use poem_openapi::Object;
use serde::Serialize; use serde::Serialize;
@ -11,6 +13,8 @@ struct ErrorResponse {
message: String, 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<F>(error: impl Error, cb: F) -> Response pub fn create_and_log<F>(error: impl Error, cb: F) -> Response
where where
F: Fn() -> (String, StatusCode), F: Fn() -> (String, StatusCode),

View File

@ -1,3 +1,5 @@
//! Implementation of a koreader progress sync server.
use anyhow::Result; use anyhow::Result;
use api::Api; use api::Api;
use app_state::AppState; use app_state::AppState;
@ -18,6 +20,7 @@ pub mod cli;
pub mod db; pub mod db;
pub mod error_response; pub mod error_response;
/// Run the progress sync server.
pub async fn run(args: &Config, db_url: &str) -> Result<()> { pub async fn run(args: &Config, db_url: &str) -> Result<()> {
let db = db::connect(db_url).await?; let db = db::connect(db_url).await?;
let app_state = Arc::new(AppState { let app_state = Arc::new(AppState {