Compare commits

..

No commits in common. "e4cc833ccc9b14d7d90b462a3399ebe1f3b33ac5" and "e8b6db7bc1f92d55fd6ed326f3f6f644dddcaeb0" have entirely different histories.

6 changed files with 4 additions and 149 deletions

View file

@ -37,7 +37,7 @@ jobs:
mkdir -p ~/discord
cp target/release/chattz ~/discord/discord
chmod +x ~/discord/discord
mkdir -p ~/discord/static/uploads
find ~/discord/static -mindepth 1 -maxdepth 1 ! -name 'uploads' -exec rm -rf {} +
mkdir -p ~/discord/static
rm -rf ~/discord/static/*
cp -r static/* ~/discord/static/
systemctl --user start discord

View file

@ -139,23 +139,6 @@ pub async fn create_guild(
Ok(map_guild(guild))
}
pub async fn get_guild_by_id(db: &DatabaseConnection, guild_id: Uuid) -> Result<Option<Guild>> {
let row = guilds::Entity::find_by_id(guild_id).one(db).await?;
Ok(row.map(map_guild))
}
pub async fn is_guild_owner(
db: &DatabaseConnection,
guild_id: Uuid,
user_id: Uuid,
) -> Result<bool> {
let guild = guilds::Entity::find_by_id(guild_id).one(db).await?;
match guild {
Some(g) => Ok(g.owner_user_id == user_id),
None => Ok(false),
}
}
pub async fn create_invite(
db: &DatabaseConnection,
guild_id: Uuid,
@ -629,16 +612,6 @@ pub async fn create_sound(
Ok(map_sound(model))
}
pub async fn get_sound_by_id(db: &DatabaseConnection, id: Uuid) -> Result<Option<SoundboardSound>> {
let row = soundboard_sounds::Entity::find_by_id(id).one(db).await?;
Ok(row.map(map_sound))
}
pub async fn delete_sound(db: &DatabaseConnection, id: Uuid) -> Result<()> {
soundboard_sounds::Entity::delete_by_id(id).exec(db).await?;
Ok(())
}
fn map_sound(model: soundboard_sounds::Model) -> SoundboardSound {
SoundboardSound {
id: model.id,

View file

@ -15,7 +15,6 @@ use crate::{
models::{Guild, SoundboardSound},
voice,
};
use tracing::info;
pub fn routes() -> Router<AppState> {
Router::new()
@ -44,10 +43,6 @@ pub fn routes() -> Router<AppState> {
"/guilds/{guild_id}/sounds",
get(list_sounds).post(upload_sound),
)
.route(
"/guilds/{guild_id}/sounds/{sound_id}",
post(delete_sound_post).delete(delete_sound),
)
.route("/channels", post(create_channel))
.route(
"/channels/{channel_id}/messages",
@ -764,57 +759,6 @@ async fn upload_sound(
Ok(Json(sound))
}
async fn delete_sound_post(
state: State<AppState>,
user: AuthUser,
path: Path<(Uuid, Uuid)>,
) -> Result<impl IntoResponse, ApiError> {
delete_sound(state, user, path).await
}
async fn delete_sound(
State(state): State<AppState>,
user: AuthUser,
Path((guild_id, sound_id)): Path<(Uuid, Uuid)>,
) -> Result<impl IntoResponse, ApiError> {
ensure_guild_member(&state, guild_id, user.id).await?;
let sound = db::get_sound_by_id(&state.db, sound_id)
.await
.map_err(|e| ApiError::internal(&e.to_string()))?
.ok_or_else(|| ApiError {
status: StatusCode::NOT_FOUND,
message: "sound not found".to_string(),
})?;
if sound.guild_id != guild_id {
return Err(ApiError::bad_request("sound does not belong to this guild"));
}
let is_owner = db::is_guild_owner(&state.db, guild_id, user.id)
.await
.map_err(|e| ApiError::internal(&e.to_string()))?;
if sound.created_by_user_id != user.id && !is_owner {
return Err(ApiError {
status: StatusCode::FORBIDDEN,
message: "you do not have permission to delete this sound".to_string(),
});
}
// Delete file from disk
let relative_path = sound.file_path.trim_start_matches('/');
if let Err(e) = tokio::fs::remove_file(relative_path).await {
info!("failed to delete sound file {}: {}", relative_path, e);
}
db::delete_sound(&state.db, sound_id)
.await
.map_err(|e| ApiError::internal(&format!("failed to delete sound record: {e}")))?;
Ok(StatusCode::NO_CONTENT)
}
async fn ensure_guild_member(
state: &AppState,
guild_id: Uuid,

View file

@ -259,10 +259,7 @@ impl VoiceHub {
return;
};
for (peer_id, peer) in room.iter() {
if *peer_id == user_id {
continue;
}
for peer in room.values() {
let _ = peer.tx.send(ServerEvent::PlaySound {
user_id,
sound_url: sound_url.clone(),

View file

@ -1049,46 +1049,13 @@ function renderSounds(sounds) {
sounds.forEach(sound => {
const item = document.createElement('div');
item.className = 'sound-item';
// Only show delete button if user is creator or owner
const canDelete = state.user && (state.user.id === sound.created_by_user_id || (state.guilds.find(g => g.id === state.selectedGuildId)?.owner_user_id === state.user.id));
item.innerHTML = `
<div class="sound-icon">${sound.icon}</div>
<div class="sound-name">${sound.name}</div>
${canDelete ? `<button class="sound-delete" title="Delete sound"><i data-lucide="x"></i></button>` : ''}
`;
item.onclick = (e) => {
if (e.target.closest('.sound-delete')) {
e.stopPropagation();
if (confirm(`Are you sure you want to delete "${sound.name}"?`)) {
deleteSound(sound.id);
}
return;
}
playRemoteSound(sound.file_path);
};
item.onclick = () => playRemoteSound(sound.file_path);
el.soundboardGrid.appendChild(item);
});
lucide.createIcons();
}
async function deleteSound(soundId) {
if (!state.selectedGuildId) return;
try {
const response = await fetch(`/guilds/${state.selectedGuildId}/sounds/${soundId}`, {
method: 'DELETE'
});
if (!response.ok) {
const err = await response.json();
throw new Error(err.error || 'Delete failed');
}
await loadSounds();
} catch (err) {
alert(err.message);
}
}
function playRemoteSound(url) {

View file

@ -729,32 +729,6 @@ select {
transform: scale(0.95);
}
.sound-delete {
position: absolute;
top: 4px;
right: 4px;
width: 18px;
height: 18px;
border-radius: 50%;
background: rgba(0, 0, 0, 0.6);
color: var(--text-muted);
display: grid;
place-items: center;
opacity: 0;
transition: all var(--transition);
z-index: 2;
}
.sound-delete:hover {
background: var(--danger);
color: #fff;
transform: scale(1.1);
}
.sound-item:hover .sound-delete {
opacity: 1;
}
.sound-icon {
font-size: 24px;
}