Compare commits

..

No commits in common. "04a0d499e17899a8e89969ee0a3262e137c12776" and "9d1d0eaecd31286eabbd12e5bdee9a9708295e14" have entirely different histories.

4 changed files with 84 additions and 143 deletions

View File

@ -8,7 +8,7 @@
outputs = { self, nixpkgs, rust-overlay, flake-utils, ... }: outputs = { self, nixpkgs, rust-overlay, flake-utils, ... }:
flake-utils.lib.eachDefaultSystem (system: flake-utils.lib.eachDefaultSystem (system:
let let
tag = "v0.1.5-alpha"; tag = "v0.1.4_4-alpha";
overlays = [ (import rust-overlay) ]; overlays = [ (import rust-overlay) ];
pkgs = import nixpkgs { pkgs = import nixpkgs {
inherit system overlays; inherit system overlays;

View File

@ -23,7 +23,6 @@ pub enum Tag {
Dialog, Dialog,
Article, Article,
Header, Header,
Footer,
Div, Div,
@ -52,7 +51,6 @@ pub enum Tag {
Anchor, Anchor,
Button, Button,
HeaderGroup,
Header1, Header1,
Header2, Header2,
Header3, Header3,
@ -87,7 +85,6 @@ impl Tag {
Self::Dialog => "dialog", Self::Dialog => "dialog",
Self::Article => "article", Self::Article => "article",
Self::Header => "header", Self::Header => "header",
Self::Footer => "footer",
Self::Div => "div", Self::Div => "div",
@ -114,7 +111,6 @@ impl Tag {
Self::Anchor => "a", Self::Anchor => "a",
Self::Button => "button", Self::Button => "button",
Self::HeaderGroup => "hgroup",
Self::Header1 => "h1", Self::Header1 => "h1",
Self::Header2 => "h2", Self::Header2 => "h2",
Self::Header3 => "h3", Self::Header3 => "h3",

View File

@ -1,7 +1,7 @@
use crate::{ use crate::{
auth::{self, User}, auth::{self, User},
htmx::{Build, HtmxBuilder, Tag}, htmx::{Build, HtmxBuilder, SwapMethod, Tag},
settings::{ApiState, GuildSettings, Intro, IntroFriendlyName}, settings::{ApiState, Intro, IntroFriendlyName},
}; };
use axum::{ use axum::{
extract::{Path, State}, extract::{Path, State},
@ -9,64 +9,14 @@ use axum::{
}; };
use tracing::error; use tracing::error;
fn page_header(title: &str) -> HtmxBuilder { pub(crate) async fn home(user: Option<User>) -> Redirect {
HtmxBuilder::new(Tag::Html).head(|b| { if user.is_some() {
b.title(title) Redirect::to("/guild/588149178912473103")
.script(
"https://unpkg.com/htmx.org@1.9.3",
Some("sha384-lVb3Rd/Ca0AxaoZg5sACe8FJKF0tnUgR2Kd7ehUOG5GCcROv5uBIZsOqovBAcWua"),
)
// Not currently using
// .script("https://unpkg.com/hyperscript.org@0.9.9", None)
.style_link("https://cdn.jsdelivr.net/npm/@picocss/pico@1/css/pico.min.css")
})
}
pub(crate) async fn home(
State(state): State<ApiState>,
user: Option<User>,
) -> Result<Html<String>, Redirect> {
if let Some(user) = user {
let settings = state.settings.lock().await;
let guild = settings
.guilds
.iter()
.filter(|(_, guild_settings)| guild_settings.users.contains_key(&user.name));
Ok(Html(
page_header("MemeJoin - Home")
.builder(Tag::Div, |b| {
b.attribute("class", "container")
.builder_text(Tag::Header2, "Choose a Guild")
.push_builder(guild_list(&state.origin, guild))
})
.build(),
))
} else { } else {
Err(Redirect::to(&format!("{}/login", state.origin))) Redirect::to("/login")
} }
} }
fn guild_list<'a>(
origin: &str,
guilds: impl Iterator<Item = (&'a u64, &'a GuildSettings)>,
) -> HtmxBuilder {
HtmxBuilder::new(Tag::Empty).ul(|b| {
let mut b = b;
for (guild_id, guild_settings) in guilds {
b = b.li(|b| {
b.link(
&guild_settings.name,
&format!("{}/guild/{}", origin, guild_id),
)
});
}
b
})
}
fn intro_list<'a>( fn intro_list<'a>(
intros: impl Iterator<Item = (&'a String, &'a Intro)>, intros: impl Iterator<Item = (&'a String, &'a Intro)>,
label: &str, label: &str,
@ -79,7 +29,7 @@ fn intro_list<'a>(
.builder(Tag::FieldSet, |b| { .builder(Tag::FieldSet, |b| {
let mut b = b let mut b = b
.attribute("class", "container") .attribute("class", "container")
.attribute("style", "max-height: 50%; overflow-y: scroll"); .attribute("style", "max-height: 20%; overflow-y: scroll");
for intro in intros { for intro in intros {
b = b.builder(Tag::Label, |b| { b = b.builder(Tag::Label, |b| {
b.builder(Tag::Input, |b| { b.builder(Tag::Input, |b| {
@ -104,42 +54,40 @@ pub(crate) async fn guild_dashboard(
let Some(guild) = settings.guilds.get(&guild_id) else { let Some(guild) = settings.guilds.get(&guild_id) else {
error!(%guild_id, "no such guild"); error!(%guild_id, "no such guild");
return Err(Redirect::to(&format!("{}/", state.origin))); return Err(Redirect::to("/"));
}; };
let Some(guild_user) = guild.users.get(&user.name) else { let Some(guild_user) = guild.users.get(&user.name) else {
error!(%guild_id, %user.name, "no user in guild"); error!(%guild_id, %user.name, "no user in guild");
return Err(Redirect::to(&format!("{}/", state.origin))); return Err(Redirect::to("/"));
}; };
let is_moderator = guild_user.permissions.can(auth::Permission::DeleteSounds); let is_moderator = guild_user.permissions.can(auth::Permission::DeleteSounds);
Ok(Html( Ok(Html(
HtmxBuilder::new(Tag::Html) HtmxBuilder::new(Tag::Html)
.push_builder(page_header("MemeJoin - Dashboard")) .head(|b| {
b.title("MemeJoin - Dashboard")
.script(
"https://unpkg.com/htmx.org@1.9.3",
Some("sha384-lVb3Rd/Ca0AxaoZg5sACe8FJKF0tnUgR2Kd7ehUOG5GCcROv5uBIZsOqovBAcWua"),
)
.script("https://unpkg.com/hyperscript.org@0.9.9", None)
.style_link("https://cdn.jsdelivr.net/npm/@picocss/pico@1/css/pico.min.css")
})
.builder(Tag::Nav, |b| { .builder(Tag::Nav, |b| {
b.builder(Tag::HeaderGroup, |b| { b.builder(Tag::Header1, |b| b.text("MemeJoin - A bot for user intros"))
b.attribute("class", "container") .builder_text(Tag::Paragraph, &user.name)
.builder(Tag::Header1, |b| b.text("MemeJoin - A bot for user intros"))
.builder_text(Tag::Header6, &user.name)
}) })
}) .builder(Tag::Main, |b| {
.builder(Tag::Empty, |b| {
if is_moderator { if is_moderator {
b.builder(Tag::Div, |b| { b.builder(Tag::Article, |b| {
b.attribute("class", "container")
.builder(Tag::Article, |b| {
b.builder_text(Tag::Header, "Wow, you're a moderator") b.builder_text(Tag::Header, "Wow, you're a moderator")
.push_builder(moderator_dashboard(&state))
.builder_text(Tag::Footer, "End of super cool mod section")
})
}) })
} else { } else {
b b
} }
.builder(Tag::Div, |b| {
b.attribute("class", "container")
.builder(Tag::Article, |b| { .builder(Tag::Article, |b| {
let mut b = b.builder_text(Tag::Header, "Guild Intros"); let mut b = b.builder_text(Tag::Header, "Guild Settings");
for (channel_name, channel_settings) in &guild.channels { for (channel_name, channel_settings) in &guild.channels {
if let Some(channel_user) = channel_settings.users.get(&user.name) { if let Some(channel_user) = channel_settings.users.get(&user.name) {
@ -150,8 +98,7 @@ pub(crate) async fn guild_dashboard(
guild.intros.get(&intro_index.index)?, guild.intros.get(&intro_index.index)?,
)) ))
}); });
let available_intros = let available_intros = guild.intros.iter().filter_map(|intro| {
guild.intros.iter().filter_map(|intro| {
if !channel_user if !channel_user
.intros .intros
.iter() .iter()
@ -162,17 +109,16 @@ pub(crate) async fn guild_dashboard(
None None
} }
}); });
b = b.builder(Tag::Article, |b| { b = b
b.builder_text(Tag::Header, channel_name).builder( .builder_text(Tag::Strong, channel_name)
Tag::Div, .builder(Tag::Div, |b| {
|b| {
b.builder_text(Tag::Strong, "Your Current Intros") b.builder_text(Tag::Strong, "Your Current Intros")
.push_builder(intro_list( .push_builder(intro_list(
current_intros, current_intros,
"Remove Intro", "Remove Intro",
&format!( &format!(
"{}/v2/intros/remove/{}/{}", "/v2/intros/remove/{}/{}",
state.origin, guild_id, channel_name guild_id, channel_name
), ),
)) ))
.builder_text(Tag::Strong, "Select Intros") .builder_text(Tag::Strong, "Select Intros")
@ -180,12 +126,10 @@ pub(crate) async fn guild_dashboard(
available_intros, available_intros,
"Add Intro", "Add Intro",
&format!( &format!(
"{}/v2/intros/add/{}/{}", "/v2/intros/add/{}/{}",
state.origin, guild_id, channel_name guild_id, channel_name
), ),
)) ))
},
)
}); });
} }
} }
@ -193,21 +137,24 @@ pub(crate) async fn guild_dashboard(
b b
}) })
}) })
})
.build(), .build(),
)) ))
} }
fn moderator_dashboard(state: &ApiState) -> HtmxBuilder {
HtmxBuilder::new(Tag::Empty).link("Go back to old UI", &format!("{}/old", state.origin))
}
pub(crate) async fn login(State(state): State<ApiState>) -> Html<String> { pub(crate) async fn login(State(state): State<ApiState>) -> Html<String> {
let authorize_uri = format!("https://discord.com/api/oauth2/authorize?client_id={}&redirect_uri={}/v2/auth&response_type=code&scope=guilds.members.read%20guilds%20identify", state.secrets.client_id, state.origin); let authorize_uri = format!("https://discord.com/api/oauth2/authorize?client_id={}&redirect_uri={}/v2/auth&response_type=code&scope=guilds.members.read%20guilds%20identify", state.secrets.client_id, state.origin);
Html( Html(
HtmxBuilder::new(Tag::Html) HtmxBuilder::new(Tag::Html)
.push_builder(page_header("MemeJoin - Login")) .head(|b| {
b.title("MemeJoin - Login")
.script(
"https://unpkg.com/htmx.org@1.9.3",
Some("sha384-lVb3Rd/Ca0AxaoZg5sACe8FJKF0tnUgR2Kd7ehUOG5GCcROv5uBIZsOqovBAcWua"),
)
.script("https://unpkg.com/hyperscript.org@0.9.9", None)
.style_link("https://cdn.jsdelivr.net/npm/@picocss/pico@1/css/pico.min.css")
})
.link("Login", &authorize_uri) .link("Login", &authorize_uri)
.build(), .build(),
) )

View File

@ -9,7 +9,7 @@ use axum::{
}; };
use axum_extra::extract::{cookie::Cookie, CookieJar}; use axum_extra::extract::{cookie::Cookie, CookieJar};
use reqwest::{Proxy, StatusCode, Url}; use reqwest::{Proxy, StatusCode};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::{json, Value}; use serde_json::{json, Value};
use tracing::{error, info}; use tracing::{error, info};
@ -221,13 +221,11 @@ pub(crate) async fn v2_auth(
); );
// TODO: add permissions based on roles // TODO: add permissions based on roles
let uri = Url::parse(&state.origin).expect("should be a valid url");
let mut cookie = Cookie::new("access_token", token.clone()); let mut cookie = Cookie::new("access_token", token.clone());
cookie.set_path(uri.path().to_string()); cookie.set_path("/");
cookie.set_secure(true); cookie.set_secure(true);
Ok((jar.add(cookie), Redirect::to(&format!("{}/", state.origin)))) Ok((jar.add(cookie), Redirect::to("/")))
} }
pub(crate) async fn auth( pub(crate) async fn auth(
@ -242,7 +240,7 @@ pub(crate) async fn auth(
let mut data = HashMap::new(); let mut data = HashMap::new();
let redirect_uri = format!("{}/old/auth", state.origin); let redirect_uri = format!("{}/auth", state.origin);
data.insert("client_id", state.secrets.client_id.as_str()); data.insert("client_id", state.secrets.client_id.as_str());
data.insert("client_secret", state.secrets.client_secret.as_str()); data.insert("client_secret", state.secrets.client_secret.as_str());
data.insert("grant_type", "authorization_code"); data.insert("grant_type", "authorization_code");