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. | ||||
| 
 | ||||
| * Authentication API | ||||
|     - [ ] Login / Verify Login | ||||
|     - [ ] Logout | ||||
|     - [x] Login / Verify Login | ||||
|     - [x] Logout | ||||
| * Directory API | ||||
|     - [ ] Retrieve Top Tags | ||||
|     - [ ] Retrieve Podcasts for Tag | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ | |||
| [print_schema] | ||||
| file = "src/db/schema.rs" | ||||
| custom_type_derives = ["diesel::query_builder::QueryId", "Clone"] | ||||
| sqlite_integer_primary_key_is_bigint = true | ||||
| 
 | ||||
| [migrations_directory] | ||||
| dir = "migrations" | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| create table users ( | ||||
|     id bigint primary key not null, | ||||
|     id integer primary key not null, | ||||
|     username text unique 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; | ||||
| 
 | ||||
| use std::path::PathBuf; | ||||
| 
 | ||||
| use clap::{Parser, Subcommand}; | ||||
| 
 | ||||
| /// Otter is a lightweight implementation of the Gpodder API, designed to be used for small
 | ||||
| /// personal deployments.
 | ||||
| #[derive(Parser)] | ||||
| pub struct Cli { | ||||
|     #[arg(
 | ||||
|  | @ -21,12 +24,15 @@ pub struct Cli { | |||
| #[derive(Subcommand)] | ||||
| pub enum Command { | ||||
|     Serve(serve::ServeCommand), | ||||
|     #[command(subcommand)] | ||||
|     Db(db::DbCommand), | ||||
| } | ||||
| 
 | ||||
| impl Cli { | ||||
|     pub fn run(&self) -> u8 { | ||||
|         match &self.cmd { | ||||
|             Command::Serve(cmd) => cmd.run(self), | ||||
|             Command::Db(cmd) => cmd.run(self), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -2,8 +2,7 @@ use clap::Args; | |||
| 
 | ||||
| use crate::{db, server}; | ||||
| 
 | ||||
| const DB_FILENAME: &str = "otter.sqlite3"; | ||||
| 
 | ||||
| /// Run the Otter web server
 | ||||
| #[derive(Args)] | ||||
| pub struct ServeCommand { | ||||
|     #[arg(
 | ||||
|  | @ -31,7 +30,7 @@ impl ServeCommand { | |||
| 
 | ||||
|         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 app = server::app().with_state(ctx); | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| use argon2::{password_hash::SaltString, Argon2, PasswordHash, PasswordHasher, PasswordVerifier}; | ||||
| use diesel::prelude::*; | ||||
| use diesel::{prelude::*, sqlite::Sqlite}; | ||||
| use rand::rngs::OsRng; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										22
									
								
								src/main.rs
								
								
								
								
							
							
						
						
									
										22
									
								
								src/main.rs
								
								
								
								
							|  | @ -4,7 +4,27 @@ mod server; | |||
| 
 | ||||
| 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 { | ||||
|     let args = cli::Cli::parse(); | ||||
|  |  | |||
|  | @ -1,8 +1,8 @@ | |||
| use std::fmt::{self, Write}; | ||||
| use std::fmt; | ||||
| 
 | ||||
| use axum::{http::StatusCode, response::IntoResponse}; | ||||
| 
 | ||||
| use crate::db; | ||||
| use crate::{db, ErrorExt}; | ||||
| 
 | ||||
| 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 { | ||||
|     fn from(value: db::DbError) -> Self { | ||||
|         Self::Db(value) | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue