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