parent
1ed1e55db4
commit
f5e976103c
|
@ -34,6 +34,7 @@
|
|||
pkg-config
|
||||
gcc
|
||||
openssl
|
||||
pkg-config
|
||||
python3
|
||||
ffmpeg
|
||||
cmake
|
||||
|
@ -47,7 +48,8 @@
|
|||
name = "memejoin-rs";
|
||||
version = "0.1.2-alpha";
|
||||
src = self;
|
||||
nativeBuildInputs = [ local-rust cmake gcc libopus ];
|
||||
buildInputs = [ openssl.dev ];
|
||||
nativeBuildInputs = [ local-rust pkg-config openssl openssl.dev cmake gcc libopus ];
|
||||
|
||||
cargoLock = {
|
||||
lockFile = ./Cargo.lock;
|
||||
|
@ -59,7 +61,7 @@
|
|||
tag = "0.1.2-alpha";
|
||||
copyToRoot = buildEnv {
|
||||
name = "image-root";
|
||||
paths = [ default ffmpeg libopus youtube-dl ];
|
||||
paths = [ default cacert openssl openssl.dev ffmpeg libopus youtube-dl yt-dlp ];
|
||||
};
|
||||
runAsRoot = ''
|
||||
#!${runtimeShell}
|
||||
|
|
|
@ -27,8 +27,8 @@ pub(crate) struct User {
|
|||
pub(crate) name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub(crate) struct Permissions(u8);
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||
pub(crate) struct Permissions(pub(crate) u8);
|
||||
impl Default for Permissions {
|
||||
fn default() -> Permissions {
|
||||
Permissions(0)
|
||||
|
|
|
@ -129,7 +129,7 @@ fn spawn_api(settings: Arc<Mutex<Settings>>) {
|
|||
let api = Router::new()
|
||||
.route("/health", get(routes::health))
|
||||
.route("/me", get(routes::me))
|
||||
.route("/intros/:guild/add/:url", get(routes::add_guild_intro))
|
||||
.route("/intros/:guild/add", get(routes::add_guild_intro))
|
||||
.route("/intros/:guild", get(routes::intros))
|
||||
.route(
|
||||
"/intros/:guild/:channel/:intro",
|
||||
|
@ -142,12 +142,13 @@ fn spawn_api(settings: Arc<Mutex<Settings>>) {
|
|||
.route("/auth", get(routes::auth))
|
||||
.layer(
|
||||
CorsLayer::new()
|
||||
.allow_origin(Any)
|
||||
// TODO: move this to env variable
|
||||
.allow_origin(["https://spacegirl.nl".parse().unwrap()])
|
||||
.allow_headers(Any)
|
||||
.allow_methods([Method::GET, Method::POST]),
|
||||
)
|
||||
.with_state(Arc::new(state));
|
||||
let addr = SocketAddr::from(([0, 0, 0, 0], 7756));
|
||||
let addr = SocketAddr::from(([0, 0, 0, 0], 8080));
|
||||
info!("socket listening on {addr}");
|
||||
axum::Server::bind(&addr)
|
||||
.serve(api.into_make_service())
|
||||
|
|
|
@ -13,7 +13,7 @@ use serde_json::{json, Value};
|
|||
use tracing::{error, info};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::settings::{ApiState, Intro, IntroIndex};
|
||||
use crate::settings::{ApiState, Intro, IntroIndex, UserSettings};
|
||||
use crate::{auth, settings::FileIntro};
|
||||
|
||||
#[derive(Serialize)]
|
||||
|
@ -38,6 +38,7 @@ pub(crate) struct Me<'a> {
|
|||
pub(crate) struct MeGuild<'a> {
|
||||
pub(crate) name: String,
|
||||
pub(crate) channels: Vec<MeChannel<'a>>,
|
||||
pub(crate) permissions: auth::Permissions,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
|
@ -99,6 +100,13 @@ struct DiscordUser {
|
|||
pub username: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct DiscordUserGuild {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub owner: bool,
|
||||
}
|
||||
|
||||
pub(crate) async fn auth(
|
||||
State(state): State<Arc<ApiState>>,
|
||||
Query(params): Query<HashMap<String, String>>,
|
||||
|
@ -114,7 +122,7 @@ pub(crate) async fn auth(
|
|||
data.insert("client_secret", state.secrets.client_secret.as_str());
|
||||
data.insert("grant_type", "authorization_code");
|
||||
data.insert("code", code);
|
||||
data.insert("redirect_uri", "http://localhost:5173/auth");
|
||||
data.insert("redirect_uri", "https://spacegirl.nl/memes/auth");
|
||||
|
||||
let client = reqwest::Client::new();
|
||||
|
||||
|
@ -219,34 +227,66 @@ pub(crate) async fn intros(
|
|||
Json(json!(IntroResponse::Intros(&guild.intros)))
|
||||
}
|
||||
|
||||
pub(crate) async fn me(State(state): State<Arc<ApiState>>, headers: HeaderMap) -> Json<Value> {
|
||||
let settings = state.settings.lock().await;
|
||||
let Some(token) = headers.get("token").and_then(|v| v.to_str().ok()) else { return Json(json!(MeResponse::NoUserFound)); };
|
||||
pub(crate) async fn me(
|
||||
State(state): State<Arc<ApiState>>,
|
||||
headers: HeaderMap,
|
||||
) -> Result<Json<Value>, Error> {
|
||||
let mut settings = state.settings.lock().await;
|
||||
let Some(token) = headers.get("token").and_then(|v| v.to_str().ok()) else { return Err(Error::NoUserFound); };
|
||||
|
||||
let user = match settings.auth_users.get(token) {
|
||||
Some(user) => user.name.clone(),
|
||||
None => return Json(json!(MeResponse::NoUserFound)),
|
||||
let (username, permissions, access_token) = match settings.auth_users.get(token) {
|
||||
Some(user) => (
|
||||
user.name.clone(),
|
||||
user.permissions,
|
||||
user.auth.access_token.clone(),
|
||||
),
|
||||
None => return Err(Error::NoUserFound),
|
||||
};
|
||||
|
||||
// TODO: get bot's guilds so we only save users who are able to use the bot
|
||||
let discord_guilds: Vec<DiscordUserGuild> = reqwest::Client::new()
|
||||
.get("https://discord.com/api/v10/users/@me/guilds")
|
||||
.bearer_auth(access_token)
|
||||
.send()
|
||||
.await?
|
||||
.json()
|
||||
.await
|
||||
.map_err(|err| {
|
||||
settings.auth_users.remove(token);
|
||||
|
||||
Error::Auth(err.to_string())
|
||||
})?;
|
||||
|
||||
let mut me = Me {
|
||||
username: user.clone(),
|
||||
username: username.clone(),
|
||||
guilds: Vec::new(),
|
||||
};
|
||||
|
||||
for g in &settings.guilds {
|
||||
for g in settings.guilds.iter_mut() {
|
||||
// TODO: don't do this n^2 lookup
|
||||
let Some(discord_guild) = discord_guilds.iter().find(|discord_guild| discord_guild.id == g.0.to_string()) else { continue; };
|
||||
|
||||
let mut guild = MeGuild {
|
||||
name: g.0.to_string(),
|
||||
channels: Vec::new(),
|
||||
// TODO: change `auth::User` to have guild specific permissions instead of global
|
||||
permissions,
|
||||
};
|
||||
|
||||
for channel in &g.1.channels {
|
||||
let user_settings = channel.1.users.iter().find(|u| *u.0 == user);
|
||||
for channel in g.1.channels.iter_mut() {
|
||||
let user_settings = channel
|
||||
.1
|
||||
.users
|
||||
.entry(username.clone())
|
||||
.or_insert(UserSettings { intros: Vec::new() });
|
||||
|
||||
let Some(user) = user_settings else { continue; };
|
||||
if discord_guild.owner {
|
||||
guild.permissions.0 |= auth::Permission::DownloadSounds as u8;
|
||||
}
|
||||
|
||||
guild.channels.push(MeChannel {
|
||||
name: channel.0.to_owned(),
|
||||
intros: &user.1.intros,
|
||||
intros: &user_settings.intros,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -254,22 +294,24 @@ pub(crate) async fn me(State(state): State<Arc<ApiState>>, headers: HeaderMap) -
|
|||
}
|
||||
|
||||
if me.guilds.is_empty() {
|
||||
Json(json!(MeResponse::NoUserFound))
|
||||
Ok(Json(json!(MeResponse::NoUserFound)))
|
||||
} else {
|
||||
Json(json!(MeResponse::Me(me)))
|
||||
Ok(Json(json!(MeResponse::Me(me))))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn add_guild_intro(
|
||||
State(state): State<Arc<ApiState>>,
|
||||
Path((guild, url)): Path<(u64, String)>,
|
||||
Path(guild): Path<u64>,
|
||||
Query(mut params): Query<HashMap<String, String>>,
|
||||
headers: HeaderMap,
|
||||
) -> Result<(), Error> {
|
||||
let mut settings = state.settings.lock().await;
|
||||
// TODO: make this an impl on HeaderMap
|
||||
let Some(token) = headers.get("token").and_then(|v| v.to_str().ok()) else { return Err(Error::NoUserFound); };
|
||||
let Some(url) = params.remove("url") else { return Err(Error::InvalidRequest); };
|
||||
let Some(friendly_name) = params.remove("name") else { return Err(Error::InvalidRequest); };
|
||||
|
||||
let user = match settings.auth_users.get(token) {
|
||||
Some(user) => user,
|
||||
None => return Err(Error::NoUserFound),
|
||||
|
@ -284,7 +326,7 @@ pub(crate) async fn add_guild_intro(
|
|||
let uuid = Uuid::new_v4().to_string();
|
||||
let child = tokio::process::Command::new("yt-dlp")
|
||||
.arg(&url)
|
||||
.args(["-o", &format!("./sounds/{uuid}")])
|
||||
.args(["-o", &format!("sounds/{uuid}")])
|
||||
.args(["-x", "--audio-format", "mp3"])
|
||||
.spawn()
|
||||
.map_err(Error::Ytdl)?
|
||||
|
|
Loading…
Reference in New Issue