You've already forked AstralRinth
forked from didirus/AstralRinth
Initial shared instances backend (#3800)
* Create base shared instance migration and initial routes * Fix build * Add version uploads * Add permissions field for shared instance users * Actually use permissions field * Add "public" flag to shared instances that allow GETing them without authorization * Add the ability to get and list shared instance versions * Add the ability to delete shared instance versions * Fix build after merge * Secured file hosting (#3784) * Remove Backblaze-specific file-hosting backend * Added S3_USES_PATH_STYLE_BUCKETS * Remove unused file_id parameter from delete_file_version * Add support for separate public and private buckets in labrinth::file_hosting * Rename delete_file_version to delete_file * Add (untested) get_url_for_private_file * Remove url field from shared instance routes * Remove url field from shared instance routes * Use private bucket for shared instance versions * Make S3 environment variables fully separate between public and private buckets * Change file host expiry for shared instances to 180 seconds * Fix lint * Merge shared instance migrations into a single migration * Replace shared instance owners with Ghost instead of deleting the instance
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
pub fn parse_var<T: FromStr>(var: &'static str) -> Option<T> {
|
||||
pub fn parse_var<T: FromStr>(var: &str) -> Option<T> {
|
||||
dotenvy::var(var).ok().and_then(|i| i.parse().ok())
|
||||
}
|
||||
pub fn parse_strings_from_var(var: &'static str) -> Option<Vec<String>> {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
pub const MRPACK_MIME_TYPE: &str = "application/x-modrinth-modpack+zip";
|
||||
|
||||
pub fn get_image_content_type(extension: &str) -> Option<&'static str> {
|
||||
match extension {
|
||||
"bmp" => Some("image/bmp"),
|
||||
@@ -24,7 +26,7 @@ pub fn project_file_type(ext: &str) -> Option<&str> {
|
||||
match ext {
|
||||
"jar" => Some("application/java-archive"),
|
||||
"zip" | "litemod" => Some("application/zip"),
|
||||
"mrpack" => Some("application/x-modrinth-modpack+zip"),
|
||||
"mrpack" => Some(MRPACK_MIME_TYPE),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::database;
|
||||
use crate::database::models::image_item;
|
||||
use crate::database::redis::RedisPool;
|
||||
use crate::file_hosting::FileHost;
|
||||
use crate::file_hosting::{FileHost, FileHostPublicity};
|
||||
use crate::models::images::ImageContext;
|
||||
use crate::routes::ApiError;
|
||||
use color_thief::ColorFormat;
|
||||
@@ -38,11 +38,14 @@ pub struct UploadImageResult {
|
||||
pub raw_url: String,
|
||||
pub raw_url_path: String,
|
||||
|
||||
pub publicity: FileHostPublicity,
|
||||
|
||||
pub color: Option<u32>,
|
||||
}
|
||||
|
||||
pub async fn upload_image_optimized(
|
||||
upload_folder: &str,
|
||||
publicity: FileHostPublicity,
|
||||
bytes: bytes::Bytes,
|
||||
file_extension: &str,
|
||||
target_width: Option<u32>,
|
||||
@@ -80,6 +83,7 @@ pub async fn upload_image_optimized(
|
||||
target_width.unwrap_or(0),
|
||||
processed_image_ext
|
||||
),
|
||||
publicity,
|
||||
processed_image,
|
||||
)
|
||||
.await?,
|
||||
@@ -92,6 +96,7 @@ pub async fn upload_image_optimized(
|
||||
.upload_file(
|
||||
content_type,
|
||||
&format!("{upload_folder}/{hash}.{file_extension}"),
|
||||
publicity,
|
||||
bytes,
|
||||
)
|
||||
.await?;
|
||||
@@ -107,6 +112,9 @@ pub async fn upload_image_optimized(
|
||||
|
||||
raw_url: url,
|
||||
raw_url_path: upload_data.file_name,
|
||||
|
||||
publicity,
|
||||
|
||||
color,
|
||||
})
|
||||
}
|
||||
@@ -165,6 +173,7 @@ fn convert_to_webp(img: &DynamicImage) -> Result<Vec<u8>, ImageError> {
|
||||
pub async fn delete_old_images(
|
||||
image_url: Option<String>,
|
||||
raw_image_url: Option<String>,
|
||||
publicity: FileHostPublicity,
|
||||
file_host: &dyn FileHost,
|
||||
) -> Result<(), ApiError> {
|
||||
let cdn_url = dotenvy::var("CDN_URL")?;
|
||||
@@ -173,7 +182,7 @@ pub async fn delete_old_images(
|
||||
let name = image_url.split(&cdn_url_start).nth(1);
|
||||
|
||||
if let Some(icon_path) = name {
|
||||
file_host.delete_file_version("", icon_path).await?;
|
||||
file_host.delete_file(icon_path, publicity).await?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,7 +190,7 @@ pub async fn delete_old_images(
|
||||
let name = raw_image_url.split(&cdn_url_start).nth(1);
|
||||
|
||||
if let Some(icon_path) = name {
|
||||
file_host.delete_file_version("", icon_path).await?;
|
||||
file_host.delete_file(icon_path, publicity).await?;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
use crate::routes::ApiError;
|
||||
use crate::routes::v3::project_creation::CreateError;
|
||||
use crate::util::validate::validation_errors_to_string;
|
||||
use actix_multipart::Field;
|
||||
use actix_web::web::Payload;
|
||||
use bytes::BytesMut;
|
||||
use futures::StreamExt;
|
||||
use serde::de::DeserializeOwned;
|
||||
use validator::Validate;
|
||||
|
||||
pub async fn read_from_payload(
|
||||
pub async fn read_limited_from_payload(
|
||||
payload: &mut Payload,
|
||||
cap: usize,
|
||||
err_msg: &'static str,
|
||||
@@ -25,6 +28,28 @@ pub async fn read_from_payload(
|
||||
Ok(bytes)
|
||||
}
|
||||
|
||||
pub async fn read_typed_from_payload<T>(
|
||||
payload: &mut Payload,
|
||||
) -> Result<T, ApiError>
|
||||
where
|
||||
T: DeserializeOwned + Validate,
|
||||
{
|
||||
let mut bytes = BytesMut::new();
|
||||
while let Some(item) = payload.next().await {
|
||||
bytes.extend_from_slice(&item.map_err(|_| {
|
||||
ApiError::InvalidInput(
|
||||
"Unable to parse bytes in payload sent!".to_string(),
|
||||
)
|
||||
})?);
|
||||
}
|
||||
|
||||
let parsed: T = serde_json::from_slice(&bytes)?;
|
||||
parsed.validate().map_err(|err| {
|
||||
ApiError::InvalidInput(validation_errors_to_string(err, None))
|
||||
})?;
|
||||
Ok(parsed)
|
||||
}
|
||||
|
||||
pub async fn read_from_field(
|
||||
field: &mut Field,
|
||||
cap: usize,
|
||||
|
||||
Reference in New Issue
Block a user