Files
modrinth/apps/labrinth/src/file_hosting/mod.rs
T
e7926083fb feat: new modpack permissions system (#6005)
* Begin external projects moderator database frontend

* add copy link button

* begin project page permissions settings

* MEL database backend routes

* include filename in external files

* wip: when uploading a version file, fetch its overrides as a list

* wip: override license checks

* improve FileHost ref counting

* file host read capability

* scan files when inserting version file

* add dependency sha1 field

* clean up version files

* wip: attributions

* update s3 file host

* attribution scanning basic works

* works

* insert attribution info after resolving

* add routes

* remove dep sha1 stuff

* prepr

* wip: override file sources

* add files_missing_attributions to versions

* return extended version info + attributed at/by

* hook up frontend to backend (mostly)

* expose version date published

* withholding version visibility

* frontend work

* prepr

* use api-client for img upload

* moar frontend

* prepr

* Add schema to attribution resolution and Flame project results

* sqlx prepare

* changes

* remove feature flag, fix optional proof images

* fix schema

* fmt

* fix deletion and file fetch

* prepare

* fix admonition

* update frontend stuff to new schema

* prepr

* attribution on dependencies

* fixes

* sqlx prepare

* fixes

* routes

* fix routes

* Version grandfathering

* prepare

* wip: bulk routes

* pushing what i've got rn

* include link in NoPermission

* change hash insert to bulk route

* query flame even if entry in MEL

* delete file with weird name

* Prioritise putting override files in existing groups even with ExternalLicense

* fix how hex bytes are handled in route

* feat: coolbot moderation changes (#6215)

* Update moderator checklist

* move permissions stage order

* Updated nagContext.versions to v3, added nag for permissions

* Update permissions.vue default messages

* prepr

---------

Co-authored-by: coolbot100s <76798835+coolbot100s@users.noreply.github.com>

* QA

* prepr

* should group by project

* return attribution resolution correctly

* updated by moderator info

* Track what moderator reviewed an attribution moderation status

* default deser FMA field

* new version page

* clean up fetching + add a couple missing features

* qa items

* prepr

* provide moderation package stuff with DI

* format?

* don't redact moderated_at

* move supplementary resources

* Reorganize moderation messages.

* Quick replies for external content permissions.

* prepare

* QA

* allow exempting projects

* Ignore Flame projects which 404

* fix ci

* fix cross project attribution stuff

* Fix permission error

* change what files get cscanned

* add more logging

* QA Jun 22

* fix

* idempotency

* Expose route for rescanning

* update blog link

---------

Co-authored-by: aecsocket <aecsocket@tutanota.com>
Co-authored-by: coolbot100s <76798835+coolbot100s@users.noreply.github.com>
Co-authored-by: aecsocket <43144841+aecsocket@users.noreply.github.com>
2026-06-23 21:27:51 +02:00

113 lines
2.9 KiB
Rust

use std::error::Error;
use std::str::FromStr;
use async_trait::async_trait;
use thiserror::Error;
mod mock;
mod s3_host;
use bytes::Bytes;
pub use mock::MockHost;
pub use s3_host::{S3BucketConfig, S3Host};
#[derive(Error, Debug)]
pub enum FileHostingError {
#[error("S3 error when {0}: {1}")]
S3Error(&'static str, #[source] Box<dyn Error + Send + Sync>),
#[error("File system error in file hosting: {0}")]
FileSystemError(#[from] std::io::Error),
#[error("Invalid Filename")]
InvalidFilename,
}
#[derive(Debug, Clone)]
pub struct UploadFileData {
pub file_name: String,
pub file_publicity: FileHostPublicity,
pub content_length: u32,
pub content_sha512: String,
pub content_sha1: String,
pub content_md5: Option<String>,
pub content_type: String,
pub upload_timestamp: u64,
}
#[derive(Debug, Clone)]
pub struct DeleteFileData {
pub file_name: String,
}
#[derive(Debug, Copy, Clone)]
pub enum FileHostPublicity {
Public,
Private,
}
#[async_trait]
pub trait FileHost: Send + Sync {
/// Uploads a file at the exact storage key provided.
///
/// Callers must URL-decode keys derived from public URLs before passing
/// them here, and URL-encode this key before exposing it in a public URL.
async fn upload_file(
&self,
content_type: &str,
file_name: &str,
file_publicity: FileHostPublicity,
file_bytes: Bytes,
) -> Result<UploadFileData, FileHostingError>;
/// Returns a private URL for the exact storage key provided.
///
/// Callers must URL-decode keys derived from public URLs before passing
/// them here.
async fn get_url_for_private_file(
&self,
file_name: &str,
expiry_secs: u32,
) -> Result<String, FileHostingError>;
/// Deletes the file at the exact storage key provided.
///
/// Callers must URL-decode keys derived from public URLs before passing
/// them here.
async fn delete_file(
&self,
file_name: &str,
file_publicity: FileHostPublicity,
) -> Result<DeleteFileData, FileHostingError>;
/// Reads the file at the exact storage key provided.
///
/// Callers must URL-decode keys derived from public URLs before passing
/// them here.
async fn read_file(
&self,
file_name: &str,
file_publicity: FileHostPublicity,
) -> Result<Bytes, FileHostingError>;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum FileHostKind {
S3,
Local,
}
#[derive(Debug, Error)]
#[error("invalid file host kind")]
pub struct InvalidFileHostKind;
impl FromStr for FileHostKind {
type Err = InvalidFileHostKind;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"s3" => Self::S3,
"local" => Self::Local,
_ => return Err(InvalidFileHostKind),
})
}
}