|
|
|
@ -1,7 +1,7 @@
|
|
|
|
|
use sqlx::{sqlite::SqliteQueryResult, Pool, Sqlite};
|
|
|
|
|
use std::{collections::HashMap, time::SystemTime};
|
|
|
|
|
|
|
|
|
|
// This struct is used to store values for metrics file stats.
|
|
|
|
|
/// This struct represents a single file, with a few statistics about said file instance.
|
|
|
|
|
pub struct FileMetric {
|
|
|
|
|
pub filename: String,
|
|
|
|
|
pub filesize: i64,
|
|
|
|
@ -10,6 +10,7 @@ pub struct FileMetric {
|
|
|
|
|
pub expiry: i64,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This struct represents a single mimetype, with a few statistics about said mimetype instance
|
|
|
|
|
pub struct MimetypeMetric {
|
|
|
|
|
pub mimetype: String,
|
|
|
|
|
pub host: String,
|
|
|
|
@ -21,8 +22,8 @@ pub struct MimetypeMetric {
|
|
|
|
|
pub views_dead: i64,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Adding a file to the database
|
|
|
|
|
// TODO: Fix panic on fileadd with same filename (even if isDeleted) (UNIQUE constraint)
|
|
|
|
|
/// Adding a file to the database
|
|
|
|
|
/// TODO: Fix panic on fileadd with same filename (even if isDeleted) (UNIQUE constraint)
|
|
|
|
|
pub async fn add_file(
|
|
|
|
|
sqlconn: &Pool<Sqlite>,
|
|
|
|
|
file: String,
|
|
|
|
@ -95,8 +96,8 @@ pub async fn check_filename(sqlconn: &Pool<Sqlite>, filename: &String) -> bool {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This function receives an adminkey, (and sqlpool) and returns a Some(String) containing the filename that corresponds to the adminkey.
|
|
|
|
|
// It returns files that haven't already been deleted, so this is a 'single-use' operation per file.
|
|
|
|
|
/// This function returns a filename that corresponds to the adminkey that is provided.
|
|
|
|
|
/// This is used for deleting files, so this is mostly a 'single-use' operation, but I imagine that it could be used for something else.
|
|
|
|
|
pub async fn check_adminkey(sqlconn: &Pool<Sqlite>, adminkey: String) -> Option<String> {
|
|
|
|
|
let result = sqlx::query!(
|
|
|
|
|
"SELECT file FROM files WHERE adminkey = ? AND isDeleted = 0",
|
|
|
|
@ -117,7 +118,8 @@ pub async fn check_adminkey(sqlconn: &Pool<Sqlite>, adminkey: String) -> Option<
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This function queries the database for a mimetype for the given file.
|
|
|
|
|
/// This returns the mimetype of the file from the database.
|
|
|
|
|
/// This is used for serving files, so that we can set the mimetype of unsafe files.
|
|
|
|
|
pub async fn get_mimetype(sqlconn: &Pool<Sqlite>, file: String) -> Option<String> {
|
|
|
|
|
let result = sqlx::query!(
|
|
|
|
|
"SELECT mimetype FROM files WHERE file = ? AND isDeleted = 0",
|
|
|
|
@ -138,8 +140,9 @@ pub async fn get_mimetype(sqlconn: &Pool<Sqlite>, file: String) -> Option<String
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Marking a file as deleted in the DB (Doesn't delete the file on disk)
|
|
|
|
|
// This function returns a Some(u64), with the number of rows modified.
|
|
|
|
|
/// This marks a file as deleted in the database.
|
|
|
|
|
/// This doesn't delete the file on disk, however.
|
|
|
|
|
/// Returns the number (Some(u64)) of the number of files deleted.
|
|
|
|
|
pub async fn delete_file(sqlconn: &Pool<Sqlite>, filename: String) -> Option<u64> {
|
|
|
|
|
tracing::debug!("delete_file(adminkey: {})", filename);
|
|
|
|
|
let result = sqlx::query!("UPDATE files SET isDeleted = 1 WHERE file = ?", filename)
|
|
|
|
@ -154,9 +157,8 @@ pub async fn delete_file(sqlconn: &Pool<Sqlite>, filename: String) -> Option<u64
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Updating a files viewcount and accesstime.
|
|
|
|
|
// This receives the a String with the filename, i32 unix timestamp (and sqlpool)
|
|
|
|
|
// This returns a Some(u64), with the number of rows affected.
|
|
|
|
|
/// Updates a file's viewcount and accesstime.
|
|
|
|
|
/// Returns a Some(u64), with the number of rows affected.
|
|
|
|
|
pub async fn update_fileview(
|
|
|
|
|
sqlconn: &Pool<Sqlite>,
|
|
|
|
|
filename: String,
|
|
|
|
@ -178,8 +180,8 @@ pub async fn update_fileview(
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Returns the unix timestamp of the last access - 0 if unviewed.
|
|
|
|
|
// This doesn't do the basic error handling like the above functions, this re-write it completely.
|
|
|
|
|
/// Returns the unix timestamp of the last access - 0 if unviewed.
|
|
|
|
|
/// This will break in 2038
|
|
|
|
|
pub async fn get_accesss_time(sqlconn: &Pool<Sqlite>, filename: String) -> i32 {
|
|
|
|
|
let result = sqlx::query!("SELECT accessed FROM files WHERE file = ?", filename)
|
|
|
|
|
.fetch_one(sqlconn)
|
|
|
|
@ -205,7 +207,7 @@ pub async fn get_accesss_time(sqlconn: &Pool<Sqlite>, filename: String) -> i32 {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Generating a list of files that should be deleted
|
|
|
|
|
/// Generating a list of files that should be deleted
|
|
|
|
|
pub async fn get_old_files(sqlconn: &Pool<Sqlite>) -> Vec<String> {
|
|
|
|
|
// Get the current time
|
|
|
|
|
let time = SystemTime::now()
|
|
|
|
@ -240,9 +242,9 @@ pub async fn get_old_files(sqlconn: &Pool<Sqlite>) -> Vec<String> {
|
|
|
|
|
files
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This function adds a new QR code scan to the database
|
|
|
|
|
// This uses the current time.
|
|
|
|
|
// Version is the 'iteration' of the QR Code, since in the future there wil be different QR Codes.
|
|
|
|
|
/// This adds a new QR code scan to the database.
|
|
|
|
|
/// The version string is the 'iteration' of the qr code itself, as I'd like to print multiple qr codes with multiple features.
|
|
|
|
|
/// TODO: we should probably move the time out of this function, so we can change the time if backfilling.
|
|
|
|
|
pub async fn add_qrscan(
|
|
|
|
|
sqlconn: &Pool<Sqlite>,
|
|
|
|
|
ip: String,
|
|
|
|
@ -270,13 +272,20 @@ pub async fn add_qrscan(
|
|
|
|
|
result
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Updating the expiry_override of a file.
|
|
|
|
|
/// Updating the expiry_override of a file.
|
|
|
|
|
/// There are a few limitations and considerations for expiry_override:
|
|
|
|
|
///
|
|
|
|
|
/// 1. The expiry_override must be set lower than the expiry of the file.
|
|
|
|
|
/// 2. EXCEPT when the file has been uploaded by an API Key that has permission to change expiry_override beyond the expiry
|
|
|
|
|
/// 3. The expiry_override cannot be set longer than file_expiry_max, unless the file has been uploaded by an API Key that allows 'permanent' uploads.
|
|
|
|
|
///
|
|
|
|
|
/// If /any/ of those limits are broken, this function will not update and return an error, with the reason why.
|
|
|
|
|
fn update_expiry_override() {
|
|
|
|
|
todo!()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This function gets a array of all the files uploaded by a specific API Key.
|
|
|
|
|
// This allows for the select few who hold an API key to view the information about the files they upload.
|
|
|
|
|
/// This function gets a array of all the files uploaded by a specific API Key.
|
|
|
|
|
/// This allows for the select few who hold an API key to view the information about the files they upload.
|
|
|
|
|
pub async fn get_my_files(sqlconn: &Pool<Sqlite>) -> Option<Vec<FileMetric>> {
|
|
|
|
|
// TODO: PLEASE OH PLEASE DO NOT USE THIS FUNCTION YET, this returns ALL files uploaded.
|
|
|
|
|
let result = sqlx::query!(
|
|
|
|
@ -312,8 +321,9 @@ pub async fn get_my_files(sqlconn: &Pool<Sqlite>) -> Option<Vec<FileMetric>> {
|
|
|
|
|
|
|
|
|
|
// Globally important stats.
|
|
|
|
|
|
|
|
|
|
// This function counts the total files that have been uploaded per ip.
|
|
|
|
|
// This returns a hashmap<String, i32> containing the IP, and the number of files uploaded.
|
|
|
|
|
/// This counts the total files that have been uploaded per ip address.
|
|
|
|
|
/// This returns a hashmap<String, i32> containing the IP, and the number of files uploaded.
|
|
|
|
|
/// This isn't perfect, as some devices that use IPv6 can sometimes use that, but then also switch to IPv4.
|
|
|
|
|
pub async fn get_total_uploads_ip(sqlconn: &Pool<Sqlite>) -> Option<HashMap<String, i32>> {
|
|
|
|
|
// SELECT ip, COUNT(file) as count FROM files GROUP BY ip ORDER BY COUNT;
|
|
|
|
|
let result = sqlx::query!(
|
|
|
|
@ -339,8 +349,12 @@ pub async fn get_total_uploads_ip(sqlconn: &Pool<Sqlite>) -> Option<HashMap<Stri
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This function queries the db for the total filesize of all alive files.
|
|
|
|
|
// Returns a Option<u128> because why not, an i32 isn't big enough anyway.
|
|
|
|
|
/// This queries the db for the total filesize (in bytes) of all alive files.
|
|
|
|
|
/// This can VERY Quickly become massive, so we use a u128.
|
|
|
|
|
///
|
|
|
|
|
/// 340,282,366,920,938,463,463,374,607,431,768,211,455 bytes
|
|
|
|
|
/// 3.095×10^26 TiB, or 309500000000000000000000000 TiB
|
|
|
|
|
/// or 3700000000000000000000 * more data than exists on the internet (as of 2014).
|
|
|
|
|
pub async fn total_alive_filesize(sqlconn: &Pool<Sqlite>) -> Option<u128> {
|
|
|
|
|
let mut total_filesize: u128 = 0;
|
|
|
|
|
let result = sqlx::query!(
|
|
|
|
@ -362,8 +376,14 @@ pub async fn total_alive_filesize(sqlconn: &Pool<Sqlite>) -> Option<u128> {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This function queries the db for the total filesize of all alive files.
|
|
|
|
|
// Returns a Option<u128> because why not, an i32 isn't big enough anyway.
|
|
|
|
|
/// This queries the db for the total filesize (in bytes) of all dead files.
|
|
|
|
|
/// This can VERY Quickly become massive, so we use a u128.
|
|
|
|
|
///
|
|
|
|
|
/// 340,282,366,920,938,463,463,374,607,431,768,211,455 bytes
|
|
|
|
|
///
|
|
|
|
|
/// 3.095×10^26 TiB, or 309500000000000000000000000 TiB
|
|
|
|
|
///
|
|
|
|
|
/// or 3700000000000000000000 * more data than exists on the internet (as of 2014).
|
|
|
|
|
pub async fn total_dead_filesize(sqlconn: &Pool<Sqlite>) -> Option<u128> {
|
|
|
|
|
let mut total_dead_filesize: u128 = 0;
|
|
|
|
|
let result = sqlx::query!(
|
|
|
|
@ -385,10 +405,10 @@ pub async fn total_dead_filesize(sqlconn: &Pool<Sqlite>) -> Option<u128> {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This function queries the db for the filesize for /each/ alive file. - We won't need to do this for dead files
|
|
|
|
|
// since they were alive at some point, and when they are removed from the alive list, we don't really care.
|
|
|
|
|
// This returns a Hashmap of the filename, filetype, and filesize inside a Vector. (How messy)
|
|
|
|
|
// We want to group them by file, or filetype, or total in grafana, so it needs to be run on each file.
|
|
|
|
|
/// This queries the db for the filesize for /each/ alive file. - We won't need to do this for dead files
|
|
|
|
|
/// since they were alive at some point, and when they are removed from the alive list, we don't really care.
|
|
|
|
|
/// This returns a Hashmap of the filename, filetype, and filesize inside a Vector. (How messy)
|
|
|
|
|
/// We want to group them by file, or filetype, or total in grafana, so it needs to be run on each file.
|
|
|
|
|
pub async fn get_file_metrics(sqlconn: &Pool<Sqlite>) -> Option<Vec<FileMetric>> {
|
|
|
|
|
let result = sqlx::query!(
|
|
|
|
|
"SELECT file, filesize, mimetype, views, expiry
|
|
|
|
@ -422,8 +442,8 @@ pub async fn get_file_metrics(sqlconn: &Pool<Sqlite>) -> Option<Vec<FileMetric>>
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This function queries the db for metrics related to mimetypes.
|
|
|
|
|
// Returns a Vec of if the MimetypeMetric Struct.
|
|
|
|
|
/// This function queries the db for metrics related to mimetypes.
|
|
|
|
|
/// Returns a Vec of if the MimetypeMetric Struct.
|
|
|
|
|
pub async fn get_mimetype_metrics(sqlconn: &Pool<Sqlite>) -> Option<Vec<MimetypeMetric>> {
|
|
|
|
|
// Get stats about the files
|
|
|
|
|
// This is insane, ChatGPT wrote this entire query. I did no changes at all and it just worked.
|
|
|
|
|