feat: started db cli tool; switched to i64 ids
							parent
							
								
									b343fbccea
								
							
						
					
					
						commit
						1f4b0c35c5
					
				|  | @ -6,8 +6,8 @@ The (initial) goal here is only to support JSON. Other formats *might* be added | ||||||
| on later, no guarantees. | on later, no guarantees. | ||||||
| 
 | 
 | ||||||
| * Authentication API | * Authentication API | ||||||
|     - [ ] Login / Verify Login |     - [x] Login / Verify Login | ||||||
|     - [ ] Logout |     - [x] Logout | ||||||
| * Directory API | * Directory API | ||||||
|     - [ ] Retrieve Top Tags |     - [ ] Retrieve Top Tags | ||||||
|     - [ ] Retrieve Podcasts for Tag |     - [ ] Retrieve Podcasts for Tag | ||||||
|  |  | ||||||
|  | @ -4,6 +4,7 @@ | ||||||
| [print_schema] | [print_schema] | ||||||
| file = "src/db/schema.rs" | file = "src/db/schema.rs" | ||||||
| custom_type_derives = ["diesel::query_builder::QueryId", "Clone"] | custom_type_derives = ["diesel::query_builder::QueryId", "Clone"] | ||||||
|  | sqlite_integer_primary_key_is_bigint = true | ||||||
| 
 | 
 | ||||||
| [migrations_directory] | [migrations_directory] | ||||||
| dir = "migrations" | dir = "migrations" | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| create table users ( | create table users ( | ||||||
|     id bigint primary key not null, |     id integer primary key not null, | ||||||
|     username text unique not null, |     username text unique not null, | ||||||
|     password_hash text not null |     password_hash text not null | ||||||
| ); | ); | ||||||
|  |  | ||||||
|  | @ -0,0 +1,49 @@ | ||||||
|  | use clap::Subcommand; | ||||||
|  | 
 | ||||||
|  | use crate::{db::DbResult, ErrorExt}; | ||||||
|  | 
 | ||||||
|  | /// Tools to view and manage the database.
 | ||||||
