headless_lms_utils/file_store/
media_uploads.rs1use std::path::PathBuf;
5
6use uuid::Uuid;
7
8use super::file_utils::get_extension_from_filename;
9use crate::strings::generate_random_string;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum MediaOwner {
14 Organization(Uuid),
15 Course(Uuid),
16 Exam(Uuid),
17}
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub enum MediaKind {
22 Image,
23 Audio,
24 File,
25}
26
27impl MediaKind {
28 pub fn from_mime_top_level(top_level: &str) -> Self {
30 match top_level {
31 "image" => MediaKind::Image,
32 "audio" => MediaKind::Audio,
33 _ => MediaKind::File,
34 }
35 }
36}
37
38pub fn stored_file_name(original_name: &str) -> String {
41 let mut name = generate_random_string(30);
42 let extension: String = get_extension_from_filename(original_name)
43 .unwrap_or_default()
44 .chars()
45 .filter(|c| c.is_alphanumeric())
46 .collect();
47 if !extension.is_empty() {
48 name.push('.');
49 name.push_str(&extension);
50 }
51 name
52}
53
54pub fn media_path(owner: MediaOwner, kind: MediaKind, file_name: &str) -> PathBuf {
56 let (base_dir, base_id) = match owner {
57 MediaOwner::Organization(id) => ("organization", id),
58 MediaOwner::Course(id) => ("course", id),
59 MediaOwner::Exam(id) => ("exam", id),
60 };
61 let kind_dir = match kind {
62 MediaKind::Image => "images",
63 MediaKind::Audio => "audios",
64 MediaKind::File => "files",
65 };
66 [base_dir, &base_id.to_string(), kind_dir, file_name]
67 .iter()
68 .collect()
69}
70
71const SIZE_LIMITS_BYTES: &[(&str, i32)] = &[
74 ("image", 10 * 1024 * 1024),
75 ("audio", 100 * 1024 * 1024),
76 ("video", 100 * 1024 * 1024),
77 ("application", 25 * 1024 * 1024),
78 ("text", 25 * 1024 * 1024),
79];
80const DEFAULT_SIZE_LIMIT_BYTES: i32 = 10 * 1024 * 1024;
81
82pub fn size_limit_bytes(mime_top_level: Option<&str>) -> i32 {
84 mime_top_level
85 .and_then(|top_level| SIZE_LIMITS_BYTES.iter().find(|(t, _)| *t == top_level))
86 .map(|(_, size)| *size)
87 .unwrap_or(DEFAULT_SIZE_LIMIT_BYTES)
88}