Compare commits

..

3 Commits

Author SHA1 Message Date
Dániel Szabó
907b595339
Update README.MD 2022-06-09 22:58:48 +01:00
Daniel Szabo
16de177083 Merge branch 'master' of https://github.com/szabodanika/microbin 2022-06-09 22:48:25 +01:00
Daniel Szabo
dda65a53e1 - changed how static web resources are served
- fixed sizing consistency for pasta setting fields on index.html
- added new logo
- updated README.MD
2022-06-09 22:48:15 +01:00
25 changed files with 146 additions and 260 deletions

13
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,13 @@
# These are supported funding model platforms
github: szabodanika
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: dani_sz
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

View File

@ -1,4 +1,4 @@
name: Rust name: Build
on: on:
push: push:

View File

@ -1,6 +1,6 @@
[package] [package]
name="microbin" name="microbin"
version="1.1.0" version="1.0.2"
edition="2021" edition="2021"
authors = ["Daniel Szabo <daniel.szabo99@outlook.com>"] authors = ["Daniel Szabo <daniel.szabo99@outlook.com>"]
license = "BSD-3-Clause" license = "BSD-3-Clause"
@ -18,13 +18,12 @@ actix-web="4"
actix-files="0.6.0" actix-files="0.6.0"
serde={ version = "1.0", features = ["derive"] } serde={ version = "1.0", features = ["derive"] }
serde_json = "1.0.80" serde_json = "1.0.80"
bytesize = { version = "1.1", features = ["serde"] }
askama="0.10" askama="0.10"
askama-filters={ version = "0.1.3", features = ["chrono"] } askama-filters={ version = "0.1.3", features = ["chrono"] }
chrono="0.4.19" chrono="0.4.19"
rand="0.8.5" rand="0.8.5"
linkify="0.8.1" linkify="0.8.1"
clap={ version = "3.1.12", features = ["derive", "env"] } clap={ version = "3.1.12", features = ["derive"] }
actix-multipart = "0.4.0" actix-multipart = "0.4.0"
futures = "0.3" futures = "0.3"
sanitize-filename = "0.3.0" sanitize-filename = "0.3.0"

View File

@ -19,5 +19,8 @@ WORKDIR /usr/local/bin
# copy built exacutable # copy built exacutable
COPY --from=builder /usr/src/microbin/target/release/microbin /usr/local/bin/microbin COPY --from=builder /usr/src/microbin/target/release/microbin /usr/local/bin/microbin
# copy /static folder containing the stylesheets
COPY --from=builder /usr/src/microbin/static /usr/local/bin/static
# run the binary # run the binary
CMD ["microbin"] CMD ["microbin"]

View File

