Session powers
This commit is contained in:
parent
b19f5dd31e
commit
6b0e87e8f8
8 changed files with 50 additions and 30 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -183,9 +183,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "axum-login"
|
||||
version = "0.15.1"
|
||||
version = "0.15.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0fbc0d7bd2577dda9aa9cac096e53b30342725d8eea5798169ff2537a214f45"
|
||||
checksum = "4012877d9672b7902aa6567960208756f68a09de81e988fa18fe369e92f90471"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum",
|
||||
|
|
|
@ -20,7 +20,7 @@ panic = "abort"
|
|||
|
||||
[dependencies]
|
||||
argon2 = { version = "0.5.3", features = ["std"] }
|
||||
axum-login = "0.15.1"
|
||||
axum-login = "0.15.3"
|
||||
bincode = "1.3.3"
|
||||
chrono = { version = "0.4.38", features = ["serde"] }
|
||||
clap = { version = "4.5.4", features = ["derive", "env"] }
|
||||
|
|
|
@ -1,17 +1,22 @@
|
|||
use std::collections::VecDeque;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::song::{Plan, Song, Verse};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct PlaylistEntry {
|
||||
pub song: Song,
|
||||
pub plan_name: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
|
||||
pub struct PlaylistVerseRef {
|
||||
pub song_index: usize,
|
||||
pub map_verse_index: usize,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct Display {
|
||||
pub playlist: VecDeque<PlaylistEntry>,
|
||||
current: PlaylistVerseRef,
|
||||
|
|
|
@ -5,8 +5,9 @@ use std::{
|
|||
};
|
||||
|
||||
use regex::Regex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
|
||||
pub struct Verse {
|
||||
// pub background: String, // url
|
||||
pub content: String,
|
||||
|
@ -21,6 +22,7 @@ impl Verse {
|
|||
/// Sequence of verse names.
|
||||
pub type Plan = VecDeque<String>;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct Song {
|
||||
pub name: String,
|
||||
pub verses: BTreeMap<String, Verse>,
|
||||
|
@ -64,14 +66,14 @@ impl FromStr for Song {
|
|||
// would probably best be done with an actual AST
|
||||
|
||||
static COMMENT_REGEX: OnceLock<Regex> = OnceLock::new();
|
||||
let comment_re = COMMENT_REGEX.get_or_init(|| Regex::new(r"(?s)#[^\n]*").unwrap());
|
||||
let comment_re = COMMENT_REGEX.get_or_init(|| Regex::new(r"(?s)\r?\n?#[^\r\n]*").unwrap());
|
||||
|
||||
let s = comment_re.replace_all(s, "").into_owned();
|
||||
|
||||
dbg!(&s);
|
||||
|
||||
static HUNK_REGEX: OnceLock<Regex> = OnceLock::new();
|
||||
let hunk_re = HUNK_REGEX.get_or_init(|| Regex::new(r"\s*[\n\r]\s*[\n\r]\s*").unwrap());
|
||||
let hunk_re = HUNK_REGEX.get_or_init(|| Regex::new(r"\s*[\r\n]\s*[\r\n]\s*").unwrap());
|
||||
|
||||
let mut hunks = VecDeque::new();
|
||||
let mut last_end: usize = 0;
|
||||
|
@ -170,6 +172,7 @@ A verse"#
|
|||
#[test]
|
||||
fn parses_song_with_comments() {
|
||||
let song: Song = r#"Song Title
|
||||
|
||||
# this is a comment
|
||||
A verse"#
|
||||
.parse()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::router::ReqResult;
|
||||
use crate::{router::ReqResult, service::accounts::AuthSession};
|
||||
use axum::response::Html;
|
||||
use maud::{html, Markup, PreEscaped, DOCTYPE};
|
||||
|
||||
|
@ -33,7 +33,12 @@ pub fn foot() -> Markup {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn page(title: &str, content: Markup) -> ReqResult<Html<String>> {
|
||||
pub fn page(
|
||||
title: &str,
|
||||
content: Markup,
|
||||
auth_session: Option<AuthSession>,
|
||||
) -> ReqResult<Html<String>> {
|
||||
let current_user = auth_session.map(|s| s.user).flatten();
|
||||
Ok(Html(
|
||||
html! {
|
||||
(head(title))
|
||||
|
@ -41,8 +46,13 @@ pub fn page(title: &str, content: Markup) -> ReqResult<Html<String>> {
|
|||
header class="drop-shadow border-b-2 border-surface0 bg-blue-500 flex overflow-x-scroll" {
|
||||
a class="flex p-2 text-3xl font-mono text-mauve opacity-80 hover:bg-mauve hover:text-bg" href="/" { "lyrs" }
|
||||
nav class="flex flex-1 justify-start" {
|
||||
a class="flex items-center p-2 hover:bg-mauve hover:text-bg" href="/auth/login" { "Login" }
|
||||
a class="flex items-center p-2 hover:bg-mauve hover:text-bg" href="/auth/register" { "Register" }
|
||||
@if let Some(user) = current_user {
|
||||
a class="flex items-center p-2 hover:bg-mauve hover:text-bg" href="/dashboard" {( user.username )}
|
||||
a class="flex items-center p-2 hover:bg-mauve hover:text-bg" href="/accounts/logout" { "Logout" }
|
||||
} @else {
|
||||
a class="flex items-center p-2 hover:bg-mauve hover:text-bg" href="/accounts/login" { "Login" }
|
||||
a class="flex items-center p-2 hover:bg-mauve hover:text-bg" href="/accounts/register" { "Register" }
|
||||
}
|
||||
}
|
||||
}
|
||||
main class="flex flex-col flex-1 relative overflow-x-scroll bg-mantle" {
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
use crate::partials::page;
|
||||
use crate::service::accounts::AuthSession;
|
||||
use crate::user::User;
|
||||
use crate::{db, user};
|
||||
use crate::{
|
||||
file_watcher::FileWatcher,
|
||||
prelude::*,
|
||||
service::{auth, static_files},
|
||||
service::{accounts, static_files},
|
||||
state::State as AppState,
|
||||
};
|
||||
use axum::extract::State;
|
||||
|
@ -79,7 +80,7 @@ pub async fn router(
|
|||
};
|
||||
|
||||
let (static_file_service, static_file_watcher) = static_files::router(orl())?;
|
||||
let auth_service = auth::router(state.clone()).unwrap();
|
||||
let accounts_service = accounts::router(state.clone()).unwrap();
|
||||
|
||||
let session_store = tower_sessions_sled_store::SledStore::new(state.db.tree("session")?);
|
||||
let session_layer = SessionManagerLayer::new(session_store);
|
||||
|
@ -88,11 +89,11 @@ pub async fn router(
|
|||
|
||||
let mut result = Router::new()
|
||||
.route("/dashboard", get(dashboard))
|
||||
.route_layer(login_required!(AppState, login_url = "/auth/login"))
|
||||
.route_layer(login_required!(AppState, login_url = "/accounts/login"))
|
||||
.route("/", get(index))
|
||||
.route("/about", get(about))
|
||||
.route("/users", get(users))
|
||||
.nest_service("/auth", auth_service)
|
||||
.nest_service("/accounts", accounts_service)
|
||||
.nest_service("/static", static_file_service)
|
||||
.layer(TraceLayer::new_for_http())
|
||||
.layer(auth_layer)
|
||||
|
@ -107,16 +108,16 @@ pub async fn router(
|
|||
Ok((result, watchers))
|
||||
}
|
||||
|
||||
async fn index() -> ReqResult<Html<String>> {
|
||||
page("index", html! { "Index" })
|
||||
async fn index(auth_session: Option<AuthSession>) -> ReqResult<Html<String>> {
|
||||
page("index", html! { "Index" }, auth_session)
|
||||
}
|
||||
|
||||
async fn about() -> ReqResult<Html<String>> {
|
||||
page("about", html! { "About" })
|
||||
async fn about(auth_session: Option<AuthSession>) -> ReqResult<Html<String>> {
|
||||
page("about", html! { "About" }, auth_session)
|
||||
}
|
||||
|
||||
async fn dashboard() -> ReqResult<Html<String>> {
|
||||
page("dashboard", html! { "Dashboard" })
|
||||
async fn dashboard(auth_session: Option<AuthSession>) -> ReqResult<Html<String>> {
|
||||
page("dashboard", html! { "Dashboard" }, auth_session)
|
||||
}
|
||||
|
||||
async fn users(State(state): State<AppState>) -> ReqResult<String> {
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
pub mod auth;
|
||||
pub mod accounts;
|
||||
pub mod static_files;
|
||||
|
|
|
@ -1,27 +1,27 @@
|
|||
use crate::router::ReqResult;
|
||||
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 maud::html;
|
||||
use std::convert::Infallible;
|
||||
use tracing::instrument;
|
||||
|
||||
use axum::{
|
||||
routing::{get, post},
|
||||
Router,
|
||||
};
|
||||
|
||||
use crate::{prelude::*, user};
|
||||
|
||||
pub fn router(state: AppState) -> Result<Router, Infallible> {
|
||||
Ok(Router::new()
|
||||
.route("/logout", get(logout))
|
||||
.route("/login", get(login))
|
||||
.route("/login", post(authenticate))
|
||||
.route("/register", get(register))
|
||||
.route("/register", post(create_user))
|
||||
.route("/logout", get(logout))
|
||||
.with_state(state))
|
||||
}
|
||||
|
||||
|
@ -37,11 +37,11 @@ async fn login() -> ReqResult<Html<String>> {
|
|||
let subaction = html! {
|
||||
small class="mt-4" {
|
||||
"Need an account? "
|
||||
a href="/auth/register" {"Get one"}
|
||||
a href="/accounts/register" {"Get one"}
|
||||
"."
|
||||
}
|
||||
};
|
||||
page("login", center_hero_form("Login", form, subaction))
|
||||
page("login", center_hero_form("Login", form, subaction), None)
|
||||
}
|
||||
|
||||
async fn register() -> ReqResult<Html<String>> {
|
||||
|
@ -56,14 +56,14 @@ async fn register() -> ReqResult<Html<String>> {
|
|||
let subaction = html! {
|
||||
small class="mt-4" {
|
||||
"Already have an account? "
|
||||
a href="/auth/login" {"Login"}
|
||||
a href="/accounts/login" {"Login"}
|
||||
"."
|
||||
}
|
||||
};
|
||||
page("login", center_hero_form("Register", form, subaction))
|
||||
page("login", center_hero_form("Register", form, subaction), None)
|
||||
}
|
||||
|
||||
type AuthSession = axum_login::AuthSession<AppState>;
|
||||
pub type AuthSession = axum_login::AuthSession<AppState>;
|
||||
|
||||
#[instrument(skip(auth_session))]
|
||||
async fn authenticate(
|
||||
|
@ -111,7 +111,8 @@ async fn create_user(
|
|||
|
||||
pub async fn logout(mut auth_session: AuthSession) -> impl IntoResponse {
|
||||
match auth_session.logout().await {
|
||||
Ok(_) => Redirect::to("/auth/login").into_response(),
|
||||
Ok(Some(_u)) => Redirect::to("/accounts/login?done").into_response(),
|
||||
Ok(None) => Redirect::to("/accounts/login?unauthenticated").into_response(),
|
||||
Err(_) => StatusCode::INTERNAL_SERVER_ERROR.into_response(),
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue