add upload intro widget

master
Patrick Cleavelin 2023-08-01 15:25:42 -05:00
parent 04a0d499e1
commit 4a9f826369
3 changed files with 112 additions and 4 deletions

View File

@ -139,6 +139,7 @@ fn spawn_api(settings: Arc<Mutex<Settings>>) {
.route("/index.html", get(page::home))
.route("/login", get(page::login))
.route("/guild/:guild_id", get(page::guild_dashboard))
.route("/v2/auth", get(routes::v2_auth))
.route(
"/v2/intros/add/:guild_id/:channel",
post(routes::v2_add_intro_to_user),
@ -147,6 +148,10 @@ fn spawn_api(settings: Arc<Mutex<Settings>>) {
"/v2/intros/remove/:guild_id/:channel",
post(routes::v2_remove_intro_from_user),
)
.route(
"/v2/intros/:guild/upload",
post(routes::v2_upload_guild_intro),
)
.route("/health", get(routes::health))
.route("/me", get(routes::me))
.route("/intros/:guild", get(routes::intros))
@ -162,7 +167,6 @@ fn spawn_api(settings: Arc<Mutex<Settings>>) {
post(routes::remove_intro_to_user),
)
.route("/auth", get(routes::auth))
.route("/v2/auth", get(routes::v2_auth))
.layer(
CorsLayer::new()
// TODO: move this to env variable

View File

@ -111,6 +111,7 @@ pub(crate) async fn guild_dashboard(
return Err(Redirect::to(&format!("{}/", state.origin)));
};
let can_upload = guild_user.permissions.can(auth::Permission::UploadSounds);
let is_moderator = guild_user.permissions.can(auth::Permission::DeleteSounds);
Ok(Html(
@ -124,7 +125,7 @@ pub(crate) async fn guild_dashboard(
})
})
.builder(Tag::Empty, |b| {
if is_moderator {
let mut b = if is_moderator {
b.builder(Tag::Div, |b| {
b.attribute("class", "container")
.builder(Tag::Article, |b| {
@ -135,8 +136,20 @@ pub(crate) async fn guild_dashboard(
})
} else {
b
}
.builder(Tag::Div, |b| {
};
b = if can_upload {
b.builder(Tag::Div, |b| {
b.attribute("class", "container")
.builder(Tag::Article, |b| {
b.builder_text(Tag::Header, "Upload New Intro")
.push_builder(upload_form(&state.origin, guild_id))
})
})
} else {
b
};
b.builder(Tag::Div, |b| {
b.attribute("class", "container")
.builder(Tag::Article, |b| {
let mut b = b.builder_text(Tag::Header, "Guild Intros");
@ -198,6 +211,26 @@ pub(crate) async fn guild_dashboard(
))
}
fn upload_form(origin: &str, guild_id: u64) -> HtmxBuilder {
HtmxBuilder::new(Tag::Empty).form(|b| {
b.attribute("class", "container")
.hx_post(&format!("{}/v2/intros/{}/upload", origin, guild_id))
.attribute("hx-encoding", "multipart/form-data")
.builder(Tag::FieldSet, |b| {
b.attribute("class", "container")
.input(|b| {
b.attribute("name", "name")
.attribute("placeholder", "enter intro title")
})
.label(|b| {
b.text("Choose File")
.input(|b| b.attribute("type", "file").attribute("name", "file"))
})
})
.button(|b| b.attribute("type", "submit").text("Upload"))
})
}
fn moderator_dashboard(state: &ApiState) -> HtmxBuilder {
HtmxBuilder::new(Tag::Empty).link("Go back to old UI", &format!("{}/old", state.origin))
}

View File

@ -611,6 +611,77 @@ pub(crate) async fn upload_guild_intro(
Ok(())
}
pub(crate) async fn v2_upload_guild_intro(
State(state): State<ApiState>,
Path(guild): Path<u64>,
user: User,
mut form_data: Multipart,
) -> Result<HeaderMap, Error> {
let mut settings = state.settings.lock().await;
let mut friendly_name = None;
let mut file = None;
while let Ok(Some(field)) = form_data.next_field().await {
let Some(field_name) = field.name() else {
continue;
};
if field_name.eq_ignore_ascii_case("name") {
friendly_name = Some(field.text().await.map_err(|_| Error::InvalidRequest)?);
continue;
}
if field_name.eq_ignore_ascii_case("file") {
file = Some(field.bytes().await.map_err(|_| Error::InvalidRequest)?);
continue;
}
}
let Some(friendly_name) = friendly_name else {
return Err(Error::InvalidRequest);
};
let Some(file) = file else {
return Err(Error::InvalidRequest);
};
{
let Some(guild) = settings.guilds.get(&guild) else {
return Err(Error::NoGuildFound);
};
let Some(guild_user) = guild.users.get(&user.name) else {
return Err(Error::NoUserFound);
};
if !guild_user.permissions.can(auth::Permission::UploadSounds) {
return Err(Error::InvalidPermission);
}
}
let Some(guild) = settings.guilds.get_mut(&guild) else {
return Err(Error::NoGuildFound);
};
let uuid = Uuid::new_v4().to_string();
let temp_path = format!("./sounds/temp/{uuid}");
let dest_path = format!("./sounds/{uuid}.mp3");
// Write original file so its ready for codec conversion
std::fs::write(&temp_path, file)?;
media::normalize(&temp_path, &dest_path).await?;
std::fs::remove_file(&temp_path)?;
guild.intros.insert(
uuid.clone(),
Intro::File(FileIntro {
filename: format!("{uuid}.mp3"),
friendly_name,
}),
);
let mut headers = HeaderMap::new();
headers.insert("HX-Refresh", HeaderValue::from_static("true"));
Ok(headers)
}
pub(crate) async fn add_guild_intro(
State(state): State<ApiState>,
Path(guild): Path<u64>,