Compare commits
No commits in common. "04a0d499e17899a8e89969ee0a3262e137c12776" and "9d1d0eaecd31286eabbd12e5bdee9a9708295e14" have entirely different histories.
04a0d499e1
...
9d1d0eaecd
|
@ -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;
|
||||||
|
|
|
@ -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",
|
||||||
|
|
133
src/page.rs
133
src/page.rs
|
@ -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(),
|
||||||
)
|
)
|
||||||
|
|
|
@ -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");
|
||||||
|
|
Loading…
Reference in New Issue