|  | #[derive(Subcommand)] | ||||||
|  | pub enum DbCommand { | ||||||
|  |     #[command(subcommand)] | ||||||
|  |     Add(AddCommand), | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Insert a new entity into the database
 | ||||||
|  | #[derive(Subcommand)] | ||||||
|  | pub enum AddCommand { | ||||||
|  |     User { username: String, password: String }, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl DbCommand { | ||||||
|  |     pub fn run(&self, cli: &super::Cli) -> u8 { | ||||||
|  |         match self { | ||||||
|  |             DbCommand::Add(cmd) => cmd.run(cli), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl AddCommand { | ||||||
|  |     pub fn run(&self, cli: &super::Cli) -> u8 { | ||||||
|  |         match self.run_err(cli) { | ||||||
|  |             Ok(()) => 0, | ||||||
|  |             Err(err) => { | ||||||
|  |                 eprintln!("An error occured: {}", err.stack()); | ||||||
|  | 
 | ||||||
|  |                 1 | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn run_err(&self, cli: &super::Cli) -> DbResult<()> { | ||||||
|  |         let pool = crate::db::initialize_db(cli.data_dir.join(crate::DB_FILENAME), false)?; | ||||||
|  | 
 | ||||||
|  |         match self { | ||||||
|  |             Self::User { username, password } => { | ||||||
|  |                 crate::db::NewUser::new(username.clone(), password.clone()).insert(&pool)?; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -1,9 +1,12 @@ | ||||||
|  | mod db; | ||||||
| mod serve; | mod serve; | ||||||
| 
 | 
 | ||||||
| use std::path::PathBuf; | use std::path::PathBuf; | ||||||
| 
 | 
 | ||||||
| use clap::{Parser, Subcommand}; | use clap::{Parser, Subcommand}; | ||||||
| 
 | 
 | ||||||
|  | /// Otter is a lightweight implementation of the Gpodder API, designed to be used for small
 | ||||||
|  | /// personal deployments.
 | ||||||
| #[derive(Parser)] | #[derive(Parser)] | ||||||
| pub struct Cli { | pub struct Cli { | ||||||
|     #[arg(
 |     #[arg(
 | ||||||
|  | @ -21,12 +24,15 @@ pub struct Cli { | ||||||
| #[derive(Subcommand)] | #[derive(Subcommand)] | ||||||
| pub enum Command { | pub enum Command { | ||||||
|     Serve(serve::ServeCommand), |     Serve(serve::ServeCommand), | ||||||
|  |     #[command(subcommand)] | ||||||
|  |     Db(db::DbCommand), | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Cli { | impl Cli { | ||||||
|     pub fn run(&self) -> u8 { |     pub fn run(&self) -> u8 { | ||||||
|         match &self.cmd { |         match &self.cmd { | ||||||
|             Command::Serve(cmd) => cmd.run(self), |             Command::Serve(cmd) => cmd.run(self), | ||||||
|  |             Command::Db(cmd) => cmd.run(self), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -2,8 +2,7 @@ use clap::Args; | ||||||
| 
 | 
 | ||||||
| use crate::{db, server}; | use crate::{db, server}; | ||||||
| 
 | 
 | ||||||
| const DB_FILENAME: &str = "otter.sqlite3"; | /// Run the Otter web server
 | ||||||
| 
 |  | ||||||
| #[derive(Args)] | #[derive(Args)] | ||||||
| pub struct ServeCommand { | pub struct ServeCommand { | ||||||
|     #[arg(
 |     #[arg(
 | ||||||
|  | @ -31,7 +30,7 @@ impl ServeCommand { | ||||||
| 
 | 
 | ||||||
|         tracing::info!("Initializing database and running migrations"); |         tracing::info!("Initializing database and running migrations"); | ||||||
| 
 | 
 | ||||||
|         let pool = db::initialize_db(cli.data_dir.join(DB_FILENAME), true).unwrap(); |         let pool = db::initialize_db(cli.data_dir.join(crate::DB_FILENAME), true).unwrap(); | ||||||
| 
 | 
 | ||||||
|         let ctx = server::Context { pool }; |         let ctx = server::Context { pool }; | ||||||
|         let app = server::app().with_state(ctx); |         let app = server::app().with_state(ctx); | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| use argon2::{password_hash::SaltString, Argon2, PasswordHash, PasswordHasher, PasswordVerifier}; | use argon2::{password_hash::SaltString, Argon2, PasswordHash, PasswordHasher, PasswordVerifier}; | ||||||
| use diesel::prelude::*; | use diesel::{prelude::*, sqlite::Sqlite}; | ||||||
| use rand::rngs::OsRng; | use rand::rngs::OsRng; | ||||||
| use serde::{Deserialize, Serialize}; | use serde::{Deserialize, Serialize}; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										22
									
								
								src/main.rs
								
								
								
								
							
							
						
						
									
										22
									
								
								src/main.rs
								
								
								
								
							|  | @ -4,7 +4,27 @@ mod server; | ||||||
| 
 | 
 | ||||||
| use clap::Parser; | use clap::Parser; | ||||||
| 
 | 
 | ||||||
| use std::process::ExitCode; | use std::{fmt::Write, process::ExitCode}; | ||||||
|  | 
 | ||||||
|  | const DB_FILENAME: &str = "otter.sqlite3"; | ||||||
|  | 
 | ||||||
|  | pub trait ErrorExt: std::error::Error { | ||||||
|  |     /// Return the full chain of error messages
 | ||||||
|  |     fn stack(&self) -> String { | ||||||
|  |         let mut msg = format!("{}", self); | ||||||
|  |         let mut err = self.source(); | ||||||
|  | 
 | ||||||
|  |         while let Some(src) = err { | ||||||
|  |             write!(msg, " - {}", src).unwrap(); | ||||||
|  | 
 | ||||||
|  |             err = src.source(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         msg | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<E: std::error::Error> ErrorExt for E {} | ||||||
| 
 | 
 | ||||||
| fn main() -> ExitCode { | fn main() -> ExitCode { | ||||||
|     let args = cli::Cli::parse(); |     let args = cli::Cli::parse(); | ||||||
|  |  | ||||||
|  | @ -1,8 +1,8 @@ | ||||||
| use std::fmt::{self, Write}; | use std::fmt; | ||||||
| 
 | 
 | ||||||
| use axum::{http::StatusCode, response::IntoResponse}; | use axum::{http::StatusCode, response::IntoResponse}; | ||||||
| 
 | 
 | ||||||
| use crate::db; | use crate::{db, ErrorExt}; | ||||||
| 
 | 
 | ||||||
| pub type AppResult<T> = Result<T, AppError>; | pub type AppResult<T> = Result<T, AppError>; | ||||||
| 
 | 
 | ||||||
|  | @ -40,24 +40,6 @@ impl std::error::Error for AppError { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub trait ErrorExt: std::error::Error { |  | ||||||
|     /// Return the full chain of error messages
 |  | ||||||
|     fn stack(&self) -> String { |  | ||||||
|         let mut msg = format!("{}", self); |  | ||||||
|         let mut err = self.source(); |  | ||||||
| 
 |  | ||||||
|         while let Some(src) = err { |  | ||||||
|             write!(msg, " - {}", src).unwrap(); |  | ||||||
| 
 |  | ||||||
|             err = src.source(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         msg |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl<E: std::error::Error> ErrorExt for E {} |  | ||||||
| 
 |  | ||||||
| impl From<db::DbError> for AppError { | impl From<db::DbError> for AppError { | ||||||
|     fn from(value: db::DbError) -> Self { |     fn from(value: db::DbError) -> Self { | ||||||
|         Self::Db(value) |         Self::Db(value) | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue