diff --git a/.env b/.env
index 59cfac1f..b9df4cad 100644
--- a/.env
+++ b/.env
@@ -151,6 +151,10 @@ export MICROBIN_GC_DAYS=90
# Default value: false
export MICROBIN_ENABLE_BURN_AFTER=true
+# Enables or disables the "Custom Url" function
+# Default value: false
+export MICROBIN_ENABLE_CUSTOM_URL=true
+
# Sets the default burn after setting on the main screen.
# Default value: 0. Available expiration options: 1, 10,
# 100, 1000, 10000, 0 (= no limit)
diff --git a/README.md b/README.md
index f3a7c6dc..df83ca1b 100644
--- a/README.md
+++ b/README.md
@@ -45,6 +45,7 @@ On our website [microbin.eu](https://microbin.eu) you will find the following:
- QR code support
- URL shortening and redirection
- Animal names instead of random numbers for upload identifiers (64 animals)
+- Custom URL
- SQLite and JSON database support
- Private and public, editable and uneditable, automatically and never expiring uploads
- Automatic dark mode and custom styling support with very little CSS and only vanilla JS (see [`water.css`](https://github.com/kognise/water.css))
diff --git a/compose.yaml b/compose.yaml
index 60ffb6ab..aaeaf54f 100644
--- a/compose.yaml
+++ b/compose.yaml
@@ -31,6 +31,7 @@ services:
MICROBIN_THREADS: ${MICROBIN_THREADS}
MICROBIN_GC_DAYS: ${MICROBIN_GC_DAYS}
MICROBIN_ENABLE_BURN_AFTER: ${MICROBIN_ENABLE_BURN_AFTER}
+ MICROBIN_ENABLE_CUSTOM_URL: ${MICROBIN_ENABLE_CUSTOM_URL}
MICROBIN_DEFAULT_BURN_AFTER: ${MICROBIN_DEFAULT_BURN_AFTER}
MICROBIN_WIDE: ${MICROBIN_WIDE}
MICROBIN_QR: ${MICROBIN_QR}
diff --git a/src/args.rs b/src/args.rs
index 64dccb54..5d1bd58e 100644
--- a/src/args.rs
+++ b/src/args.rs
@@ -103,6 +103,9 @@ pub struct Args {
#[clap(long, env = "MICROBIN_ENABLE_READONLY")]
pub enable_readonly: bool,
+ #[clap(long, env = "MICROBIN_ENABLE_CUSTOM_URL")]
+ pub enable_custom_url: bool,
+
#[clap(long, env = "MICROBIN_DEFAULT_EXPIRY", default_value = "24hour")]
pub default_expiry: String,
@@ -114,7 +117,7 @@ pub struct Args {
#[clap(long, env = "MICROBIN_CUSTOM_CSS")]
pub custom_css: Option,
-
+
#[clap(long, env = "MICROBIN_HASH_IDS")]
pub hash_ids: bool,
@@ -211,6 +214,7 @@ impl Args {
max_file_size_encrypted_mb: self.max_file_size_encrypted_mb,
max_file_size_unencrypted_mb: self.max_file_size_unencrypted_mb,
disable_update_checking: self.disable_update_checking,
+ enable_custom_url: self.enable_custom_url,
}
}
}
diff --git a/src/endpoints/auth_upload.rs b/src/endpoints/auth_upload.rs
index fd605db1..4efc8a8f 100644
--- a/src/endpoints/auth_upload.rs
+++ b/src/endpoints/auth_upload.rs
@@ -1,7 +1,6 @@
use crate::args::{Args, ARGS};
use crate::endpoints::errors::ErrorTemplate;
-use crate::util::animalnumbers::to_u64;
-use crate::util::hashids::to_u64 as hashid_to_u64;
+use crate::util::hashids::alias_comparator;
use crate::util::misc::remove_expired;
use crate::AppState;
use actix_web::{get, web, HttpResponse};
@@ -25,14 +24,10 @@ pub async fn auth_upload(data: web::Data, id: web::Path) -> Ht
remove_expired(&mut pastas);
- let intern_id = if ARGS.hash_ids {
- hashid_to_u64(&id).unwrap_or(0)
- } else {
- to_u64(&id).unwrap_or(0)
- };
+ let comparator = alias_comparator(id.as_str());
for (_i, pasta) in pastas.iter().enumerate() {
- if pasta.id == intern_id {
+ if comparator(pasta) {
return HttpResponse::Ok().content_type("text/html").body(
AuthPasta {
args: &ARGS,
@@ -65,14 +60,10 @@ pub async fn auth_upload_with_status(
let (id, status) = param.into_inner();
- let intern_id = if ARGS.hash_ids {
- hashid_to_u64(&id).unwrap_or(0)
- } else {
- to_u64(&id).unwrap_or(0)
- };
+ let comparator = alias_comparator(id.as_str());
for (_i, pasta) in pastas.iter().enumerate() {
- if pasta.id == intern_id {
+ if comparator(pasta) {
return HttpResponse::Ok().content_type("text/html").body(
AuthPasta {
args: &ARGS,
@@ -100,14 +91,10 @@ pub async fn auth_raw_pasta(data: web::Data, id: web::Path) ->
remove_expired(&mut pastas);
- let intern_id = if ARGS.hash_ids {
- hashid_to_u64(&id).unwrap_or(0)
- } else {
- to_u64(&id).unwrap_or(0)
- };
+ let comparator = alias_comparator(id.as_str());
for (_i, pasta) in pastas.iter().enumerate() {
- if pasta.id == intern_id {
+ if comparator(pasta) {
return HttpResponse::Ok().content_type("text/html").body(
AuthPasta {
args: &ARGS,
@@ -140,14 +127,10 @@ pub async fn auth_raw_pasta_with_status(
let (id, status) = param.into_inner();
- let intern_id = if ARGS.hash_ids {
- hashid_to_u64(&id).unwrap_or(0)
- } else {
- to_u64(&id).unwrap_or(0)
- };
+ let comparator = alias_comparator(id.as_str());
for (_i, pasta) in pastas.iter().enumerate() {
- if pasta.id == intern_id {
+ if comparator(pasta) {
return HttpResponse::Ok().content_type("text/html").body(
AuthPasta {
args: &ARGS,
@@ -175,14 +158,10 @@ pub async fn auth_edit_private(data: web::Data, id: web::Path)
remove_expired(&mut pastas);
- let intern_id = if ARGS.hash_ids {
- hashid_to_u64(&id).unwrap_or(0)
- } else {
- to_u64(&id).unwrap_or(0)
- };
+ let comparator = alias_comparator(id.as_str());
for (_, pasta) in pastas.iter().enumerate() {
- if pasta.id == intern_id {
+ if comparator(pasta) {
return HttpResponse::Ok().content_type("text/html").body(
AuthPasta {
args: &ARGS,
@@ -215,14 +194,10 @@ pub async fn auth_edit_private_with_status(
let (id, status) = param.into_inner();
- let intern_id = if ARGS.hash_ids {
- hashid_to_u64(&id).unwrap_or(0)
- } else {
- to_u64(&id).unwrap_or(0)
- };
+ let comparator = alias_comparator(id.as_str());
for (_i, pasta) in pastas.iter().enumerate() {
- if pasta.id == intern_id {
+ if comparator(pasta) {
return HttpResponse::Ok().content_type("text/html").body(
AuthPasta {
args: &ARGS,
@@ -250,14 +225,10 @@ pub async fn auth_file(data: web::Data, id: web::Path) -> Http
remove_expired(&mut pastas);
- let intern_id = if ARGS.hash_ids {
- hashid_to_u64(&id).unwrap_or(0)
- } else {
- to_u64(&id).unwrap_or(0)
- };
+ let comparator = alias_comparator(id.as_str());
for (_, pasta) in pastas.iter().enumerate() {
- if pasta.id == intern_id {
+ if comparator(pasta) {
return HttpResponse::Ok().content_type("text/html").body(
AuthPasta {
args: &ARGS,
@@ -290,14 +261,10 @@ pub async fn auth_file_with_status(
let (id, status) = param.into_inner();
- let intern_id = if ARGS.hash_ids {
- hashid_to_u64(&id).unwrap_or(0)
- } else {
- to_u64(&id).unwrap_or(0)
- };
+ let comparator = alias_comparator(id.as_str());
for (_i, pasta) in pastas.iter().enumerate() {
- if pasta.id == intern_id {
+ if comparator(pasta) {
return HttpResponse::Ok().content_type("text/html").body(
AuthPasta {
args: &ARGS,
@@ -325,14 +292,10 @@ pub async fn auth_remove_private(data: web::Data, id: web::Path() as u64,
content: String::from(""),
file: None,
+ custom_alias: None,
extension: String::from(""),
private: false,
readonly: false,
@@ -207,6 +211,31 @@ pub async fn create(
}
continue;
}
+ "custom_alias" => {
+ if !ARGS.enable_custom_url {
+ continue;
+ }
+ while let Some(chunk) = field.try_next().await? {
+ let custom_alias_unchecked =
+ std::str::from_utf8(&chunk).unwrap().trim().to_string();
+ // todo check it
+ if hashid_to_u64(&custom_alias_unchecked).is_ok()
+ || to_u64(&&custom_alias_unchecked).is_ok()
+ {
+ // prevent conflicts with default url.
+ return Err(ErrorForbidden("Conflicts with default URL format"));
+ }
+ if pastas
+ .iter()
+ .any(|pasta| pasta.custom_alias.as_ref() == Some(&custom_alias_unchecked))
+ {
+ // prevent conflicts with default url.
+ return Err(ErrorForbidden("Custom url conflicts with existing pasta"));
+ }
+ new_pasta.custom_alias = Some(custom_alias_unchecked);
+ }
+ continue;
+ }
"file" => {
if ARGS.no_file_upload {
continue;
@@ -303,6 +332,7 @@ pub async fn create(
}
let encrypt_server = new_pasta.encrypt_server;
+ let alias = new_pasta.custom_alias.clone();
pastas.push(new_pasta);
@@ -312,7 +342,9 @@ pub async fn create(
}
}
- let slug = if ARGS.hash_ids {
+ let slug = if let Some(alias) = alias {
+ alias
+ } else if ARGS.hash_ids {
to_hashids(id)
} else {
to_animal_names(id)
diff --git a/src/endpoints/edit.rs b/src/endpoints/edit.rs
index e379b788..b64a7cf7 100644
--- a/src/endpoints/edit.rs
+++ b/src/endpoints/edit.rs
@@ -1,8 +1,7 @@
use crate::args::Args;
use crate::endpoints::errors::ErrorTemplate;
-use crate::util::animalnumbers::to_u64;
use crate::util::db::update;
-use crate::util::hashids::to_u64 as hashid_to_u64;
+use crate::util::hashids::alias_comparator;
use crate::util::misc::{decrypt, encrypt, remove_expired};
use crate::{AppState, Pasta, ARGS};
use actix_multipart::Multipart;
@@ -23,16 +22,12 @@ struct EditTemplate<'a> {
pub async fn get_edit(data: web::Data, id: web::Path) -> HttpResponse {
let mut pastas = data.pastas.lock().unwrap();
- let id = if ARGS.hash_ids {
- hashid_to_u64(&id).unwrap_or(0)
- } else {
- to_u64(&id.into_inner()).unwrap_or(0)
- };
+ let comparator = alias_comparator(id.as_str());
remove_expired(&mut pastas);
for pasta in pastas.iter() {
- if pasta.id == id {
+ if comparator(pasta) {
if !pasta.editable {
return HttpResponse::Found()
.append_header(("Location", format!("{}/", ARGS.public_path_as_str())))
@@ -75,16 +70,12 @@ pub async fn get_edit_with_status(
let (id, status) = param.into_inner();
- let intern_id = if ARGS.hash_ids {
- hashid_to_u64(&id).unwrap_or(0)
- } else {
- to_u64(&id).unwrap_or(0)
- };
+ let comparator = alias_comparator(id.as_str());
remove_expired(&mut pastas);
for pasta in pastas.iter() {
- if pasta.id == intern_id {
+ if comparator(pasta) {
if !pasta.editable {
return HttpResponse::Found()
.append_header(("Location", format!("{}/", ARGS.public_path_as_str())))
@@ -127,11 +118,7 @@ pub async fn post_edit_private(
// get access to the pasta collection
let mut pastas = data.pastas.lock().unwrap();
- let id = if ARGS.hash_ids {
- hashid_to_u64(&id).unwrap_or(0)
- } else {
- to_u64(&id.into_inner()).unwrap_or(0)
- };
+ let comparator = alias_comparator(id.as_str());
let mut password = String::from("");
@@ -150,7 +137,7 @@ pub async fn post_edit_private(
let mut index: usize = 0;
let mut found: bool = false;
for (i, pasta) in pastas.iter().enumerate() {
- if pasta.id == id {
+ if comparator(pasta) {
index = i;
found = true;
break;
@@ -214,11 +201,7 @@ pub async fn post_submit_edit_private(
// get access to the pasta collection
let mut pastas = data.pastas.lock().unwrap();
- let id = if ARGS.hash_ids {
- hashid_to_u64(&id).unwrap_or(0)
- } else {
- to_u64(&id.into_inner()).unwrap_or(0)
- };
+ let comparator = alias_comparator(id.as_str());
let mut password = String::from("");
let mut new_content = String::from("");
@@ -243,7 +226,7 @@ pub async fn post_submit_edit_private(
let mut index: usize = 0;
let mut found: bool = false;
for (i, pasta) in pastas.iter().enumerate() {
- if pasta.id == id {
+ if comparator(pasta) {
index = i;
found = true;
break;
@@ -304,11 +287,7 @@ pub async fn post_edit(
id: web::Path,
mut payload: Multipart,
) -> Result {
- let id = if ARGS.hash_ids {
- hashid_to_u64(&id).unwrap_or(0)
- } else {
- to_u64(&id.into_inner()).unwrap_or(0)
- };
+ let comparator = alias_comparator(id.as_str());
let mut pastas = data.pastas.lock().unwrap();
@@ -331,7 +310,7 @@ pub async fn post_edit(
}
for (i, pasta) in pastas.iter().enumerate() {
- if pasta.id == id {
+ if comparator(pasta) {
if pasta.editable && !pasta.encrypt_client {
if pastas[i].readonly || pastas[i].encrypt_server {
if password != *"" {
diff --git a/src/endpoints/file.rs b/src/endpoints/file.rs
index 289bf663..ad58e455 100644
--- a/src/endpoints/file.rs
+++ b/src/endpoints/file.rs
@@ -2,9 +2,9 @@ use std::fs::{self, File};
use std::path::PathBuf;
use crate::args::ARGS;
-use crate::util::hashids::to_u64 as hashid_to_u64;
+use crate::util::hashids::alias_comparator;
use crate::util::misc::remove_expired;
-use crate::util::{animalnumbers::to_u64, misc::decrypt_file};
+use crate::util::misc::decrypt_file;
use crate::AppState;
use actix_multipart::Multipart;
use actix_web::{get, post, web, Error, HttpResponse};
@@ -19,11 +19,7 @@ pub async fn post_secure_file(
// get access to the pasta collection
let mut pastas = data.pastas.lock().unwrap();
- let id = if ARGS.hash_ids {
- hashid_to_u64(&id).unwrap_or(0)
- } else {
- to_u64(&id.into_inner()).unwrap_or(0)
- };
+ let comparator = alias_comparator(id.as_str());
// remove expired pastas (including this one if needed)
remove_expired(&mut pastas);
@@ -32,7 +28,7 @@ pub async fn post_secure_file(
let mut index: usize = 0;
let mut found: bool = false;
for (i, pasta) in pastas.iter().enumerate() {
- if pasta.id == id {
+ if comparator(pasta) {
index = i;
found = true;
break;
@@ -86,11 +82,7 @@ pub async fn get_file(
// get access to the pasta collection
let mut pastas = data.pastas.lock().unwrap();
- let id_intern = if ARGS.hash_ids {
- hashid_to_u64(&id).unwrap_or(0)
- } else {
- to_u64(&id.into_inner()).unwrap_or(0)
- };
+ let comparator = alias_comparator(id.as_str());
// remove expired pastas (including this one if needed)
remove_expired(&mut pastas);
@@ -99,7 +91,7 @@ pub async fn get_file(
let mut index: usize = 0;
let mut found: bool = false;
for (i, pasta) in pastas.iter().enumerate() {
- if pasta.id == id_intern {
+ if comparator(pasta) {
index = i;
found = true;
break;
diff --git a/src/endpoints/list.rs b/src/endpoints/list.rs
index 8d7f3a64..78ee8a0b 100644
--- a/src/endpoints/list.rs
+++ b/src/endpoints/list.rs
@@ -21,7 +21,7 @@ pub async fn list(data: web::Data) -> HttpResponse {
.finish();
}
- let mut pastas = data.pastas.lock().unwrap();
+ let mut pastas: std::sync::MutexGuard<'_, Vec> = data.pastas.lock().unwrap();
remove_expired(&mut pastas);
diff --git a/src/endpoints/pasta.rs b/src/endpoints/pasta.rs
index ee7916d8..76e49927 100644
--- a/src/endpoints/pasta.rs
+++ b/src/endpoints/pasta.rs
@@ -1,9 +1,8 @@
use crate::args::{Args, ARGS};
use crate::endpoints::errors::ErrorTemplate;
use crate::pasta::Pasta;
-use crate::util::animalnumbers::to_u64;
use crate::util::db::update;
-use crate::util::hashids::to_u64 as hashid_to_u64;
+use crate::util::hashids::alias_comparator;
use crate::util::misc::remove_expired;
use crate::AppState;
use actix_multipart::Multipart;
@@ -27,12 +26,8 @@ fn pastaresponse(
) -> HttpResponse {
// get access to the pasta collection
let mut pastas = data.pastas.lock().unwrap();
+ let comparator = alias_comparator(id.as_str());
- let id = if ARGS.hash_ids {
- hashid_to_u64(&id).unwrap_or(0)
- } else {
- to_u64(&id.into_inner()).unwrap_or(0)
- };
// remove expired pastas (including this one if needed)
remove_expired(&mut pastas);
@@ -41,7 +36,7 @@ fn pastaresponse(
let mut index: usize = 0;
let mut found: bool = false;
for (i, pasta) in pastas.iter().enumerate() {
- if pasta.id == id {
+ if comparator(pasta) {
index = i;
found = true;
break;
@@ -173,11 +168,7 @@ fn urlresponse(data: web::Data, id: web::Path) -> HttpResponse
// get access to the pasta collection
let mut pastas = data.pastas.lock().unwrap();
- let id = if ARGS.hash_ids {
- hashid_to_u64(&id).unwrap_or(0)
- } else {
- to_u64(&id.into_inner()).unwrap_or(0)
- };
+ let comparator = alias_comparator(id.as_str());
// remove expired pastas (including this one if needed)
remove_expired(&mut pastas);
@@ -187,7 +178,7 @@ fn urlresponse(data: web::Data, id: web::Path) -> HttpResponse
let mut found: bool = false;
for (i, pasta) in pastas.iter().enumerate() {
- if pasta.id == id {
+ if comparator(pasta) {
index = i;
found = true;
break;
@@ -255,11 +246,8 @@ pub async fn getrawpasta(
// get access to the pasta collection
let mut pastas = data.pastas.lock().unwrap();
- let id = if ARGS.hash_ids {
- hashid_to_u64(&id).unwrap_or(0)
- } else {
- to_u64(&id.into_inner()).unwrap_or(0)
- };
+ let comparator = alias_comparator(id.as_str());
+
// remove expired pastas (including this one if needed)
remove_expired(&mut pastas);
@@ -268,7 +256,7 @@ pub async fn getrawpasta(
let mut index: usize = 0;
let mut found: bool = false;
for (i, pasta) in pastas.iter().enumerate() {
- if pasta.id == id {
+ if comparator(pasta) {
index = i;
found = true;
break;
@@ -336,11 +324,7 @@ pub async fn postrawpasta(
// get access to the pasta collection
let mut pastas = data.pastas.lock().unwrap();
- let id = if ARGS.hash_ids {
- hashid_to_u64(&id).unwrap_or(0)
- } else {
- to_u64(&id.into_inner()).unwrap_or(0)
- };
+ let comparator = alias_comparator(id.as_str());
// remove expired pastas (including this one if needed)
remove_expired(&mut pastas);
@@ -349,7 +333,7 @@ pub async fn postrawpasta(
let mut index: usize = 0;
let mut found: bool = false;
for (i, pasta) in pastas.iter().enumerate() {
- if pasta.id == id {
+ if comparator(pasta) {
index = i;
found = true;
break;
diff --git a/src/endpoints/qr.rs b/src/endpoints/qr.rs
index 785f7105..7f46a46d 100644
--- a/src/endpoints/qr.rs
+++ b/src/endpoints/qr.rs
@@ -1,8 +1,7 @@
use crate::args::{Args, ARGS};
use crate::endpoints::errors::ErrorTemplate;
use crate::pasta::Pasta;
-use crate::util::animalnumbers::to_u64;
-use crate::util::hashids::to_u64 as hashid_to_u64;
+use crate::util::hashids::alias_comparator;
use crate::util::misc::{self, remove_expired};
use crate::AppState;
use actix_web::{get, web, HttpResponse};
@@ -21,11 +20,7 @@ pub async fn getqr(data: web::Data, id: web::Path) -> HttpResp
// get access to the pasta collection
let mut pastas = data.pastas.lock().unwrap();
- let u64_id = if ARGS.hash_ids {
- hashid_to_u64(&id).unwrap_or(0)
- } else {
- to_u64(&id).unwrap_or(0)
- };
+ let comparator = alias_comparator(id.as_str());
// remove expired pastas (including this one if needed)
remove_expired(&mut pastas);
@@ -34,7 +29,7 @@ pub async fn getqr(data: web::Data, id: web::Path) -> HttpResp
let mut index: usize = 0;
let mut found: bool = false;
for (i, pasta) in pastas.iter().enumerate() {
- if pasta.id == u64_id {
+ if comparator(pasta) {
index = i;
found = true;
break;
diff --git a/src/endpoints/remove.rs b/src/endpoints/remove.rs
index 644fcab8..38dffa84 100644
--- a/src/endpoints/remove.rs
+++ b/src/endpoints/remove.rs
@@ -5,9 +5,8 @@ use futures::TryStreamExt;
use crate::args::ARGS;
use crate::endpoints::errors::ErrorTemplate;
use crate::pasta::PastaFile;
-use crate::util::animalnumbers::to_u64;
use crate::util::db::delete;
-use crate::util::hashids::to_u64 as hashid_to_u64;
+use crate::util::hashids::alias_comparator;
use crate::util::misc::{decrypt, remove_expired};
use crate::AppState;
use askama::Template;
@@ -17,14 +16,11 @@ use std::fs;
pub async fn remove(data: web::Data, id: web::Path) -> HttpResponse {
let mut pastas = data.pastas.lock().unwrap();
- let id = if ARGS.hash_ids {
- hashid_to_u64(&id).unwrap_or(0)
- } else {
- to_u64(&id.into_inner()).unwrap_or(0)
- };
+ let comparator = alias_comparator(id.as_str());
for (i, pasta) in pastas.iter().enumerate() {
- if pasta.id == id {
+ if comparator(pasta) {
+ let id = pasta.id;
// if it's encrypted or read-only, it needs password to be deleted
if pasta.encrypt_server || pasta.readonly {
return HttpResponse::Found()
@@ -84,11 +80,7 @@ pub async fn post_remove(
id: web::Path,
mut payload: Multipart,
) -> Result {
- let id = if ARGS.hash_ids {
- hashid_to_u64(&id).unwrap_or(0)
- } else {
- to_u64(&id.into_inner()).unwrap_or(0)
- };
+ let comparator = alias_comparator(id.as_str());
let mut pastas = data.pastas.lock().unwrap();
@@ -105,7 +97,8 @@ pub async fn post_remove(
}
for (i, pasta) in pastas.iter().enumerate() {
- if pasta.id == id {
+ if comparator(pasta) {
+ let id = pasta.id;
if pastas[i].readonly || pastas[i].encrypt_server {
if password != *"" {
let res = decrypt(pastas[i].content.to_owned().as_str(), &password);
diff --git a/src/pasta.rs b/src/pasta.rs
index 6352ad31..a375651c 100644
--- a/src/pasta.rs
+++ b/src/pasta.rs
@@ -58,6 +58,7 @@ pub struct Pasta {
pub id: u64,
pub content: String,
pub file: Option,
+ pub custom_alias: Option,
pub extension: String,
pub private: bool,
pub readonly: bool,
@@ -75,11 +76,11 @@ pub struct Pasta {
impl Pasta {
pub fn id_as_animals(&self) -> String {
- if ARGS.hash_ids {
+ self.custom_alias.clone().unwrap_or(if ARGS.hash_ids {
to_hashids(self.id)
} else {
to_animal_names(self.id)
- }
+ })
}
pub fn has_file(&self) -> bool {
diff --git a/src/util/db_sqlite.rs b/src/util/db_sqlite.rs
index 5eab1f83..8fc9f934 100644
--- a/src/util/db_sqlite.rs
+++ b/src/util/db_sqlite.rs
@@ -42,7 +42,8 @@ pub fn rewrite_all_to_db(pasta_data: &[Pasta]) {
last_read INTEGER NOT NULL,
read_count INTEGER NOT NULL,
burn_after_reads INTEGER NOT NULL,
- pasta_type TEXT NOT NULL
+ pasta_type TEXT NOT NULL,
+ custom_alias TEXT
);",
params![],
)
@@ -67,8 +68,9 @@ pub fn rewrite_all_to_db(pasta_data: &[Pasta]) {
last_read,
read_count,
burn_after_reads,
- pasta_type
- ) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17)",
+ pasta_type,
+ custom_alias
+ ) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18)",
params![
pasta.id,
pasta.content,
@@ -87,6 +89,7 @@ pub fn rewrite_all_to_db(pasta_data: &[Pasta]) {
pasta.read_count,
pasta.burn_after_reads,
pasta.pasta_type,
+ pasta.custom_alias.as_ref().map_or("", |a| a.as_str()),
],
)
.expect("Failed to insert pasta.");
@@ -116,7 +119,8 @@ pub fn select_all_from_db() -> Vec {
last_read INTEGER NOT NULL,
read_count INTEGER NOT NULL,
burn_after_reads INTEGER NOT NULL,
- pasta_type TEXT NOT NULL
+ pasta_type TEXT NOT NULL,
+ custom_alias TEXT
);",
params![],
)
@@ -157,6 +161,7 @@ pub fn select_all_from_db() -> Vec {
read_count: row.get(14)?,
burn_after_reads: row.get(15)?,
pasta_type: row.get(16)?,
+ custom_alias: row.get(17)?,
})
})
.expect("Failed to select Pastas from SQLite database.");
@@ -189,7 +194,8 @@ pub fn insert(pasta: &Pasta) {
last_read INTEGER NOT NULL,
read_count INTEGER NOT NULL,
burn_after_reads INTEGER NOT NULL,
- pasta_type TEXT NOT NULL
+ pasta_type TEXT NOT NULL,
+ custom_alias TEXT
);",
params![],
)
@@ -213,8 +219,9 @@ pub fn insert(pasta: &Pasta) {
last_read,
read_count,
burn_after_reads,
- pasta_type
- ) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17)",
+ pasta_type,
+ custom_alias
+ ) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18)",
params![
pasta.id,
pasta.content,
@@ -233,6 +240,7 @@ pub fn insert(pasta: &Pasta) {
pasta.read_count,
pasta.burn_after_reads,
pasta.pasta_type,
+ pasta.custom_alias.as_ref().map_or("", |a| a.as_str()),
],
)
.expect("Failed to insert pasta.");
@@ -259,7 +267,8 @@ pub fn update(pasta: &Pasta) {
last_read = ?14,
read_count = ?15,
burn_after_reads = ?16,
- pasta_type = ?17
+ pasta_type = ?17,
+ custom_alias = ?18
WHERE id = ?1;",
params![
pasta.id,
@@ -279,6 +288,7 @@ pub fn update(pasta: &Pasta) {
pasta.read_count,
pasta.burn_after_reads,
pasta.pasta_type,
+ pasta.custom_alias.as_ref().map_or("", |a| a.as_str()),
],
)
.expect("Failed to update pasta.");
diff --git a/src/util/hashids.rs b/src/util/hashids.rs
index 0e53f313..444ad931 100644
--- a/src/util/hashids.rs
+++ b/src/util/hashids.rs
@@ -1,6 +1,10 @@
+use crate::{args::ARGS, util::hashids::to_u64 as hashid_to_u64};
+use crate::util::animalnumbers::to_u64 as animal_to_u64;
use harsh::Harsh;
use lazy_static::lazy_static;
+use crate::pasta::Pasta;
+
lazy_static! {
pub static ref HARSH: Harsh = Harsh::builder().length(6).build().unwrap();
}
@@ -16,3 +20,15 @@ pub fn to_u64(hash_id: &str) -> Result {
let id = ids.first().ok_or("No ID found in hash ID")?;
Ok(*id)
}
+
+pub fn alias_comparator(id: &str) -> Box bool> {
+ let raw_id = id.to_string();
+ let id = if ARGS.hash_ids {
+ hashid_to_u64(id).unwrap_or(0)
+ } else {
+ animal_to_u64(id).unwrap_or(0)
+ };
+ return Box::new(move |pasta|{
+ pasta.id == id || (ARGS.enable_custom_url && pasta.custom_alias.as_ref() == Some(&raw_id))
+ });
+}
diff --git a/templates/guide.html b/templates/guide.html
index 4a961a7b..14ff8234 100644
--- a/templates/guide.html
+++ b/templates/guide.html
@@ -78,5 +78,15 @@ Level 5: Secret
password never even leave your device. This option requires you to enter your
password many times when accessing your data, but is extremely safe.
+
+ Custom Url
+
+
+ Use the custom url field to set a password for your upload. This will allow
+ you to access the it with a custom url.
+
+
+ Note: Avoid using custom combinations that exist in default names (such as animal names).
+
{% include "footer.html" %}
\ No newline at end of file
diff --git a/templates/index.html b/templates/index.html
index 043f4ba4..783e33ac 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -1,5 +1,8 @@
{% include "header.html" %}