Big cleanup
This commit is contained in:
parent
f1b1533268
commit
d2b67d5071
15 changed files with 98 additions and 192 deletions
|
@ -2,11 +2,11 @@
|
|||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1714763106,
|
||||
"narHash": "sha256-DrDHo74uTycfpAF+/qxZAMlP/Cpe04BVioJb6fdI0YY=",
|
||||
"lastModified": 1720768451,
|
||||
"narHash": "sha256-EYekUHJE2gxeo2pM/zM9Wlqw1Uw2XTJXOSAO79ksc4Y=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "e9be42459999a253a9f92559b1f5b72e1b44c13d",
|
||||
"rev": "7e7c39ea35c5cdd002cd4588b03a3fb9ece6fad9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
14
src/cli.rs
14
src/cli.rs
|
@ -27,19 +27,7 @@ enum Commands {
|
|||
Admin(admin::Admin),
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum RunError {
|
||||
#[error("run error: {0}")]
|
||||
Run(#[from] run::RunError),
|
||||
|
||||
#[error("admin error: {0}")]
|
||||
Admin(#[from] admin::RunError),
|
||||
|
||||
#[error("{0}")]
|
||||
Eyre(#[from] color_eyre::Report),
|
||||
}
|
||||
|
||||
pub async fn run() -> Result<(), RunError> {
|
||||
pub async fn run() -> AnyResult<()> {
|
||||
let cli = App::parse();
|
||||
observe::setup_logging(&cli.log_env_filter)?;
|
||||
match cli.command {
|
||||
|
|
|
@ -1,15 +1,11 @@
|
|||
use color_eyre::eyre::anyhow;
|
||||
use super::prelude::*;
|
||||
use crate::prelude::*;
|
||||
use crate::{db::Data, user::User};
|
||||
use color_eyre::eyre::eyre;
|
||||
use rand::{distributions::Alphanumeric, thread_rng, Rng};
|
||||
use sled::IVec;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
db::{self, Data},
|
||||
user::{self, User},
|
||||
};
|
||||
|
||||
use super::prelude::*;
|
||||
|
||||
#[derive(Args)]
|
||||
pub struct Admin {
|
||||
#[command(subcommand)]
|
||||
|
@ -17,7 +13,7 @@ pub struct Admin {
|
|||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
pub enum AdminCommands {
|
||||
enum AdminCommands {
|
||||
#[command(subcommand)]
|
||||
Accounts(AccountsCommands),
|
||||
}
|
||||
|
@ -29,17 +25,8 @@ enum AccountsCommands {
|
|||
List(List),
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum RunError {
|
||||
#[error("{0}")]
|
||||
Eyre(#[from] color_eyre::Report),
|
||||
|
||||
#[error("create account error: {0}")]
|
||||
CreateAccount(#[from] CreateAccountError),
|
||||
}
|
||||
|
||||
impl Admin {
|
||||
pub async fn run(&self) -> Result<(), RunError> {
|
||||
pub async fn run(&self) -> AnyResult<()> {
|
||||
match &self.command {
|
||||
AdminCommands::Accounts(accounts) => match accounts {
|
||||
AccountsCommands::Create(args) => Ok(args.run().await?),
|
||||
|
@ -53,7 +40,7 @@ impl Admin {
|
|||
#[derive(Args)]
|
||||
pub struct List {}
|
||||
impl List {
|
||||
pub async fn run(&self) -> Result<(), CreateAccountError> {
|
||||
pub async fn run(&self) -> AnyResult<()> {
|
||||
let db = Data::try_new()?;
|
||||
for entry in db.all::<IVec, User>(User::tree())? {
|
||||
if let Ok((_, user)) = entry {
|
||||
|
@ -80,7 +67,7 @@ impl Delete {
|
|||
user.username, self.id
|
||||
);
|
||||
} else {
|
||||
return Err(anyhow!("user not found"));
|
||||
return Err(eyre!("user not found"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -106,23 +93,8 @@ pub struct Create {
|
|||
pub initial_password: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum CreateAccountError {
|
||||
#[error("password hash error: {0}")]
|
||||
PasswordHash(#[from] argon2::password_hash::Error),
|
||||
|
||||
#[error("data error: {0}")]
|
||||
Data(#[from] db::Error),
|
||||
|
||||
#[error("user error: {0}")]
|
||||
User(#[from] user::Error),
|
||||
|
||||
#[error("bincode error: {0}")]
|
||||
Bincode(#[from] bincode::Error),
|
||||
}
|
||||
|
||||
impl Create {
|
||||
pub async fn run(&self) -> Result<(), CreateAccountError> {
|
||||
pub async fn run(&self) -> AnyResult<()> {
|
||||
// self.email_address
|
||||
let password: String = if let Some(password) = self.initial_password.as_ref() {
|
||||
password.clone()
|
||||
|
@ -141,7 +113,7 @@ impl Create {
|
|||
// TODO: fail2ban?
|
||||
if existing_user.is_some() {
|
||||
// timing/enumeration attacks or something
|
||||
return Err(user::Error::UsernameExists(Box::new(self.username.clone())).into());
|
||||
return Err(eyre!("username already exists: {}", self.username));
|
||||
}
|
||||
|
||||
db.insert(User::tree(), &user.username, bincode::serialize(&user)?)?;
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
use super::prelude::*;
|
||||
use crate::{
|
||||
router::NewRouterError,
|
||||
state::{NewStateError, State},
|
||||
};
|
||||
use crate::cli::prelude::*;
|
||||
use crate::prelude::*;
|
||||
use crate::state::State;
|
||||
|
||||
/// Run the web application server
|
||||
#[derive(Args)]
|
||||
|
@ -28,20 +26,8 @@ pub struct Run {
|
|||
// pub database_reset: bool,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum RunError {
|
||||
#[error("router error: {0}")]
|
||||
Router(#[from] NewRouterError),
|
||||
|
||||
#[error("io error: {0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
|
||||
#[error("new state error: {0}")]
|
||||
State(#[from] NewStateError),
|
||||
}
|
||||
|
||||
impl Run {
|
||||
pub async fn run(&self) -> Result<(), RunError> {
|
||||
pub async fn run(&self) -> AnyResult<()> {
|
||||
let app_state = State::try_new().await?;
|
||||
let (router, _watchers) = crate::router::router(app_state, self.watch).await?;
|
||||
Ok(
|
||||
|
|
29
src/db.rs
29
src/db.rs
|
@ -1,29 +1,20 @@
|
|||
use crate::prelude::*;
|
||||
use serde::de::DeserializeOwned;
|
||||
use sled::{Db, IVec, Tree};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Data {
|
||||
db: Db,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("sled error: {0}")]
|
||||
Sled(#[from] sled::Error),
|
||||
|
||||
#[error("bincode error: {0}")]
|
||||
Binccode(#[from] Box<bincode::ErrorKind>),
|
||||
}
|
||||
|
||||
impl Data {
|
||||
pub fn try_new() -> Result<Self, Error> {
|
||||
pub fn try_new() -> AnyResult<Self> {
|
||||
Ok(Self {
|
||||
db: sled::open("data/lyrs")?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn delete<K, V>(&self, tree_name: &str, key: K) -> Result<Option<V>, Error>
|
||||
pub fn delete<K, V>(&self, tree_name: &str, key: K) -> AnyResult<Option<V>>
|
||||
where
|
||||
K: AsRef<[u8]>,
|
||||
V: DeserializeOwned,
|
||||
|
@ -34,7 +25,7 @@ impl Data {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get<K, V>(&self, tree_name: &str, key: K) -> Result<Option<V>, Error>
|
||||
pub fn get<K, V>(&self, tree_name: &str, key: K) -> AnyResult<Option<V>>
|
||||
where
|
||||
K: AsRef<[u8]>,
|
||||
V: DeserializeOwned,
|
||||
|
@ -50,14 +41,14 @@ impl Data {
|
|||
tree_name: &str,
|
||||
key: K,
|
||||
value: V,
|
||||
) -> Result<Option<IVec>, Error> {
|
||||
) -> AnyResult<Option<IVec>> {
|
||||
Ok(self.db.open_tree(tree_name)?.insert(key, value.into())?)
|
||||
}
|
||||
|
||||
pub fn all<'de, K, V>(
|
||||
&self,
|
||||
tree_name: &str,
|
||||
) -> Result<impl Iterator<Item = Result<(K, V), Error>>, Error>
|
||||
) -> AnyResult<impl Iterator<Item = AnyResult<(K, V)>>>
|
||||
where
|
||||
V: DeserializeOwned,
|
||||
K: From<IVec>,
|
||||
|
@ -69,17 +60,17 @@ impl Data {
|
|||
.map(|r| match r {
|
||||
Ok((k, v)) => {
|
||||
let key = K::from(k);
|
||||
match bincode::deserialize::<V>(&v).map_err(Error::from) {
|
||||
match bincode::deserialize::<V>(&v) {
|
||||
Ok(v) => Ok((key, v)),
|
||||
Err(err) => Err(Error::from(err)),
|
||||
Err(err) => Err(err.into()),
|
||||
}
|
||||
}
|
||||
Err(err) => Err(Error::from(err)),
|
||||
Err(err) => Err(err.into()),
|
||||
})
|
||||
.into_iter())
|
||||
}
|
||||
|
||||
pub fn tree(&self, name: &str) -> Result<Tree, Error> {
|
||||
pub fn tree(&self, name: &str) -> AnyResult<Tree> {
|
||||
Ok(self.db.open_tree(name)?)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,6 @@ mod webserver;
|
|||
use crate::prelude::*;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> AnonResult<()> {
|
||||
async fn main() -> AnyResult<()> {
|
||||
Ok(cli::run().await?)
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use std::collections::VecDeque;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
#![allow(dead_code)]
|
||||
|
||||
use super::song::{Plan, Song, Verse};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::VecDeque;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct PlaylistEntry {
|
||||
|
@ -89,6 +89,7 @@ impl Display {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
use std::{
|
||||
collections::{BTreeMap, VecDeque},
|
||||
str::FromStr,
|
||||
|
@ -145,6 +147,7 @@ impl FromStr for Song {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use std::collections::VecDeque;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{router::ReqResult, service::accounts::AuthSession};
|
||||
use crate::service::accounts::AuthSession;
|
||||
use axum::response::Html;
|
||||
use maud::{html, Markup, PreEscaped, DOCTYPE};
|
||||
|
||||
|
@ -27,20 +27,16 @@ pub fn foot() -> Markup {
|
|||
(PreEscaped("© 2024 "))
|
||||
a class="underline text-mauve" href="https://lyte.dev" { "lytedev" }
|
||||
}
|
||||
section .ml-auto {("Made with ❤️")}
|
||||
section .ml-auto {"Made with ❤️"}
|
||||
" "
|
||||
a .underline.text-mauve href="/about" { "About" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn page(
|
||||
title: &str,
|
||||
content: Markup,
|
||||
auth_session: Option<AuthSession>,
|
||||
) -> ReqResult<Html<String>> {
|
||||
pub fn page(title: &str, content: Markup, auth_session: Option<AuthSession>) -> Html<String> {
|
||||
let current_user = auth_session.map(|s| s.user).flatten();
|
||||
Ok(Html(
|
||||
Html(
|
||||
html! {
|
||||
(head(title))
|
||||
body hx-boost="true" class="bg-bg text-text min-h-lvh flex flex-col font-sans overflow-x-hidden" {
|
||||
|
@ -60,7 +56,7 @@ pub fn page(
|
|||
}
|
||||
(foot())
|
||||
}.into_string()
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
pub fn center_hero_form(title: &str, content: Markup, subform: Markup) -> Markup {
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#![allow(unused_imports)]
|
||||
|
||||
pub use color_eyre::eyre::Result as AnonResult;
|
||||
pub use color_eyre::eyre::eyre;
|
||||
pub use color_eyre::eyre::Error;
|
||||
pub use color_eyre::eyre::Result as AnyResult;
|
||||
pub use std::result::Result;
|
||||
pub use tracing::{debug, error, event, info, instrument, span, trace, warn, Level};
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use crate::partials::page;
|
||||
use crate::service::accounts::AuthSession;
|
||||
use crate::user::User;
|
||||
use crate::{db, user};
|
||||
use crate::{
|
||||
file_watcher::FileWatcher,
|
||||
prelude::*,
|
||||
|
@ -9,62 +8,51 @@ use crate::{
|
|||
state::State as AppState,
|
||||
};
|
||||
use axum::extract::State;
|
||||
use axum::{
|
||||
http::StatusCode,
|
||||
response::{Html, IntoResponse},
|
||||
routing::get,
|
||||
Router,
|
||||
};
|
||||
use axum::http::StatusCode;
|
||||
use axum::response::{Html, IntoResponse};
|
||||
use axum::{routing::get, Router};
|
||||
use axum_login::{login_required, AuthManagerLayerBuilder};
|
||||
use maud::html;
|
||||
use sled::IVec;
|
||||
use thiserror::Error;
|
||||
use tower_http::trace::TraceLayer;
|
||||
use tower_livereload::LiveReloadLayer;
|
||||
use tower_sessions::SessionManagerLayer;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum NewRouterError {
|
||||
#[error("watcher error: {0}")]
|
||||
Watcher(#[from] notify::Error),
|
||||
#[derive(Debug)]
|
||||
#[allow(dead_code)]
|
||||
pub struct WebError(Error);
|
||||
|
||||
#[error("database error: {0}")]
|
||||
Database(#[from] db::Error),
|
||||
impl From<Error> for WebError {
|
||||
fn from(value: Error) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ReqError {
|
||||
#[error("argon2 error: {0}")]
|
||||
Argon2(#[from] argon2::password_hash::Error),
|
||||
|
||||
#[error("bincode error: {0}")]
|
||||
Bincode(#[from] bincode::Error),
|
||||
|
||||
#[error("database error: {0}")]
|
||||
Database(#[from] db::Error),
|
||||
|
||||
#[error("user error: {0}")]
|
||||
User(#[from] user::Error),
|
||||
impl From<bincode::ErrorKind> for WebError {
|
||||
fn from(value: bincode::ErrorKind) -> Self {
|
||||
Self(value.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoResponse for ReqError {
|
||||
impl From<Box<bincode::ErrorKind>> for WebError {
|
||||
fn from(value: Box<bincode::ErrorKind>) -> Self {
|
||||
Self(value.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoResponse for WebError {
|
||||
fn into_response(self) -> axum::http::Response<axum::body::Body> {
|
||||
error!("webserver error: {:?}", self);
|
||||
(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
// TODO: don't expose raw errors over the internet?
|
||||
format!("internal server error: {}", self),
|
||||
)
|
||||
.into_response()
|
||||
(StatusCode::INTERNAL_SERVER_ERROR, "internal server error").into_response()
|
||||
}
|
||||
}
|
||||
|
||||
pub type ReqResult<T> = Result<T, ReqError>;
|
||||
pub type WebResult<T> = Result<T, WebError>;
|
||||
|
||||
pub async fn router(
|
||||
state: AppState,
|
||||
with_watchers: bool,
|
||||
) -> Result<(Router, Vec<Option<FileWatcher>>), NewRouterError> {
|
||||
) -> AnyResult<(Router, Vec<Option<FileWatcher>>)> {
|
||||
let live_reload_layer: Option<LiveReloadLayer> = if with_watchers {
|
||||
Some(LiveReloadLayer::new())
|
||||
} else {
|
||||
|
@ -109,7 +97,7 @@ pub async fn router(
|
|||
Ok((result, watchers))
|
||||
}
|
||||
|
||||
async fn index(auth_session: Option<AuthSession>) -> ReqResult<Html<String>> {
|
||||
async fn index(auth_session: Option<AuthSession>) -> Html<String> {
|
||||
page(
|
||||
"index",
|
||||
html! {
|
||||
|
@ -124,7 +112,7 @@ async fn index(auth_session: Option<AuthSession>) -> ReqResult<Html<String>> {
|
|||
)
|
||||
}
|
||||
|
||||
async fn about(auth_session: Option<AuthSession>) -> ReqResult<Html<String>> {
|
||||
async fn about(auth_session: Option<AuthSession>) -> Html<String> {
|
||||
page(
|
||||
"about",
|
||||
html! {
|
||||
|
@ -139,7 +127,7 @@ async fn about(auth_session: Option<AuthSession>) -> ReqResult<Html<String>> {
|
|||
)
|
||||
}
|
||||
|
||||
async fn dashboard(auth_session: Option<AuthSession>) -> ReqResult<Html<String>> {
|
||||
async fn dashboard(auth_session: Option<AuthSession>) -> Html<String> {
|
||||
page(
|
||||
"dashboard",
|
||||
html! {
|
||||
|
@ -149,7 +137,7 @@ async fn dashboard(auth_session: Option<AuthSession>) -> ReqResult<Html<String>>
|
|||
)
|
||||
}
|
||||
|
||||
async fn users(State(state): State<AppState>) -> ReqResult<String> {
|
||||
async fn users(State(state): State<AppState>) -> WebResult<String> {
|
||||
let mut s = String::new();
|
||||
let mut users = state.db.all::<IVec, User>(User::tree())?;
|
||||
while let Some(Ok((_, user))) = users.next() {
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
use crate::router::ReqResult;
|
||||
use crate::prelude::*;
|
||||
use crate::router::WebResult;
|
||||
use crate::state::{Creds, State as AppState};
|
||||
use crate::{partials::*, user::User};
|
||||
use crate::{prelude::*, user};
|
||||
use axum::extract::State;
|
||||
use axum::http::StatusCode;
|
||||
use axum::response::{Html, IntoResponse, Redirect};
|
||||
use axum::Form;
|
||||
use color_eyre::eyre::eyre;
|
||||
use maud::html;
|
||||
use std::convert::Infallible;
|
||||
use tracing::instrument;
|
||||
|
@ -93,14 +94,14 @@ async fn authenticate(
|
|||
async fn create_user(
|
||||
State(state): State<AppState>,
|
||||
Form(creds): Form<Creds>,
|
||||
) -> ReqResult<Html<String>> {
|
||||
) -> WebResult<Html<String>> {
|
||||
let user = User::try_new(&creds.username, creds.password.expose_secret())?;
|
||||
let existing_user: Option<User> = state.db.get(User::tree(), &creds.username)?;
|
||||
|
||||
// TODO: fail2ban?
|
||||
if existing_user.is_some() {
|
||||
// timing/enumeration attacks or something
|
||||
return Err(user::Error::UsernameExists(Box::new(creds.username)).into());
|
||||
return Err(eyre!("username exists: {}", creds.username).into());
|
||||
}
|
||||
|
||||
state
|
||||
|
|
31
src/state.rs
31
src/state.rs
|
@ -1,13 +1,8 @@
|
|||
use crate::{
|
||||
db::{self, Data},
|
||||
prelude::*,
|
||||
user::{self, User},
|
||||
};
|
||||
use crate::{db::Data, prelude::*, user::User};
|
||||
use axum::async_trait;
|
||||
use axum_login::{AuthnBackend, UserId};
|
||||
use redact::Secret;
|
||||
use serde::Deserialize;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct State {
|
||||
|
@ -15,42 +10,30 @@ pub struct State {
|
|||
}
|
||||
|
||||
impl State {
|
||||
pub async fn try_new() -> Result<Self, NewStateError> {
|
||||
pub async fn try_new() -> AnyResult<Self> {
|
||||
Ok(Self {
|
||||
db: Data::try_new()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum NewStateError {
|
||||
#[error("database error: {0}")]
|
||||
Database(#[from] db::Error),
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Clone)]
|
||||
pub struct Creds {
|
||||
pub username: String,
|
||||
pub password: Secret<String>,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum AuthError {
|
||||
#[error("user error: {0}")]
|
||||
User(#[from] user::Error),
|
||||
|
||||
#[error("data error: {0}")]
|
||||
Db(#[from] db::Error),
|
||||
|
||||
#[error("data error: {0}")]
|
||||
Argon2(#[from] argon2::password_hash::Error),
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum AuthnError {
|
||||
#[error("{0}")]
|
||||
Eyre(#[from] Error),
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl AuthnBackend for State {
|
||||
type User = User;
|
||||
type Credentials = Creds;
|
||||
type Error = AuthError;
|
||||
type Error = AuthnError;
|
||||
|
||||
async fn authenticate(
|
||||
&self,
|
||||
|
|
19
src/user.rs
19
src/user.rs
|
@ -3,7 +3,6 @@ use axum_login::AuthUser;
|
|||
use chrono::Utc;
|
||||
use redact::{expose_secret, Secret};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
use uuid::Uuid;
|
||||
|
||||
pub const USER_TREE: &str = "user";
|
||||
|
@ -19,21 +18,12 @@ pub struct User {
|
|||
pub registered_at: chrono::DateTime<Utc>,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("username exists: {0}")]
|
||||
UsernameExists(Box<String>),
|
||||
|
||||
#[error("username not found: {0}")]
|
||||
UsernameNotFound(Box<String>),
|
||||
}
|
||||
|
||||
impl User {
|
||||
pub const fn tree() -> &'static str {
|
||||
USER_TREE
|
||||
}
|
||||
|
||||
pub fn try_new(username: &str, password: &str) -> Result<Self, argon2::password_hash::Error> {
|
||||
pub fn try_new(username: &str, password: &str) -> AnyResult<Self> {
|
||||
let now = Utc::now();
|
||||
Ok(Self {
|
||||
id: uuid::v7(now),
|
||||
|
@ -43,8 +33,11 @@ impl User {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn verify(&self, password: &str) -> Result<(), argon2::password_hash::Error> {
|
||||
crate::auth::verified_password(password, self.password_digest.expose_secret())
|
||||
pub fn verify(&self, password: &str) -> AnyResult<()> {
|
||||
Ok(crate::auth::verified_password(
|
||||
password,
|
||||
self.password_digest.expose_secret(),
|
||||
)?)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
use chrono::{DateTime, TimeZone, Utc};
|
||||
pub use uuid::Uuid;
|
||||
use uuid::{NoContext, Timestamp};
|
||||
|
|
Loading…
Reference in a new issue