@ -1,24 +1,17 @@
![Screenshot](git/index.png) # <img src="git/logo.png" alt="Logo" width="35" /> MicroBin
# MicroBin
![Build](https://github.com/szabodanika/microbin/actions/workflows/rust.yml/badge.svg) ![Build](https://github.com/szabodanika/microbin/actions/workflows/rust.yml/badge.svg)
![crates.io](https://img.shields.io/crates/v/microbin.svg) ![crates.io](https://img.shields.io/crates/v/microbin.svg)
MicroBin is a super tiny, feature rich, configurable, self-contained and self-hosted paste bin web application. It is very easy to set up and use, and will only require a few megabytes of memory and disk storage. It takes only a couple minutes to set it up, why not give it a try now? MicroBin is a super tiny, feature rich, configurable, self-contained and self-hosted paste bin web application. It is very easy to set up and use, and will only require a few megabytes of memory and disk storage. It takes only a couple minutes to set it up, why not give it a try now?
[![Deploy to Render](https://render.com/images/deploy-to-render-button.svg)](https://render.com/deploy?repo=https://github.com/szabodanika/microbin) [![Deploy to Render](https://render.com/images/deploy-to-render-button.svg)](https://render.com/deploy?repo=https://github.com/szabodanika/microbin)
Or install from Cargo: Or install from Cargo:
`cargo install microbin`, and run with your custom configuration: `microbin --port 8080 --highlightsyntax --editable`.
`cargo install microbin` ![Screenshot](git/index.png)
And run with your custom configuration:
`microbin --port 8080 --highlightsyntax --editable`
### Features ### Features
- Is very small - Is very small
@ -105,14 +98,11 @@ Remember, MicroBin will create your database and file storage wherever you execu
`cd ~/microbin/` `cd ~/microbin/`
`microbin --highlightsyntax --editable` `microbin --port 8080 --highlightsyntax --editable`
### From AUR (for any Arch-based distro)
Install `microbin` package from AUR and start/enable microbin systemd service. Systemd will start server on 127.0.0.1:8080 with almost all features enabled, but this can be changed in `/etc/microbin.conf`.
### Building MicroBin ### Building MicroBin
Simply clone the repository, build it with `cargo build --release` and run the `microbin` executable in the created `target/release/` directory. It will start listening on 0.0.0.0:8080. You can change the port or bind address with CL arguments `-p (--port)` or `-b (--bind)` respectively . For other arguments see [the Wiki](https://github.com/szabodanika/microbin/wiki). Simply clone the repository, build it with `cargo build --release` and run the `microbin` executable in the created `target/release/` directory. It will start on port 8080. You can change the port with `-p` or `--port` CL arguments. For other arguments see [the Wiki](https://github.com/szabodanika/microbin/wiki).
``` ```
git clone https://github.com/szabodanika/microbin.git git clone https://github.com/szabodanika/microbin.git
@ -121,49 +111,6 @@ cargo build --release
./target/release/microbin -p 80 ./target/release/microbin -p 80
``` ```
### Building Docker Image
MicroBin includes a Dockerfile. To build the image, follow these steps:
```
git clone https://github.com/szabodanika/microbin.git
cd microbin
docker build -t microbin-docker .
```
Then, for `docker compose` you can repurpose the following example in your compose file:
```
services:
paste:
image: microbin-docker
restart: always
ports:
- "80:8080"
volumes:
- ./microbin-data:/usr/local/bin/pasta_data
```
To pass command line arguments you must edit the Dockerfile and change the CMD line. In this example we add the syntax highlighting option and enable private pastas:
```
CMD ["microbin", "--highlightsyntax", "--private"]
```
You then need to rebuild the image and recreate your container.
**Note:** If you are getting the following error about domain name resolution:
```
warning: spurious network error (2 tries remaining): failed to resolve address for github.com: Temporary failure in name resolution; class=Net (12)
warning: spurious network error (1 tries remaining): failed to resolve address for github.com: Temporary failure in name resolution; class=Net (12)
```
You might need to run `docker build` with the `--network` option:
```
docker build --network host -t microbin-docker .
```
### MicroBin as a service ### MicroBin as a service
To install it as a service on your Linux machine, create a file called `/etc/systemd/system/microbin.service`, paste this into it with the `[username]` and `[path to installation directory]` replaced with the actual values. If you installed MicroBin from cargo, your executable will be in your cargo directory, e.g. `/Users/daniel/.cargo/bin/microbin`. To install it as a service on your Linux machine, create a file called `/etc/systemd/system/microbin.service`, paste this into it with the `[username]` and `[path to installation directory]` replaced with the actual values. If you installed MicroBin from cargo, your executable will be in your cargo directory, e.g. `/Users/daniel/.cargo/bin/microbin`.
@ -293,13 +240,6 @@ Default value: 8080
Sets the port for the server will be listening on. Sets the port for the server will be listening on.
### -b, --bind [ADDRESS]
Default value: 0.0.0.0
Sets the bind address for the server will be listening on. Both ipv4 and ipv6 are supported.
### --private ### --private
Enables private pastas. Adds a new checkbox to make your pasta private, which then won't show up on the pastalist page. With the URL to your pasta, it will still be accessible. Enables private pastas. Adds a new checkbox to make your pasta private, which then won't show up on the pastalist page. With the URL to your pasta, it will still be accessible.

View File

@ -1,19 +0,0 @@
# Security Policy
## Supported Versions
Use this section to tell people about which versions of your project are
currently being supported with security updates.
| Version | Supported |
| ------- | ------------------ |
| 1.1.* | :white_check_mark: |
| < 1.1.0 | :x: |
## Reporting a Vulnerability
Security vulnerabilities can be reported directly to
the developer/maintainer at d@szab.eu.
Sensitive information may be GPG encrypted with my public key available at
https://szab.eu/assets/files/daniel-szabo-pub.asc.

BIN
git/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

View File

@ -1,4 +1,3 @@
use std::net::IpAddr;
use clap::Parser; use clap::Parser;
use lazy_static::lazy_static; use lazy_static::lazy_static;
@ -9,54 +8,51 @@ lazy_static! {
#[derive(Parser, Debug, Clone)] #[derive(Parser, Debug, Clone)]
#[clap(author, version, about, long_about = None)] #[clap(author, version, about, long_about = None)]
pub struct Args { pub struct Args {
#[clap(long, env="MICROBIN_AUTH_USERNAME")] #[clap(long)]
pub auth_username: Option<String>, pub auth_username: Option<String>,
#[clap(long, env="MICROBIN_AUTH_PASSWORD")] #[clap(long)]
pub auth_password: Option<String>, pub auth_password: Option<String>,
#[clap(long, env="MICROBIN_EDITABLE")] #[clap(long)]
pub editable: bool, pub editable: bool,
#[clap(long, env="MICROBIN_FOOTER_TEXT")] #[clap(long)]
pub footer_text: Option<String>, pub footer_text: Option<String>,
#[clap(long, env="MICROBIN_HIDE_FOOTER")] #[clap(long)]
pub hide_footer: bool, pub hide_footer: bool,
#[clap(long, env="MICROBIN_HIDE_HEADER")] #[clap(long)]
pub hide_header: bool, pub hide_header: bool,
#[clap(long, env="MICROBIN_HIDE_LOGO")] #[clap(long)]
pub hide_logo: bool, pub hide_logo: bool,
#[clap(long, env="MICROBIN_NO_LISTING")] #[clap(long)]
pub no_listing: bool, pub no_listing: bool,
#[clap(long, env="MICROBIN_HIGHLIGHTSYNTAX")] #[clap(long)]
pub highlightsyntax: bool, pub highlightsyntax: bool,
#[clap(short, long, env="MICROBIN_PORT", default_value_t = 8080)] #[clap(short, long, default_value_t = 8080)]
pub port: u16, pub port: u32,
#[clap(short, long, env="MICROBIN_BIND", default_value_t = IpAddr::from([0, 0, 0, 0]))] #[clap(long)]
pub bind: IpAddr,
#[clap(long, env="MICROBIN_PRIVATE")]
pub private: bool, pub private: bool,
#[clap(long, env="MICROBIN_PURE_HTML")] #[clap(long)]
pub pure_html: bool, pub pure_html: bool,
#[clap(long, env="MICROBIN_READONLY")] #[clap(long)]
pub readonly: bool, pub readonly: bool,
#[clap(long, env="MICROBIN_TITLE")] #[clap(long)]
pub title: Option<String>, pub title: Option<String>,
#[clap(short, long, env="MICROBIN_THREADS", default_value_t = 1)] #[clap(short, long, default_value_t = 1)]
pub threads: u8, pub threads: u8,
#[clap(long, env="MICROBIN_WIDE")] #[clap(long)]
pub wide: bool, pub wide: bool,
} }

View File

@ -1,14 +1,11 @@
use crate::dbio::save_to_file; use crate::dbio::save_to_file;
use crate::pasta::PastaFile;
use crate::util::animalnumbers::to_animal_names; use crate::util::animalnumbers::to_animal_names;
use crate::util::misc::is_valid_url; use crate::util::misc::is_valid_url;
use crate::{AppState, Pasta, ARGS}; use crate::{AppState, Pasta, ARGS};
use actix_multipart::Multipart; use actix_multipart::Multipart;
use actix_web::{get, web, Error, HttpResponse, Responder}; use actix_web::{get, web, Error, HttpResponse, Responder};
use askama::Template; use askama::Template;
use bytesize::ByteSize;
use futures::TryStreamExt; use futures::TryStreamExt;
use log::warn;
use rand::Rng; use rand::Rng;
use std::io::Write; use std::io::Write;
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
@ -49,7 +46,7 @@ pub async fn create(
let mut new_pasta = Pasta { let mut new_pasta = Pasta {
id: rand::thread_rng().gen::<u16>() as u64, id: rand::thread_rng().gen::<u16>() as u64,
content: String::from("No Text Content"), content: String::from("No Text Content"),
file: None, file: String::from("no-file"),
extension: String::from(""), extension: String::from(""),
private: false, private: false,
editable: false, editable: false,
@ -106,41 +103,27 @@ pub async fn create(
continue; continue;
} }
"file" => { "file" => {
let path = field.content_disposition().get_filename(); let content_disposition = field.content_disposition();
let path = match path { let filename = match content_disposition.get_filename() {
Some("") => continue, Some("") => continue,
Some(p) => p, Some(filename) => filename.replace(' ', "_").to_string(),
None => continue, None => continue,
}; };
let mut file = match PastaFile::from_unsanitized(&path) { std::fs::create_dir_all(format!("./pasta_data/{}", &new_pasta.id_as_animals()))
Ok(f) => f,
Err(e) => {
warn!("Unsafe file name: {e:?}");
continue;
}
};
std::fs::create_dir_all(format!("./pasta_data/public/{}", &new_pasta.id_as_animals()))
.unwrap(); .unwrap();
let filepath = format!( let filepath = format!("./pasta_data/{}/{}", &new_pasta.id_as_animals(), &filename);
"./pasta_data/public/{}/{}",
&new_pasta.id_as_animals(), new_pasta.file = filename;
&file.name()
);
let mut f = web::block(|| std::fs::File::create(filepath)).await??; let mut f = web::block(|| std::fs::File::create(filepath)).await??;
let mut size = 0;
while let Some(chunk) = field.try_next().await? { while let Some(chunk) = field.try_next().await? {
size += chunk.len();
f = web::block(move || f.write_all(&chunk).map(|_| f)).await??; f = web::block(move || f.write_all(&chunk).map(|_| f)).await??;
} }
file.size = ByteSize::b(size as u64);
new_pasta.file = Some(file);
new_pasta.pasta_type = String::from("text"); new_pasta.pasta_type = String::from("text");
} }
_ => {} _ => {}

View File

@ -2,12 +2,10 @@ use actix_web::{get, web, HttpResponse};
use crate::args::ARGS; use crate::args::ARGS;
use crate::endpoints::errors::ErrorTemplate; use crate::endpoints::errors::ErrorTemplate;
use crate::pasta::PastaFile;
use crate::util::animalnumbers::to_u64; use crate::util::animalnumbers::to_u64;
use crate::util::misc::remove_expired; use crate::util::misc::remove_expired;
use crate::AppState; use crate::AppState;
use askama::Template; use askama::Template;
use std::fs;
#[get("/remove/{id}")] #[get("/remove/{id}")]
pub async fn remove(data: web::Data<AppState>, id: web::Path<String>) -> HttpResponse { pub async fn remove(data: web::Data<AppState>, id: web::Path<String>) -> HttpResponse {
@ -21,22 +19,10 @@ pub async fn remove(data: web::Data<AppState>, id: web::Path<String>) -> HttpRes
let id = to_u64(&*id.into_inner()).unwrap_or(0); let id = to_u64(&*id.into_inner()).unwrap_or(0);
remove_expired(&mut pastas);
for (i, pasta) in pastas.iter().enumerate() { for (i, pasta) in pastas.iter().enumerate() {
if pasta.id == id { if pasta.id == id {
// remove the file itself
if let Some(PastaFile { name, .. }) = &pasta.file {
if fs::remove_file(format!("./pasta_data/public/{}/{}", pasta.id_as_animals(), name))
.is_err()
{
log::error!("Failed to delete file {}!", name)
}
// and remove the containing directory
if fs::remove_dir(format!("./pasta_data/public/{}/", pasta.id_as_animals())).is_err() {
log::error!("Failed to delete directory {}!", name)
}
}
// remove it from in-memory pasta list
pastas.remove(i); pastas.remove(i);
return HttpResponse::Found() return HttpResponse::Found()
.append_header(("Location", "/pastalist")) .append_header(("Location", "/pastalist"))
@ -44,8 +30,6 @@ pub async fn remove(data: web::Data<AppState>, id: web::Path<String>) -> HttpRes
} }
} }
remove_expired(&mut pastas);
HttpResponse::Ok() HttpResponse::Ok()
.content_type("text/html") .content_type("text/html")
.body(ErrorTemplate { args: &ARGS }.render().unwrap()) .body(ErrorTemplate { args: &ARGS }.render().unwrap())

View File

@ -1,23 +1,37 @@
use actix_web::dev::JsonBody::Body;
use actix_web::error::UrlencodedError::ContentType;
use actix_web::web::Path;
use actix_web::{get, web, HttpResponse}; use actix_web::{get, web, HttpResponse};
use askama::Template; use askama::Template;
use std::io::ErrorKind::NotFound;
use std::marker::PhantomData; use std::marker::PhantomData;
#[derive(Template)]
#[template(path = "water.css", escape = "none")]
struct WaterCSS<'a> {
_marker: PhantomData<&'a ()>,
}
#[get("/static/{resource}")] #[get("/static/{resource}")]
pub async fn static_resources(resource_id: web::Path<String>) -> HttpResponse { pub async fn static_resources(resource_id: web::Path<String>) -> HttpResponse {
match resource_id.into_inner().as_str() { return match resource_id.into_inner().as_str() {
"water.css" => HttpResponse::Ok().content_type("text/css").body( "water.css" => HttpResponse::Ok()
WaterCSS { .content_type("text/css")
_marker: Default::default(), .body(include_bytes!("../../templates/static/water.css").to_vec()),
}
.render() "icon.ico" => HttpResponse::Ok()
.unwrap(), .content_type("image/x-icon")
), .body(include_bytes!("../../templates/static/icon.ico").to_vec()),
"icon-16x16.png" => HttpResponse::Ok()
.content_type("image/x-icon")
.body(include_bytes!("../../templates/static/icon-16x16.png").to_vec()),
"icon-32x32.png" => HttpResponse::Ok()
.content_type("image/x-icon")
.body(include_bytes!("../../templates/static/icon-32x32.png").to_vec()),
"icon-192x192.png" => HttpResponse::Ok()
.content_type("image/x-icon")
.body(include_bytes!("../../templates/static/icon-192x192.png").to_vec()),
"icon-512x512.png" => HttpResponse::Ok()
.content_type("image/x-icon")
.body(include_bytes!("../../templates/static/icon-512x512.png").to_vec()),
"apple-touch-icon.png" => HttpResponse::Ok()
.content_type("image/x-icon")
.body(include_bytes!("../../templates/static/apple-touch-icon.png").to_vec()),
_ => HttpResponse::NotFound().content_type("text/html").finish(), _ => HttpResponse::NotFound().content_type("text/html").finish(),
} };
} }

View File

@ -58,16 +58,15 @@ async fn main() -> std::io::Result<()> {
.init(); .init();
log::info!( log::info!(
"MicroBin starting on http://{}:{}", "MicroBin starting on http://127.0.0.1:{}",
ARGS.bind.to_string(),
ARGS.port.to_string() ARGS.port.to_string()
); );
match fs::create_dir_all("./pasta_data/public") { match fs::create_dir_all("./pasta_data") {
Ok(dir) => dir, Ok(dir) => dir,
Err(error) => { Err(error) => {
log::error!("Couldn't create data directory ./pasta_data/public/: {:?}", error); log::error!("Couldn't create data directory ./pasta_data: {:?}", error);
panic!("Couldn't create data directory ./pasta_data/public/: {:?}", error); panic!("Couldn't create data directory ./pasta_data: {:?}", error);
} }
}; };
@ -87,7 +86,7 @@ async fn main() -> std::io::Result<()> {
.service(edit::get_edit) .service(edit::get_edit)
.service(edit::post_edit) .service(edit::post_edit)
.service(static_resources::static_resources) .service(static_resources::static_resources)
.service(actix_files::Files::new("/file", "./pasta_data/public/")) .service(actix_files::Files::new("/file", "./pasta_data"))
.service(web::resource("/upload").route(web::post().to(create::create))) .service(web::resource("/upload").route(web::post().to(create::create)))
.default_service(web::route().to(errors::not_found)) .default_service(web::route().to(errors::not_found))
.wrap(middleware::Logger::default()) .wrap(middleware::Logger::default())
@ -98,7 +97,7 @@ async fn main() -> std::io::Result<()> {
HttpAuthentication::basic(util::auth::auth_validator), HttpAuthentication::basic(util::auth::auth_validator),
)) ))
}) })
.bind((ARGS.bind, ARGS.port))? .bind(format!("0.0.0.0:{}", ARGS.port.to_string()))?
.workers(ARGS.threads as usize) .workers(ARGS.threads as usize)
.run() .run()
.await .await

View File

@ -1,39 +1,16 @@
use bytesize::ByteSize;
use chrono::{Datelike, Local, TimeZone, Timelike};
use serde::{Deserialize, Serialize};
use std::fmt; use std::fmt;
use std::path::Path;
use chrono::{DateTime, Datelike, NaiveDateTime, Timelike, Utc};
use serde::{Deserialize, Serialize};
use crate::util::animalnumbers::to_animal_names; use crate::util::animalnumbers::to_animal_names;
use crate::util::syntaxhighlighter::html_highlight; use crate::util::syntaxhighlighter::html_highlight;
#[derive(Serialize, Deserialize, PartialEq, Eq)]
pub struct PastaFile {
pub name: String,
pub size: ByteSize,
}
impl PastaFile {
pub fn from_unsanitized(path: &str) -> Result<Self, &'static str> {
let path = Path::new(path);
let name = path.file_name().ok_or("Path did not contain a file name")?;
let name = name.to_string_lossy().replace(' ', "_");
Ok(Self {
name,
size: ByteSize::b(0),
})
}
pub fn name(&self) -> &str {
&self.name
}
}
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct Pasta { pub struct Pasta {
pub id: u64, pub id: u64,
pub content: String, pub content: String,
pub file: Option<PastaFile>, pub file: String,
pub extension: String, pub extension: String,
pub private: bool, pub private: bool,
pub editable: bool, pub editable: bool,
@ -48,9 +25,9 @@ impl Pasta {
} }
pub fn created_as_string(&self) -> String { pub fn created_as_string(&self) -> String {
let date = Local.timestamp(self.created, 0); let date = DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(self.created, 0), Utc);
format!( format!(
"{:02}-{:02} {:02}:{:02}", "{:02}-{:02} {}:{}",
date.month(), date.month(),
date.day(), date.day(),
date.hour(), date.hour(),
@ -62,9 +39,10 @@ impl Pasta {
if self.expiration == 0 { if self.expiration == 0 {
String::from("Never") String::from("Never")
} else { } else {
let date = Local.timestamp(self.expiration, 0); let date =
DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(self.expiration, 0), Utc);
format!( format!(
"{:02}-{:02} {:02}:{:02}", "{:02}-{:02} {}:{}",
date.month(), date.month(),
date.day(), date.day(),
date.hour(), date.hour(),

View File

@ -22,22 +22,20 @@ pub fn remove_expired(pastas: &mut Vec<Pasta>) {
true true
} else { } else {
// remove the file itself // remove the file itself
if let Some(file) = &p.file { match fs::remove_file(format!("./pasta_data/{}/{}", p.id_as_animals(), p.file)) {
if fs::remove_file(format!( Ok(_) => {}
"./pasta_data/public/{}/{}", Err(_) => {
p.id_as_animals(), log::error!("Failed to delete file {}!", p.file)
file.name()
))
.is_err()
{
log::error!("Failed to delete file {}!", file.name())
}
// and remove the containing directory
if fs::remove_dir(format!("./pasta_data/public/{}/", p.id_as_animals())).is_err() {
log::error!("Failed to delete directory {}!", file.name())
} }
} }
// and remove the containing directory
match fs::remove_dir(format!("./pasta_data/{}/", p.id_as_animals())) {
Ok(_) => {}
Err(_) => {
log::error!("Failed to delete directory {}!", p.file)
}
}
// remove
false false
} }
}); });

View File

@ -1,12 +1,19 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
{% if args.title.as_ref().is_none() %} {% if args.footer_text.as_ref().is_none() %}
<title>MicroBin</title> <title>MicroBin</title>
{%- else %} {%- else %}
<title>{{ args.title.as_ref().unwrap() }}</title> <title>{{ args.title.as_ref().unwrap() }}</title>
{%- endif %} {%- endif %}
<link rel="icon" type="image/png" href="/static/icon.ico">
<link rel="icon" type="image/png" href="/static/icon-16x16.png" sizes="16x16">
<link rel="icon" type="image/png" href="/static/icon-32x32.png" sizes="32x32">
<link rel="icon" type="image/png" href="/static/icon-192x192.png" sizes="192x192">
<link rel="icon" type="image/png" href="/static/icon-512x512.png">
<link rel="apple-touch-icon" href="/static/apple-touch-icon.png">
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
{% if !args.pure_html %} {% if !args.pure_html %}
@ -39,11 +46,12 @@
<b style="margin-right: 0.5rem"> <b style="margin-right: 0.5rem">
{% if !args.hide_logo %} {% if !args.hide_logo %}
<i><span style="font-size:2.2rem; margin-right:1rem">μ</span></i> <!-- <i><span style="font-size:2.2rem; margin-right:1rem">μ</span></i>-->
<img width="25" style="margin-bottom: -0.4rem; margin-right: 0.4rem" src="static/icon-192x192.png"/>
{%- endif %} {%- endif %}
{% if args.title.as_ref().is_none() %} {% if args.footer_text.as_ref().is_none() %}
MicroBin <span>MicroBin</span>
{%- else %} {%- else %}
{{ args.title.as_ref().unwrap() }} {{ args.title.as_ref().unwrap() }}
{%- endif %} {%- endif %}

View File

@ -4,10 +4,10 @@
<div style="display: grid; <div style="display: grid;
grid-gap: 4px; grid-gap: 4px;
grid-template-columns: repeat(auto-fit, 234px); grid-template-columns: repeat(auto-fit, 234px);
grid-template-rows: repeat(1, 100px); "> grid-template-rows: repeat(1, 78px); ">
<div> <div>
<label for="expiration">Expiration</label><br> <label for="expiration">Expiration</label><br>
<select style="width: 100%;" name="expiration" id="expiration"> <select style="width: 100%;margin-top: 0" name="expiration" id="expiration">
<optgroup label="Expire"> <optgroup label="Expire">
<option value="1min">1 minute</option> <option value="1min">1 minute</option>
<option value="10min">10 minutes</option> <option value="10min">10 minutes</option>
@ -21,7 +21,7 @@
{% if args.highlightsyntax %} {% if args.highlightsyntax %}
<div> <div>
<label for="syntax-highlight">Syntax Highlighting</label><br> <label for="syntax-highlight">Syntax Highlighting</label><br>
<select style="width: 100%;" name="syntax-highlight" id="syntax-highlight"> <select style="width: 100%; ;margin-top: 0" name="syntax-highlight" id="syntax-highlight">
<option value="none">None</option> <option value="none">None</option>
<optgroup label="Source Code"> <optgroup label="Source Code">
<option value="sh">Bash Shell</option> <option value="sh">Bash Shell</option>
@ -58,13 +58,12 @@
{%- else %} {%- else %}
<input type="hidden" name="syntax-highlight" value="none"> <input type="hidden" name="syntax-highlight" value="none">
{%- endif %} {%- endif %}
<div> <div>
<label>File attachment</label> <label>File attachment</label><br>
<br> <input style="width: 100%; height: 21px" type="file" id="file" name="file">
<input style="width: 100%;" type="file" id="file" name="file">
</div> </div>
</div> </div>
<br>
<label>Content</label> <label>Content</label>
<br> <br>
<textarea style="width: 100%; min-height: 100px" name="content" autofocus></textarea> <textarea style="width: 100%; min-height: 100px" name="content" autofocus></textarea>

View File

@ -1,28 +1,19 @@
{% include "header.html" %} {% include "header.html" %}
<div style="float: left"> <a style="margin-right: 0.5rem" href="/raw/{{pasta.id_as_animals()}}">Raw Text Content</a>
<a style="margin-right: 0.5rem" href="/raw/{{pasta.id_as_animals()}}">Raw Text Content</a> {% if pasta.file != "no-file" %}
{% if pasta.file.is_some() %} <a style="margin-right: 0.5rem; margin-left: 0.5rem" href="/file/{{pasta.id_as_animals()}}/{{pasta.file}}">Attached file
<a style="margin-right: 0.5rem; margin-left: 0.5rem" '{{pasta.file}}'</a>
href="/file/{{pasta.id_as_animals()}}/{{pasta.file.as_ref().unwrap().name()}}"> {%- endif %}
Attached file'{{pasta.file.as_ref().unwrap().name()}}' [{{pasta.file.as_ref().unwrap().size}}] {% if pasta.editable %}
</a> <a style="margin-right: 0.5rem; margin-left: 0.5rem" href="/edit/{{pasta.id_as_animals()}}">Edit</a>
{%- endif %} {%- endif %}
{% if pasta.editable %} <a style="margin-right: 0.5rem; margin-left: 0.5rem" href="/remove/{{pasta.id_as_animals()}}">Remove</a>
<a style="margin-right: 0.5rem; margin-left: 0.5rem" href="/edit/{{pasta.id_as_animals()}}">Edit</a> {% if args.highlightsyntax %}
{%- endif %} <pre><code>{{pasta.content_syntax_highlighted()}}</code></pre>
<a style="margin-right: 0.5rem; margin-left: 0.5rem" href="/remove/{{pasta.id_as_animals()}}">Remove</a> {%- else %}
</div> <pre><code>{{pasta.content_not_highlighted()}}</code></pre>
<div style="float: right"> {%- endif %}
<a href="/pasta/{{pasta.id_as_animals()}}"><i>{{pasta.id_as_animals()}}</i></a>
</div>
<br>
<div style="clear: both;">
{% if args.highlightsyntax %}
<pre><code>{{pasta.content_syntax_highlighted()}}</code></pre>
{%- else %}
<pre><code>{{pasta.content_not_highlighted()}}</code></pre>
{%- endif %}
</div>
<style> <style>
code-line { code-line {
counter-increment: listing; counter-increment: listing;

View File

@ -48,8 +48,8 @@
</td> </td>
<td> <td>
<a style="margin-right:1rem" href="/raw/{{pasta.id_as_animals()}}">Raw</a> <a style="margin-right:1rem" href="/raw/{{pasta.id_as_animals()}}">Raw</a>
{% if pasta.file.is_some() %} {% if pasta.file != "no-file" %}
<a style="margin-right:1rem" href="/file/{{pasta.id_as_animals()}}/{{pasta.file.as_ref().unwrap().name()}}">File</a> <a style="margin-right:1rem" href="/file/{{pasta.id_as_animals()}}/{{pasta.file}}">File</a>
{%- endif %} {%- endif %}
{% if pasta.editable %} {% if pasta.editable %}
<a style="margin-right:1rem" href="/edit/{{pasta.id_as_animals()}}">Edit</a> <a style="margin-right:1rem" href="/edit/{{pasta.id_as_animals()}}">Edit</a>

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 875 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

BIN
templates/static/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB