feat: allow usage of local folder for file storage
This commit is contained in:
parent
6640d03b70
commit
e1a136ff51
11 changed files with 158 additions and 81 deletions
|
@ -37,6 +37,7 @@ diesel-async = { version = "0.5", features = ["deadpool", "postgres", "async-con
|
||||||
diesel_migrations = { version = "2.2.0", features = ["postgres"] }
|
diesel_migrations = { version = "2.2.0", features = ["postgres"] }
|
||||||
thiserror = "2.0.12"
|
thiserror = "2.0.12"
|
||||||
actix-multipart = "0.7.2"
|
actix-multipart = "0.7.2"
|
||||||
|
actix-files = "0.6.6"
|
||||||
|
|
||||||
[dependencies.tokio]
|
[dependencies.tokio]
|
||||||
version = "1.44"
|
version = "1.44"
|
||||||
|
|
11
Dockerfile
11
Dockerfile
|
@ -18,16 +18,17 @@ RUN useradd --create-home --home-dir /gorb gorb
|
||||||
|
|
||||||
USER gorb
|
USER gorb
|
||||||
|
|
||||||
ENV DATABASE_USERNAME=gorb \
|
ENV URL=http://localhost:8080 \
|
||||||
|
DATABASE_USERNAME=gorb \
|
||||||
DATABASE_PASSWORD=gorb \
|
DATABASE_PASSWORD=gorb \
|
||||||
DATABASE=gorb \
|
DATABASE=gorb \
|
||||||
DATABASE_HOST=database \
|
DATABASE_HOST=database \
|
||||||
DATABASE_PORT=5432 \
|
DATABASE_PORT=5432 \
|
||||||
CACHE_DB_HOST=valkey \
|
CACHE_DB_HOST=valkey \
|
||||||
CACHE_DB_PORT=6379 \
|
CACHE_DB_PORT=6379 \
|
||||||
BUNNY_API_KEY=your_storage_zone_password_here \
|
BUNNY_API_KEY= \
|
||||||
BUNNY_ENDPOINT=Frankfurt \
|
BUNNY_ENDPOINT= \
|
||||||
BUNNY_ZONE=gorb \
|
BUNNY_ZONE= \
|
||||||
BUNNY_CDN_URL=https://cdn.gorb.app
|
BUNNY_CDN_URL=
|
||||||
|
|
||||||
ENTRYPOINT ["/usr/bin/entrypoint.sh"]
|
ENTRYPOINT ["/usr/bin/entrypoint.sh"]
|
||||||
|
|
|
@ -18,15 +18,18 @@ services:
|
||||||
- gorb-backend:/gorb
|
- gorb-backend:/gorb
|
||||||
environment:
|
environment:
|
||||||
#- RUST_LOG=debug
|
#- RUST_LOG=debug
|
||||||
|
# This should be changed to the public URL of the server!
|
||||||
|
- URL=http://localhost:8080
|
||||||
- DATABASE_USERNAME=gorb
|
- DATABASE_USERNAME=gorb
|
||||||
- DATABASE_PASSWORD=gorb
|
- DATABASE_PASSWORD=gorb
|
||||||
- DATABASE=gorb
|
- DATABASE=gorb
|
||||||
- DATABASE_HOST=database
|
- DATABASE_HOST=database
|
||||||
- DATABASE_PORT=5432
|
- DATABASE_PORT=5432
|
||||||
- BUNNY_API_KEY=your_storage_zone_password_here
|
# These can be set to use a CDN, if they are not set then files will be stored locally
|
||||||
- BUNNY_ENDPOINT=Frankfurt
|
#- BUNNY_API_KEY=your_storage_zone_password_here
|
||||||
- BUNNY_ZONE=gorb
|
#- BUNNY_ENDPOINT=Frankfurt
|
||||||
- BUNNY_CDN_URL=https://cdn.gorb.app
|
#- BUNNY_ZONE=gorb
|
||||||
|
#- BUNNY_CDN_URL=https://cdn.gorb.app
|
||||||
database:
|
database:
|
||||||
image: postgres:16
|
image: postgres:16
|
||||||
restart: always
|
restart: always
|
||||||
|
|
11
compose.yml
11
compose.yml
|
@ -16,15 +16,18 @@ services:
|
||||||
- gorb-backend:/gorb
|
- gorb-backend:/gorb
|
||||||
environment:
|
environment:
|
||||||
#- RUST_LOG=debug
|
#- RUST_LOG=debug
|
||||||
|
# This should be changed to the public URL of the server!
|
||||||
|
- URL=http://localhost:8080
|
||||||
- DATABASE_USERNAME=gorb
|
- DATABASE_USERNAME=gorb
|
||||||
- DATABASE_PASSWORD=gorb
|
- DATABASE_PASSWORD=gorb
|
||||||
- DATABASE=gorb
|
- DATABASE=gorb
|
||||||
- DATABASE_HOST=database
|
- DATABASE_HOST=database
|
||||||
- DATABASE_PORT=5432
|
- DATABASE_PORT=5432
|
||||||
- BUNNY_API_KEY=your_storage_zone_password_here
|
# These can be set to use a CDN, if they are not set then files will be stored locally
|
||||||
- BUNNY_ENDPOINT=Frankfurt
|
#- BUNNY_API_KEY=your_storage_zone_password_here
|
||||||
- BUNNY_ZONE=gorb
|
#- BUNNY_ENDPOINT=Frankfurt
|
||||||
- BUNNY_CDN_URL=https://cdn.gorb.app
|
#- BUNNY_ZONE=gorb
|
||||||
|
#- BUNNY_CDN_URL=https://cdn.gorb.app
|
||||||
database:
|
database:
|
||||||
image: postgres:16
|
image: postgres:16
|
||||||
restart: always
|
restart: always
|
||||||
|
|
|
@ -8,6 +8,10 @@ if [ ! -d "/gorb/logs" ]; then
|
||||||
mkdir /gorb/logs
|
mkdir /gorb/logs
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ ! -d "/gorb/data" ]; then
|
||||||
|
mkdir /gorb/data
|
||||||
|
fi
|
||||||
|
|
||||||
if [ ! -f "/gorb/config/config.toml" ]; then
|
if [ ! -f "/gorb/config/config.toml" ]; then
|
||||||
cat > /gorb/config/config.toml <<EOF
|
cat > /gorb/config/config.toml <<EOF
|
||||||
[database]
|
[database]
|
||||||
|
@ -52,4 +56,4 @@ rotate_log "/gorb/logs/backend.log"
|
||||||
# Give the DB time to start up before connecting
|
# Give the DB time to start up before connecting
|
||||||
sleep 5
|
sleep 5
|
||||||
|
|
||||||
/usr/bin/gorb-backend --config /gorb/config/config.toml 2>&1 | tee /gorb/logs/backend.log
|
/usr/bin/gorb-backend --config /gorb/config/config.toml --data-dir /gorb/data 2>&1 | tee /gorb/logs/backend.log
|
||||||
|
|
|
@ -38,9 +38,8 @@ pub async fn upload(
|
||||||
|
|
||||||
guild
|
guild
|
||||||
.set_icon(
|
.set_icon(
|
||||||
&data.bunny_cdn,
|
&data.storage,
|
||||||
&mut conn,
|
&mut conn,
|
||||||
data.config.bunny.cdn_url.clone(),
|
|
||||||
bytes,
|
bytes,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
|
@ -58,9 +58,8 @@ pub async fn update(
|
||||||
let byte_slice: &[u8] = &bytes;
|
let byte_slice: &[u8] = &bytes;
|
||||||
|
|
||||||
me.set_avatar(
|
me.set_avatar(
|
||||||
&data.bunny_cdn,
|
&data.storage,
|
||||||
&mut conn,
|
&mut conn,
|
||||||
data.config.bunny.cdn_url.clone(),
|
|
||||||
byte_slice.into(),
|
byte_slice.into(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
|
@ -9,8 +9,8 @@ use url::Url;
|
||||||
pub struct ConfigBuilder {
|
pub struct ConfigBuilder {
|
||||||
database: Database,
|
database: Database,
|
||||||
cache_database: CacheDatabase,
|
cache_database: CacheDatabase,
|
||||||
web: Option<WebBuilder>,
|
web: WebBuilder,
|
||||||
bunny: BunnyBuilder,
|
bunny: Option<BunnyBuilder>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
@ -33,8 +33,9 @@ pub struct CacheDatabase {
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
struct WebBuilder {
|
struct WebBuilder {
|
||||||
url: Option<String>,
|
ip: Option<String>,
|
||||||
port: Option<u16>,
|
port: Option<u16>,
|
||||||
|
url: Url,
|
||||||
_ssl: Option<bool>,
|
_ssl: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,19 +58,16 @@ impl ConfigBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(self) -> Config {
|
pub fn build(self) -> Config {
|
||||||
let web = if let Some(web) = self.web {
|
let web = Web {
|
||||||
Web {
|
ip: self.web.ip.unwrap_or(String::from("0.0.0.0")),
|
||||||
url: web.url.unwrap_or(String::from("0.0.0.0")),
|
port: self.web.port.unwrap_or(8080),
|
||||||
port: web.port.unwrap_or(8080),
|
url: self.web.url
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Web {
|
|
||||||
url: String::from("0.0.0.0"),
|
|
||||||
port: 8080,
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let endpoint = match &*self.bunny.endpoint {
|
let bunny;
|
||||||
|
|
||||||
|
if let Some(bunny_builder) = self.bunny {
|
||||||
|
let endpoint = match &*bunny_builder.endpoint {
|
||||||
"Frankfurt" => Endpoint::Frankfurt,
|
"Frankfurt" => Endpoint::Frankfurt,
|
||||||
"London" => Endpoint::London,
|
"London" => Endpoint::London,
|
||||||
"New York" => Endpoint::NewYork,
|
"New York" => Endpoint::NewYork,
|
||||||
|
@ -82,12 +80,15 @@ impl ConfigBuilder {
|
||||||
url => Endpoint::Custom(url.to_string()),
|
url => Endpoint::Custom(url.to_string()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let bunny = Bunny {
|
bunny = Some(Bunny {
|
||||||
api_key: self.bunny.api_key,
|
api_key: bunny_builder.api_key,
|
||||||
endpoint,
|
endpoint,
|
||||||
storage_zone: self.bunny.storage_zone,
|
storage_zone: bunny_builder.storage_zone,
|
||||||
cdn_url: self.bunny.cdn_url,
|
cdn_url: bunny_builder.cdn_url,
|
||||||
};
|
});
|
||||||
|
} else {
|
||||||
|
bunny = None;
|
||||||
|
}
|
||||||
|
|
||||||
Config {
|
Config {
|
||||||
database: self.database,
|
database: self.database,
|
||||||
|
@ -103,13 +104,14 @@ pub struct Config {
|
||||||
pub database: Database,
|
pub database: Database,
|
||||||
pub cache_database: CacheDatabase,
|
pub cache_database: CacheDatabase,
|
||||||
pub web: Web,
|
pub web: Web,
|
||||||
pub bunny: Bunny,
|
pub bunny: Option<Bunny>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Web {
|
pub struct Web {
|
||||||
pub url: String,
|
pub ip: String,
|
||||||
pub port: u16,
|
pub port: u16,
|
||||||
|
pub url: Url,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
|
@ -55,6 +55,8 @@ pub enum Error {
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
PasswordHashError(String),
|
PasswordHashError(String),
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
|
PathError(String),
|
||||||
|
#[error("{0}")]
|
||||||
BadRequest(String),
|
BadRequest(String),
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
Unauthorized(String),
|
Unauthorized(String),
|
||||||
|
|
24
src/main.rs
24
src/main.rs
|
@ -1,4 +1,5 @@
|
||||||
use actix_cors::Cors;
|
use actix_cors::Cors;
|
||||||
|
use actix_files::Files;
|
||||||
use actix_web::{App, HttpServer, web};
|
use actix_web::{App, HttpServer, web};
|
||||||
use argon2::Argon2;
|
use argon2::Argon2;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
@ -6,6 +7,7 @@ use diesel_async::pooled_connection::AsyncDieselConnectionManager;
|
||||||
use diesel_async::pooled_connection::deadpool::Pool;
|
use diesel_async::pooled_connection::deadpool::Pool;
|
||||||
use error::Error;
|
use error::Error;
|
||||||
use simple_logger::SimpleLogger;
|
use simple_logger::SimpleLogger;
|
||||||
|
use structs::Storage;
|
||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
mod config;
|
mod config;
|
||||||
use config::{Config, ConfigBuilder};
|
use config::{Config, ConfigBuilder};
|
||||||
|
@ -22,11 +24,13 @@ pub mod schema;
|
||||||
pub mod structs;
|
pub mod structs;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug, Clone)]
|
||||||
#[command(version, about, long_about = None)]
|
#[command(version, about, long_about = None)]
|
||||||
struct Args {
|
struct Args {
|
||||||
#[arg(short, long, default_value_t = String::from("/etc/gorb/config.toml"))]
|
#[arg(short, long, default_value_t = String::from("/etc/gorb/config.toml"))]
|
||||||
config: String,
|
config: String,
|
||||||
|
#[arg(short, long, default_value_t = String::from("/var/lib/gorb/"))]
|
||||||
|
data_dir: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -39,7 +43,7 @@ pub struct Data {
|
||||||
pub config: Config,
|
pub config: Config,
|
||||||
pub argon2: Argon2<'static>,
|
pub argon2: Argon2<'static>,
|
||||||
pub start_time: SystemTime,
|
pub start_time: SystemTime,
|
||||||
pub bunny_cdn: bunny_api_tokio::Client,
|
pub storage: Storage,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
|
@ -63,14 +67,7 @@ async fn main() -> Result<(), Error> {
|
||||||
|
|
||||||
let cache_pool = redis::Client::open(config.cache_database.url())?;
|
let cache_pool = redis::Client::open(config.cache_database.url())?;
|
||||||
|
|
||||||
let mut bunny_cdn = bunny_api_tokio::Client::new("").await?;
|
let storage = Storage::new(config.clone(), args.data_dir.clone()).await?;
|
||||||
|
|
||||||
let bunny = config.bunny.clone();
|
|
||||||
|
|
||||||
bunny_cdn
|
|
||||||
.storage
|
|
||||||
.init(bunny.api_key, bunny.endpoint, bunny.storage_zone)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let database_url = config.database.url();
|
let database_url = config.database.url();
|
||||||
|
|
||||||
|
@ -111,9 +108,11 @@ async fn main() -> Result<(), Error> {
|
||||||
// TODO: Possibly implement "pepper" into this (thinking it could generate one if it doesnt exist and store it on disk)
|
// TODO: Possibly implement "pepper" into this (thinking it could generate one if it doesnt exist and store it on disk)
|
||||||
argon2: Argon2::default(),
|
argon2: Argon2::default(),
|
||||||
start_time: SystemTime::now(),
|
start_time: SystemTime::now(),
|
||||||
bunny_cdn,
|
storage,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let data_dir = args.data_dir.clone();
|
||||||
|
|
||||||
HttpServer::new(move || {
|
HttpServer::new(move || {
|
||||||
// Set CORS headers
|
// Set CORS headers
|
||||||
let cors = Cors::default()
|
let cors = Cors::default()
|
||||||
|
@ -143,9 +142,10 @@ async fn main() -> Result<(), Error> {
|
||||||
App::new()
|
App::new()
|
||||||
.app_data(web::Data::new(data.clone()))
|
.app_data(web::Data::new(data.clone()))
|
||||||
.wrap(cors)
|
.wrap(cors)
|
||||||
|
.service(Files::new("/api/assets", &data_dir))
|
||||||
.service(api::web())
|
.service(api::web())
|
||||||
})
|
})
|
||||||
.bind((web.url, web.port))?
|
.bind((web.ip, web.port))?
|
||||||
.run()
|
.run()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
use actix_web::web::BytesMut;
|
use actix_web::web::BytesMut;
|
||||||
use diesel::{
|
use diesel::{
|
||||||
ExpressionMethods, QueryDsl, Selectable, SelectableHelper, delete, insert_into,
|
ExpressionMethods, QueryDsl, Selectable, SelectableHelper, delete, insert_into,
|
||||||
|
@ -6,11 +8,11 @@ use diesel::{
|
||||||
};
|
};
|
||||||
use diesel_async::{RunQueryDsl, pooled_connection::AsyncDieselConnectionManager};
|
use diesel_async::{RunQueryDsl, pooled_connection::AsyncDieselConnectionManager};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tokio::task;
|
use tokio::{fs::{create_dir_all, remove_file, File}, io::AsyncWriteExt, 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::{config::Config, error::Error, schema::*, utils::image_check, Conn, Data};
|
||||||
|
|
||||||
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,6 +24,74 @@ fn load_or_empty<T>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Storage {
|
||||||
|
bunny_cdn: Option<bunny_api_tokio::Client>,
|
||||||
|
cdn_url: Url,
|
||||||
|
data_dir: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Storage {
|
||||||
|
pub async fn new(config: Config, data_dir: String) -> Result<Self, Error> {
|
||||||
|
let mut bunny_cdn;
|
||||||
|
let cdn_url;
|
||||||
|
|
||||||
|
if let Some(bunny) = config.bunny {
|
||||||
|
bunny_cdn = Some(bunny_api_tokio::Client::new("").await?);
|
||||||
|
|
||||||
|
bunny_cdn
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.storage
|
||||||
|
.init(bunny.api_key, bunny.endpoint, bunny.storage_zone)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
cdn_url = bunny.cdn_url;
|
||||||
|
} else {
|
||||||
|
bunny_cdn = None;
|
||||||
|
cdn_url = config.web.url.join("api/assets/")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
bunny_cdn,
|
||||||
|
data_dir,
|
||||||
|
cdn_url
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn write<T: AsRef<str>>(&self, path: T, bytes: BytesMut) -> Result<Url, Error> {
|
||||||
|
if let Some(bunny_cdn) = &self.bunny_cdn {
|
||||||
|
bunny_cdn.storage.upload(&path, bytes.into()).await?;
|
||||||
|
Ok(self.cdn_url.join(&path.as_ref())?)
|
||||||
|
} else {
|
||||||
|
let file_path = Path::new(&self.data_dir);
|
||||||
|
|
||||||
|
let file_path = file_path.join(path.as_ref());
|
||||||
|
|
||||||
|
create_dir_all(file_path.parent().ok_or(Error::PathError("Unable to get parent directory".to_string()))?).await?;
|
||||||
|
|
||||||
|
let mut file = File::create(file_path).await?;
|
||||||
|
|
||||||
|
file.write_all(&bytes).await?;
|
||||||
|
|
||||||
|
Ok(self.cdn_url.join(path.as_ref())?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn delete<T: AsRef<str>>(&self, path: T) -> Result<(), Error> {
|
||||||
|
if let Some(bunny_cdn) = &self.bunny_cdn {
|
||||||
|
Ok(bunny_cdn.storage.delete(&path).await?)
|
||||||
|
} else {
|
||||||
|
let file_path = Path::new(&self.data_dir);
|
||||||
|
|
||||||
|
let file_path = file_path.join(path.as_ref());
|
||||||
|
|
||||||
|
remove_file(file_path).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Queryable, Selectable, Insertable, Clone)]
|
#[derive(Queryable, Selectable, Insertable, Clone)]
|
||||||
#[diesel(table_name = channels)]
|
#[diesel(table_name = channels)]
|
||||||
#[diesel(check_for_backend(diesel::pg::Pg))]
|
#[diesel(check_for_backend(diesel::pg::Pg))]
|
||||||
|
@ -419,9 +489,8 @@ impl Guild {
|
||||||
// FIXME: Horrible security
|
// FIXME: Horrible security
|
||||||
pub async fn set_icon(
|
pub async fn set_icon(
|
||||||
&mut self,
|
&mut self,
|
||||||
bunny_cdn: &bunny_api_tokio::Client,
|
storage: &Storage,
|
||||||
conn: &mut Conn,
|
conn: &mut Conn,
|
||||||
cdn_url: Url,
|
|
||||||
icon: BytesMut,
|
icon: BytesMut,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let icon_clone = icon.clone();
|
let icon_clone = icon.clone();
|
||||||
|
@ -430,14 +499,12 @@ impl Guild {
|
||||||
if let Some(icon) = &self.icon {
|
if let Some(icon) = &self.icon {
|
||||||
let relative_url = icon.path().trim_start_matches('/');
|
let relative_url = icon.path().trim_start_matches('/');
|
||||||
|
|
||||||
bunny_cdn.storage.delete(relative_url).await?;
|
storage.delete(relative_url).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let path = format!("icons/{}/icon.{}", self.uuid, image_type);
|
let path = format!("icons/{}/icon.{}", self.uuid, image_type);
|
||||||
|
|
||||||
bunny_cdn.storage.upload(path.clone(), icon.into()).await?;
|
let icon_url = storage.write(path.clone(), icon.into()).await?;
|
||||||
|
|
||||||
let icon_url = cdn_url.join(&path)?;
|
|
||||||
|
|
||||||
use guilds::dsl;
|
use guilds::dsl;
|
||||||
update(guilds::table)
|
update(guilds::table)
|
||||||
|
@ -673,9 +740,8 @@ impl Me {
|
||||||
|
|
||||||
pub async fn set_avatar(
|
pub async fn set_avatar(
|
||||||
&mut self,
|
&mut self,
|
||||||
bunny_cdn: &bunny_api_tokio::Client,
|
storage: &Storage,
|
||||||
conn: &mut Conn,
|
conn: &mut Conn,
|
||||||
cdn_url: Url,
|
|
||||||
avatar: BytesMut,
|
avatar: BytesMut,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let avatar_clone = avatar.clone();
|
let avatar_clone = avatar.clone();
|
||||||
|
@ -686,18 +752,15 @@ impl Me {
|
||||||
|
|
||||||
let relative_url = avatar_url.path().trim_start_matches('/');
|
let relative_url = avatar_url.path().trim_start_matches('/');
|
||||||
|
|
||||||
bunny_cdn.storage.delete(relative_url).await?;
|
storage.delete(relative_url).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let path = format!("avatar/{}/avatar.{}", self.uuid, image_type);
|
let path = format!("avatar/{}/avatar.{}", self.uuid, image_type);
|
||||||
|
|
||||||
bunny_cdn
|
let avatar_url = storage
|
||||||
.storage
|
.write(path.clone(), avatar.into())
|
||||||
.upload(path.clone(), avatar.into())
|
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let avatar_url = cdn_url.join(&path)?;
|
|
||||||
|
|
||||||
use users::dsl;
|
use users::dsl;
|
||||||
update(users::table)
|
update(users::table)
|
||||||
.filter(dsl::uuid.eq(self.uuid))
|
.filter(dsl::uuid.eq(self.uuid))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue