Compare commits
2 commits
bcb82d0f46
...
1cda34d16b
Author | SHA1 | Date | |
---|---|---|---|
1cda34d16b | |||
d8541b2eea |
12 changed files with 85 additions and 23 deletions
|
@ -0,0 +1,2 @@
|
||||||
|
-- This file should undo anything in `up.sql`
|
||||||
|
ALTER TABLE channels DROP COLUMN is_above;
|
2
migrations/2025-05-26-181536_add_channel_ordering/up.sql
Normal file
2
migrations/2025-05-26-181536_add_channel_ordering/up.sql
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
-- Your SQL goes here
|
||||||
|
ALTER TABLE channels ADD COLUMN is_above UUID UNIQUE REFERENCES channels(uuid) DEFAULT NULL;
|
|
@ -59,14 +59,9 @@ pub async fn res(req: HttpRequest, data: web::Data<Data>) -> Result<HttpResponse
|
||||||
let current_time = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs() as i64;
|
let current_time = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs() as i64;
|
||||||
|
|
||||||
if lifetime > 1987200 {
|
if lifetime > 1987200 {
|
||||||
let new_refresh_token = generate_refresh_token();
|
let new_refresh_token = generate_refresh_token()?;
|
||||||
|
|
||||||
if new_refresh_token.is_err() {
|
let new_refresh_token = new_refresh_token;
|
||||||
error!("{}", new_refresh_token.unwrap_err());
|
|
||||||
return Ok(HttpResponse::InternalServerError().finish());
|
|
||||||
}
|
|
||||||
|
|
||||||
let new_refresh_token = new_refresh_token.unwrap();
|
|
||||||
|
|
||||||
match update(refresh_tokens::table)
|
match update(refresh_tokens::table)
|
||||||
.filter(rdsl::token.eq(&refresh_token))
|
.filter(rdsl::token.eq(&refresh_token))
|
||||||
|
|
|
@ -63,7 +63,7 @@ pub async fn get(
|
||||||
|
|
||||||
let amount = request_query.amount.unwrap_or(10);
|
let amount = request_query.amount.unwrap_or(10);
|
||||||
|
|
||||||
check_access_token(auth_header, &mut data.pool.get().await.unwrap()).await?;
|
check_access_token(auth_header, &mut data.pool.get().await?).await?;
|
||||||
|
|
||||||
let guilds = Guild::fetch_amount(&data.pool, start, amount).await?;
|
let guilds = Guild::fetch_amount(&data.pool, start, amount).await?;
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ use crate::{
|
||||||
api::v1::auth::check_access_token,
|
api::v1::auth::check_access_token,
|
||||||
error::Error,
|
error::Error,
|
||||||
structs::{Channel, Member},
|
structs::{Channel, Member},
|
||||||
utils::get_auth_header,
|
utils::{get_auth_header, order_channels},
|
||||||
};
|
};
|
||||||
use ::uuid::Uuid;
|
use ::uuid::Uuid;
|
||||||
use actix_web::{HttpRequest, HttpResponse, get, post, web};
|
use actix_web::{HttpRequest, HttpResponse, get, post, web};
|
||||||
|
@ -43,10 +43,16 @@ pub async fn get(
|
||||||
|
|
||||||
let channels = Channel::fetch_all(&data.pool, guild_uuid).await?;
|
let channels = Channel::fetch_all(&data.pool, guild_uuid).await?;
|
||||||
|
|
||||||
data.set_cache_key(format!("{}_channels", guild_uuid), channels.clone(), 1800)
|
let channels_ordered = order_channels(channels).await?;
|
||||||
|
|
||||||
|
data.set_cache_key(
|
||||||
|
format!("{}_channels", guild_uuid),
|
||||||
|
channels_ordered.clone(),
|
||||||
|
1800,
|
||||||
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(HttpResponse::Ok().json(channels))
|
Ok(HttpResponse::Ok().json(channels_ordered))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("{uuid}/channels")]
|
#[post("{uuid}/channels")]
|
||||||
|
@ -76,7 +82,7 @@ pub async fn create(
|
||||||
channel_info.name.clone(),
|
channel_info.name.clone(),
|
||||||
channel_info.description.clone(),
|
channel_info.description.clone(),
|
||||||
)
|
)
|
||||||
.await;
|
.await?;
|
||||||
|
|
||||||
Ok(HttpResponse::Ok().json(channel.unwrap()))
|
Ok(HttpResponse::Ok().json(channel))
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,7 @@ pub async fn delete(
|
||||||
let channel: Channel;
|
let channel: Channel;
|
||||||
|
|
||||||
if let Ok(cache_hit) = data.get_cache_key(format!("{}", channel_uuid)).await {
|
if let Ok(cache_hit) = data.get_cache_key(format!("{}", channel_uuid)).await {
|
||||||
channel = serde_json::from_str(&cache_hit).unwrap();
|
channel = serde_json::from_str(&cache_hit)?;
|
||||||
|
|
||||||
data.del_cache_key(format!("{}", channel_uuid)).await?;
|
data.del_cache_key(format!("{}", channel_uuid)).await?;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -42,7 +42,7 @@ pub async fn echo(
|
||||||
|
|
||||||
// Return channel cache or result from psql as `channel` variable
|
// Return channel cache or result from psql as `channel` variable
|
||||||
if let Ok(cache_hit) = data.get_cache_key(format!("{}", channel_uuid)).await {
|
if let Ok(cache_hit) = data.get_cache_key(format!("{}", channel_uuid)).await {
|
||||||
channel = serde_json::from_str(&cache_hit).unwrap()
|
channel = serde_json::from_str(&cache_hit)?
|
||||||
} else {
|
} else {
|
||||||
channel = Channel::fetch_one(&mut conn, channel_uuid).await?;
|
channel = Channel::fetch_one(&mut conn, channel_uuid).await?;
|
||||||
|
|
||||||
|
|
|
@ -62,7 +62,7 @@ pub async fn create(
|
||||||
|
|
||||||
let guild_uuid = path.into_inner().0;
|
let guild_uuid = path.into_inner().0;
|
||||||
|
|
||||||
let mut conn = data.pool.get().await.unwrap();
|
let mut conn = data.pool.get().await?;
|
||||||
|
|
||||||
let uuid = check_access_token(auth_header, &mut conn).await?;
|
let uuid = check_access_token(auth_header, &mut conn).await?;
|
||||||
|
|
||||||
|
|
|
@ -60,6 +60,8 @@ pub enum Error {
|
||||||
BadRequest(String),
|
BadRequest(String),
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
Unauthorized(String),
|
Unauthorized(String),
|
||||||
|
#[error("{0}")]
|
||||||
|
InternalServerError(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ResponseError for Error {
|
impl ResponseError for Error {
|
||||||
|
|
|
@ -27,6 +27,7 @@ diesel::table! {
|
||||||
name -> Varchar,
|
name -> Varchar,
|
||||||
#[max_length = 500]
|
#[max_length = 500]
|
||||||
description -> Nullable<Varchar>,
|
description -> Nullable<Varchar>,
|
||||||
|
is_above -> Nullable<Uuid>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,12 +5,18 @@ use diesel::{
|
||||||
update,
|
update,
|
||||||
};
|
};
|
||||||
use diesel_async::{RunQueryDsl, pooled_connection::AsyncDieselConnectionManager};
|
use diesel_async::{RunQueryDsl, pooled_connection::AsyncDieselConnectionManager};
|
||||||
|
use log::debug;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tokio::task;
|
use tokio::task;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{Conn, Data, error::Error, schema::*, utils::image_check};
|
use crate::{
|
||||||
|
Conn, Data,
|
||||||
|
error::Error,
|
||||||
|
schema::*,
|
||||||
|
utils::{image_check, order_channels},
|
||||||
|
};
|
||||||
|
|
||||||
fn load_or_empty<T>(
|
fn load_or_empty<T>(
|
||||||
query_result: Result<Vec<T>, diesel::result::Error>,
|
query_result: Result<Vec<T>, diesel::result::Error>,
|
||||||
|
@ -22,7 +28,7 @@ fn load_or_empty<T>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Queryable, Selectable, Insertable, Clone)]
|
#[derive(Queryable, Selectable, Insertable, Clone, Debug)]
|
||||||
#[diesel(table_name = channels)]
|
#[diesel(table_name = channels)]
|
||||||
#[diesel(check_for_backend(diesel::pg::Pg))]
|
#[diesel(check_for_backend(diesel::pg::Pg))]
|
||||||
struct ChannelBuilder {
|
struct ChannelBuilder {
|
||||||
|
@ -30,6 +36,7 @@ struct ChannelBuilder {
|
||||||
guild_uuid: Uuid,
|
guild_uuid: Uuid,
|
||||||
name: String,
|
name: String,
|
||||||
description: Option<String>,
|
description: Option<String>,
|
||||||
|
is_above: Option<Uuid>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ChannelBuilder {
|
impl ChannelBuilder {
|
||||||
|
@ -48,21 +55,23 @@ impl ChannelBuilder {
|
||||||
guild_uuid: self.guild_uuid,
|
guild_uuid: self.guild_uuid,
|
||||||
name: self.name,
|
name: self.name,
|
||||||
description: self.description,
|
description: self.description,
|
||||||
|
is_above: self.is_above,
|
||||||
permissions: channel_permission,
|
permissions: channel_permission,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
pub struct Channel {
|
pub struct Channel {
|
||||||
pub uuid: Uuid,
|
pub uuid: Uuid,
|
||||||
pub guild_uuid: Uuid,
|
pub guild_uuid: Uuid,
|
||||||
name: String,
|
name: String,
|
||||||
description: Option<String>,
|
description: Option<String>,
|
||||||
|
pub is_above: Option<Uuid>,
|
||||||
pub permissions: Vec<ChannelPermission>,
|
pub permissions: Vec<ChannelPermission>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Queryable, Selectable)]
|
#[derive(Serialize, Deserialize, Clone, Queryable, Selectable, Debug)]
|
||||||
#[diesel(table_name = channel_permissions)]
|
#[diesel(table_name = channel_permissions)]
|
||||||
#[diesel(check_for_backend(diesel::pg::Pg))]
|
#[diesel(check_for_backend(diesel::pg::Pg))]
|
||||||
pub struct ChannelPermission {
|
pub struct ChannelPermission {
|
||||||
|
@ -118,24 +127,47 @@ impl Channel {
|
||||||
|
|
||||||
let channel_uuid = Uuid::now_v7();
|
let channel_uuid = Uuid::now_v7();
|
||||||
|
|
||||||
|
let channels = Self::fetch_all(&data.pool, guild_uuid).await?;
|
||||||
|
|
||||||
|
debug!("{:?}", channels);
|
||||||
|
|
||||||
|
let channels_ordered = order_channels(channels).await?;
|
||||||
|
|
||||||
|
debug!("{:?}", channels_ordered);
|
||||||
|
|
||||||
|
let last_channel = channels_ordered.last();
|
||||||
|
|
||||||
let new_channel = ChannelBuilder {
|
let new_channel = ChannelBuilder {
|
||||||
uuid: channel_uuid,
|
uuid: channel_uuid,
|
||||||
guild_uuid,
|
guild_uuid,
|
||||||
name: name.clone(),
|
name: name.clone(),
|
||||||
description: description.clone(),
|
description: description.clone(),
|
||||||
|
is_above: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
debug!("New Channel: {:?}", new_channel);
|
||||||
|
|
||||||
insert_into(channels::table)
|
insert_into(channels::table)
|
||||||
.values(new_channel)
|
.values(new_channel.clone())
|
||||||
.execute(&mut conn)
|
.execute(&mut conn)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
if let Some(old_last_channel) = last_channel {
|
||||||
|
use channels::dsl;
|
||||||
|
update(channels::table)
|
||||||
|
.filter(dsl::uuid.eq(old_last_channel.uuid))
|
||||||
|
.set(dsl::is_above.eq(new_channel.uuid))
|
||||||
|
.execute(&mut conn)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
// returns different object because there's no reason to build the channelbuilder (wastes 1 database request)
|
// returns different object because there's no reason to build the channelbuilder (wastes 1 database request)
|
||||||
let channel = Self {
|
let channel = Self {
|
||||||
uuid: channel_uuid,
|
uuid: channel_uuid,
|
||||||
guild_uuid,
|
guild_uuid,
|
||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
|
is_above: None,
|
||||||
permissions: vec![],
|
permissions: vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
24
src/utils.rs
24
src/utils.rs
|
@ -9,7 +9,7 @@ use hex::encode;
|
||||||
use redis::RedisError;
|
use redis::RedisError;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use crate::{Data, error::Error};
|
use crate::{Data, error::Error, structs::Channel};
|
||||||
|
|
||||||
pub fn get_auth_header(headers: &HeaderMap) -> Result<&str, Error> {
|
pub fn get_auth_header(headers: &HeaderMap) -> Result<&str, Error> {
|
||||||
let auth_token = headers.get(actix_web::http::header::AUTHORIZATION);
|
let auth_token = headers.get(actix_web::http::header::AUTHORIZATION);
|
||||||
|
@ -119,6 +119,28 @@ pub fn image_check(icon: BytesMut) -> Result<String, Error> {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn order_channels(mut channels: Vec<Channel>) -> Result<Vec<Channel>, Error> {
|
||||||
|
let mut ordered = Vec::new();
|
||||||
|
|
||||||
|
// Find head
|
||||||
|
let head_pos = channels
|
||||||
|
.iter()
|
||||||
|
.position(|channel| !channels.iter().any(|i| i.is_above == Some(channel.uuid)));
|
||||||
|
|
||||||
|
if let Some(pos) = head_pos {
|
||||||
|
ordered.push(channels.swap_remove(pos));
|
||||||
|
|
||||||
|
while let Some(next_pos) = channels
|
||||||
|
.iter()
|
||||||
|
.position(|channel| Some(channel.uuid) == ordered.last().unwrap().is_above)
|
||||||
|
{
|
||||||
|
ordered.push(channels.swap_remove(next_pos));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(ordered)
|
||||||
|
}
|
||||||
|
|
||||||
impl Data {
|
impl Data {
|
||||||
pub async fn set_cache_key(
|
pub async fn set_cache_key(
|
||||||
&self,
|
&self,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue