Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add custom slug feature #147

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ pub struct Args {

#[clap(long, env = "MICROBIN_HASH_IDS")]
pub hash_ids: bool,

#[clap(long, env = "MICROBIN_SLUGS")]
pub slugs: bool,
}

#[derive(Debug, Clone)]
Expand Down
31 changes: 27 additions & 4 deletions src/endpoints/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@ pub async fn create(
.finish());
}

let mut pastas = data.pastas.lock().unwrap();

let timenow: i64 = match SystemTime::now().duration_since(UNIX_EPOCH) {
Ok(n) => n.as_secs(),
Err(_) => {
Expand All @@ -49,6 +47,7 @@ pub async fn create(

let mut new_pasta = Pasta {
id: rand::thread_rng().gen::<u16>() as u64,
slug: None,
content: String::from("No Text Content"),
file: None,
extension: String::from(""),
Expand Down Expand Up @@ -118,6 +117,16 @@ pub async fn create(

continue;
}
"slug" => {
while let Some(chunk) = field.try_next().await? {
let slug = std::str::from_utf8(&chunk).unwrap().to_string();
if !slug.is_empty() {
new_pasta.slug = Some(slug);
}
}

continue;
}
"content" => {
let mut content = String::from("");
while let Some(chunk) = field.try_next().await? {
Expand Down Expand Up @@ -193,15 +202,29 @@ pub async fn create(

let id = new_pasta.id;

pastas.push(new_pasta);
let mut pastas = data.pastas.lock().unwrap();

save_to_file(&pastas);
if let Some(slug) = &new_pasta.slug {
let pasta = pastas
.iter()
.find(|p| p.slug.is_some() && p.slug.as_ref().unwrap() == slug);
if pasta.is_some() {
new_pasta.slug = Some(format!("{}-{}", slug, to_animal_names(id)));
}
}

let slug = if ARGS.hash_ids {
to_hashids(id)
} else if ARGS.slugs && new_pasta.slug.is_some() {
new_pasta.slug.clone().unwrap()
} else {
to_animal_names(id)
};

pastas.push(new_pasta);

save_to_file(&pastas);

Ok(HttpResponse::Found()
.append_header(("Location", format!("{}/pasta/{}", ARGS.public_path, slug)))
.finish())
Expand Down
88 changes: 57 additions & 31 deletions src/endpoints/edit.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::sync::MutexGuard;

use crate::args::Args;
use crate::dbio::save_to_file;
use crate::endpoints::errors::ErrorTemplate;
Expand All @@ -17,40 +19,52 @@ struct EditTemplate<'a> {
args: &'a Args,
}

#[get("/edit/{id}")]
pub async fn get_edit(data: web::Data<AppState>, id: web::Path<String>) -> HttpResponse {
#[get("/edit/{slug}")]
pub async fn get_edit(data: web::Data<AppState>, slug: web::Path<String>) -> HttpResponse {
let mut pastas = data.pastas.lock().unwrap();

let id = if ARGS.hash_ids {
hashid_to_u64(&id).unwrap_or(0)
hashid_to_u64(&slug).unwrap_or(0)
} else {
to_u64(&id.into_inner()).unwrap_or(0)
to_u64(&slug).unwrap_or(0)
};

remove_expired(&mut pastas);

for pasta in pastas.iter() {
if pasta.id == id {
if !pasta.editable {
return HttpResponse::Found()
.append_header(("Location", format!("{}/", ARGS.public_path)))
.finish();
}
let slug = slug.as_ref();
let response = |pasta: &Pasta| {
if pasta.editable {
return HttpResponse::Ok()
.content_type("text/html")
.body(EditTemplate { pasta, args: &ARGS }.render().unwrap());
}
return HttpResponse::Found()
.append_header(("Location", format!("{}/", ARGS.public_path)))
.finish();
};
for pasta in pastas.iter() {
match pasta.slug {
Some(ref s) if ARGS.slugs && slug == s => {
return response(pasta);
}
None if pasta.id == id => {
return response(pasta);
}
_ => {
continue;
}
}
}

HttpResponse::Ok()
.content_type("text/html")
.body(ErrorTemplate { args: &ARGS }.render().unwrap())
}

#[post("/edit/{id}")]
#[post("/edit/{slug}")]
pub async fn post_edit(
data: web::Data<AppState>,
id: web::Path<String>,
slug: web::Path<String>,
mut payload: Multipart,
) -> Result<HttpResponse, Error> {
if ARGS.readonly {
Expand All @@ -60,15 +74,11 @@ pub async fn post_edit(
}

let id = if ARGS.hash_ids {
hashid_to_u64(&id).unwrap_or(0)
hashid_to_u64(&slug).unwrap_or(0)
} else {
to_u64(&id.into_inner()).unwrap_or(0)
to_u64(&slug).unwrap_or(0)
};

let mut pastas = data.pastas.lock().unwrap();

remove_expired(&mut pastas);

let mut new_content = String::from("");

while let Some(mut field) = payload.try_next().await? {
Expand All @@ -79,21 +89,37 @@ pub async fn post_edit(
}
}

for (i, pasta) in pastas.iter().enumerate() {
if pasta.id == id {
if pasta.editable {
pastas[i].content.replace_range(.., &new_content);
save_to_file(&pastas);
let mut pastas = data.pastas.lock().unwrap();
remove_expired(&mut pastas);
let slug = slug.as_ref();
let response = |pastas: &mut MutexGuard<Vec<Pasta>>, i: usize| {
pastas[i].content.replace_range(.., &new_content);
save_to_file(pastas);
return Ok(HttpResponse::Found()
.append_header((
"Location",
format!("{}/pasta/{}", ARGS.public_path, pastas[i].id_as_animals()),
))
.finish());
};

return Ok(HttpResponse::Found()
.append_header((
"Location",
format!("{}/pasta/{}", ARGS.public_path, pastas[i].id_as_animals()),
))
.finish());
} else {
for (i, pasta) in pastas.iter().enumerate() {
match pasta.slug {
Some(ref s) if ARGS.slugs && slug == s && pasta.editable => {
return response(&mut pastas, i);
}
Some(ref s) if ARGS.slugs && slug == s && !pasta.editable => {
break;
}
None if pasta.id == id && pasta.editable => {
return response(&mut pastas, i);
}
None if pasta.id == id && !pasta.editable => {
break;
}
_ => {
continue;
}
}
}

Expand Down
59 changes: 3 additions & 56 deletions src/endpoints/pasta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ use crate::args::{Args, ARGS};
use crate::dbio::save_to_file;
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::misc::remove_expired;
use crate::AppState;

Expand All @@ -23,25 +21,11 @@ pub async fn getpasta(data: web::Data<AppState>, id: web::Path<String>) -> HttpR
// 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)
};

// remove expired pastas (including this one if needed)
remove_expired(&mut pastas);

// find the index of the pasta in the collection based on u64 id
let mut index: usize = 0;
let mut found: bool = false;
for (i, pasta) in pastas.iter().enumerate() {
if pasta.id == id {
index = i;
found = true;
break;
}
}
let (index, found) = Pasta::get_index(id.as_ref(), &mut pastas);

if found {
// increment read count
Expand Down Expand Up @@ -87,26 +71,7 @@ pub async fn redirecturl(data: web::Data<AppState>, id: web::Path<String>) -> Ht
// 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)
};

// remove expired pastas (including this one if needed)
remove_expired(&mut pastas);

// find the index of the pasta in the collection based on u64 id
let mut index: usize = 0;
let mut found: bool = false;

for (i, pasta) in pastas.iter().enumerate() {
if pasta.id == id {
index = i;
found = true;
break;
}
}
let (index, found) = Pasta::get_index(id.as_ref(), &mut pastas);

if found {
// increment read count
Expand Down Expand Up @@ -154,25 +119,7 @@ pub async fn getrawpasta(data: web::Data<AppState>, id: web::Path<String>) -> St
// 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)
};

// remove expired pastas (including this one if needed)
remove_expired(&mut pastas);

// find the index of the pasta in the collection based on u64 id
let mut index: usize = 0;
let mut found: bool = false;
for (i, pasta) in pastas.iter().enumerate() {
if pasta.id == id {
index = i;
found = true;
break;
}
}
let (index, found) = Pasta::get_index(id.as_ref(), &mut pastas);

if found {
// increment read count
Expand Down
24 changes: 2 additions & 22 deletions src/endpoints/qr.rs
Original file line number Diff line number Diff line change
@@ -1,9 +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::misc::{self, remove_expired};
use crate::util::misc;
use crate::AppState;
use actix_web::{get, web, HttpResponse};
use askama::Template;
Expand All @@ -21,25 +19,7 @@ pub async fn getqr(data: web::Data<AppState>, id: web::Path<String>) -> 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)
};

// remove expired pastas (including this one if needed)
remove_expired(&mut pastas);

// find the index of the pasta in the collection based on u64 id
let mut index: usize = 0;
let mut found: bool = false;
for (i, pasta) in pastas.iter().enumerate() {
if pasta.id == u64_id {
index = i;
found = true;
break;
}
}
let (index, found) = Pasta::get_index(&id, &mut pastas);

if found {
// generate the QR code as an SVG - if its a file or text pastas, this will point to the /pasta endpoint, otherwise to the /url endpoint, essentially directly taking the user to the url stored in the pasta
Expand Down