Compare commits
3 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
907b595339 | ||
![]() |
16de177083 | ||
![]() |
dda65a53e1 |
13
.github/FUNDING.yml
vendored
Normal 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']
|
2
.github/workflows/rust.yml
vendored
@ -1,4 +1,4 @@
|
||||
name: Rust
|
||||
name: Build
|
||||
|
||||
on:
|
||||
push:
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name="microbin"
|
||||
version="1.1.0"
|
||||
version="1.0.2"
|
||||
edition="2021"
|
||||
authors = ["Daniel Szabo <daniel.szabo99@outlook.com>"]
|
||||
license = "BSD-3-Clause"
|
||||
@ -18,13 +18,12 @@ actix-web="4"
|
||||
actix-files="0.6.0"
|
||||
serde={ version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0.80"
|
||||
bytesize = { version = "1.1", features = ["serde"] }
|
||||
askama="0.10"
|
||||
askama-filters={ version = "0.1.3", features = ["chrono"] }
|
||||
chrono="0.4.19"
|
||||
rand="0.8.5"
|
||||
linkify="0.8.1"
|
||||
clap={ version = "3.1.12", features = ["derive", "env"] }
|
||||
clap={ version = "3.1.12", features = ["derive"] }
|
||||
actix-multipart = "0.4.0"
|
||||
futures = "0.3"
|
||||
sanitize-filename = "0.3.0"
|
||||
|
@ -19,5 +19,8 @@ WORKDIR /usr/local/bin
|
||||
# copy built exacutable
|
||||
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
|
||||
CMD ["microbin"]
|
||||
CMD ["microbin"]
|
72
README.MD
@ -1,24 +1,17 @@
|
||||
|
||||

|
||||
|
||||
# MicroBin
|
||||
|
||||
# <img src="git/logo.png" alt="Logo" width="35" /> MicroBin
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
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?
|
||||
|
||||
[](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`
|
||||
|
||||
And run with your custom configuration:
|
||||
|
||||
`microbin --port 8080 --highlightsyntax --editable`
|
||||

|
||||
|
||||
### Features
|
||||
- Is very small
|
||||
@ -105,14 +98,11 @@ Remember, MicroBin will create your database and file storage wherever you execu
|
||||
|
||||
`cd ~/microbin/`
|
||||
|
||||
`microbin --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`.
|
||||
`microbin --port 8080 --highlightsyntax --editable`
|
||||
|
||||
### 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
|
||||
@ -121,49 +111,6 @@ cargo build --release
|
||||
./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
|
||||
|
||||
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.
|
||||
|
||||
|
||||
### -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
|
||||
|
||||
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.
|
||||
|
19
SECURITY.md
@ -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
After Width: | Height: | Size: 135 KiB |
40
src/args.rs
@ -1,4 +1,3 @@
|
||||
use std::net::IpAddr;
|
||||
use clap::Parser;
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
@ -9,54 +8,51 @@ lazy_static! {
|
||||
#[derive(Parser, Debug, Clone)]
|
||||
#[clap(author, version, about, long_about = None)]
|
||||
pub struct Args {
|
||||
#[clap(long, env="MICROBIN_AUTH_USERNAME")]
|
||||
#[clap(long)]
|
||||
pub auth_username: Option<String>,
|
||||
|
||||
#[clap(long, env="MICROBIN_AUTH_PASSWORD")]
|
||||
#[clap(long)]
|
||||
pub auth_password: Option<String>,
|
||||
|
||||
#[clap(long, env="MICROBIN_EDITABLE")]
|
||||
#[clap(long)]
|
||||
pub editable: bool,
|
||||
|
||||
#[clap(long, env="MICROBIN_FOOTER_TEXT")]
|
||||
#[clap(long)]
|
||||
pub footer_text: Option<String>,
|
||||
|
||||
#[clap(long, env="MICROBIN_HIDE_FOOTER")]
|
||||
#[clap(long)]
|
||||
pub hide_footer: bool,
|
||||
|
||||
#[clap(long, env="MICROBIN_HIDE_HEADER")]
|
||||
#[clap(long)]
|
||||
pub hide_header: bool,
|
||||
|
||||
#[clap(long, env="MICROBIN_HIDE_LOGO")]
|
||||
#[clap(long)]
|
||||
pub hide_logo: bool,
|
||||
|
||||
#[clap(long, env="MICROBIN_NO_LISTING")]
|
||||
#[clap(long)]
|
||||
pub no_listing: bool,
|
||||
|
||||
#[clap(long, env="MICROBIN_HIGHLIGHTSYNTAX")]
|
||||
#[clap(long)]
|
||||
pub highlightsyntax: bool,
|
||||
|
||||
#[clap(short, long, env="MICROBIN_PORT", default_value_t = 8080)]
|
||||
pub port: u16,
|
||||
#[clap(short, long, default_value_t = 8080)]
|
||||
pub port: u32,
|
||||
|
||||
#[clap(short, long, env="MICROBIN_BIND", default_value_t = IpAddr::from([0, 0, 0, 0]))]
|
||||
pub bind: IpAddr,
|
||||
|
||||
#[clap(long, env="MICROBIN_PRIVATE")]
|
||||
#[clap(long)]
|
||||
pub private: bool,
|
||||
|
||||
#[clap(long, env="MICROBIN_PURE_HTML")]
|
||||
#[clap(long)]
|
||||
pub pure_html: bool,
|
||||
|
||||
#[clap(long, env="MICROBIN_READONLY")]
|
||||
#[clap(long)]
|
||||
pub readonly: bool,
|
||||
|
||||
#[clap(long, env="MICROBIN_TITLE")]
|
||||
#[clap(long)]
|
||||
pub title: Option<String>,
|
||||
|
||||
#[clap(short, long, env="MICROBIN_THREADS", default_value_t = 1)]
|
||||
#[clap(short, long, default_value_t = 1)]
|
||||
pub threads: u8,
|
||||
|
||||
#[clap(long, env="MICROBIN_WIDE")]
|
||||
#[clap(long)]
|
||||
pub wide: bool,
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,11 @@
|
||||
use crate::dbio::save_to_file;
|
||||
use crate::pasta::PastaFile;
|
||||
use crate::util::animalnumbers::to_animal_names;
|
||||
use crate::util::misc::is_valid_url;
|
||||
use crate::{AppState, Pasta, ARGS};
|
||||
use actix_multipart::Multipart;
|
||||
use actix_web::{get, web, Error, HttpResponse, Responder};
|
||||
use askama::Template;
|
||||
use bytesize::ByteSize;
|
||||
use futures::TryStreamExt;
|
||||
use log::warn;
|
||||
use rand::Rng;
|
||||
use std::io::Write;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
@ -49,7 +46,7 @@ pub async fn create(
|
||||
let mut new_pasta = Pasta {
|
||||
id: rand::thread_rng().gen::<u16>() as u64,
|
||||
content: String::from("No Text Content"),
|
||||
file: None,
|
||||
file: String::from("no-file"),
|
||||
extension: String::from(""),
|
||||
private: false,
|
||||
editable: false,
|
||||
@ -106,41 +103,27 @@ pub async fn create(
|
||||
continue;
|
||||
}
|
||||
"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(p) => p,
|
||||
Some(filename) => filename.replace(' ', "_").to_string(),
|
||||
None => continue,
|
||||
};
|
||||
|
||||
let mut file = match PastaFile::from_unsanitized(&path) {
|
||||
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()))
|
||||
std::fs::create_dir_all(format!("./pasta_data/{}", &new_pasta.id_as_animals()))
|
||||
.unwrap();
|
||||
|
||||
let filepath = format!(
|
||||
"./pasta_data/public/{}/{}",
|
||||
&new_pasta.id_as_animals(),
|
||||
&file.name()
|
||||
);
|
||||
let filepath = format!("./pasta_data/{}/{}", &new_pasta.id_as_animals(), &filename);
|
||||
|
||||
new_pasta.file = filename;
|
||||
|
||||
let mut f = web::block(|| std::fs::File::create(filepath)).await??;
|
||||
let mut size = 0;
|
||||
|
||||
while let Some(chunk) = field.try_next().await? {
|
||||
size += chunk.len();
|
||||
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");
|
||||
}
|
||||
_ => {}
|
||||
|
@ -2,12 +2,10 @@ use actix_web::{get, web, HttpResponse};
|
||||
|
||||
use crate::args::ARGS;
|
||||
use crate::endpoints::errors::ErrorTemplate;
|
||||
use crate::pasta::PastaFile;
|
||||
use crate::util::animalnumbers::to_u64;
|
||||
use crate::util::misc::remove_expired;
|
||||
use crate::AppState;
|
||||
use askama::Template;
|
||||
use std::fs;
|
||||
|
||||
#[get("/remove/{id}")]
|
||||
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);
|
||||
|
||||
remove_expired(&mut pastas);
|
||||
|
||||
for (i, pasta) in pastas.iter().enumerate() {
|
||||
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);
|
||||
return HttpResponse::Found()
|
||||
.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()
|
||||
.content_type("text/html")
|
||||
.body(ErrorTemplate { args: &ARGS }.render().unwrap())
|
||||
|
@ -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 askama::Template;
|
||||
use std::io::ErrorKind::NotFound;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "water.css", escape = "none")]
|
||||
struct WaterCSS<'a> {
|
||||
_marker: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
#[get("/static/{resource}")]
|
||||
pub async fn static_resources(resource_id: web::Path<String>) -> HttpResponse {
|
||||
match resource_id.into_inner().as_str() {
|
||||
"water.css" => HttpResponse::Ok().content_type("text/css").body(
|
||||
WaterCSS {
|
||||
_marker: Default::default(),
|
||||
}
|
||||
.render()
|
||||
.unwrap(),
|
||||
),
|
||||
return match resource_id.into_inner().as_str() {
|
||||
"water.css" => HttpResponse::Ok()
|
||||
.content_type("text/css")
|
||||
.body(include_bytes!("../../templates/static/water.css").to_vec()),
|
||||
|
||||
"icon.ico" => HttpResponse::Ok()
|
||||
.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(),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
13
src/main.rs
@ -58,16 +58,15 @@ async fn main() -> std::io::Result<()> {
|
||||
.init();
|
||||
|
||||
log::info!(
|
||||
"MicroBin starting on http://{}:{}",
|
||||
ARGS.bind.to_string(),
|
||||
"MicroBin starting on http://127.0.0.1:{}",
|
||||
ARGS.port.to_string()
|
||||
);
|
||||
|
||||
match fs::create_dir_all("./pasta_data/public") {
|
||||
match fs::create_dir_all("./pasta_data") {
|
||||
Ok(dir) => dir,
|
||||
Err(error) => {
|
||||
log::error!("Couldn't create data directory ./pasta_data/public/: {:?}", error);
|
||||
panic!("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: {:?}", error);
|
||||
}
|
||||
};
|
||||
|
||||
@ -87,7 +86,7 @@ async fn main() -> std::io::Result<()> {
|
||||
.service(edit::get_edit)
|
||||
.service(edit::post_edit)
|
||||
.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)))
|
||||
.default_service(web::route().to(errors::not_found))
|
||||
.wrap(middleware::Logger::default())
|
||||
@ -98,7 +97,7 @@ async fn main() -> std::io::Result<()> {
|
||||
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)
|
||||
.run()
|
||||
.await
|
||||
|
40
src/pasta.rs
@ -1,39 +1,16 @@
|
||||
use bytesize::ByteSize;
|
||||
use chrono::{Datelike, Local, TimeZone, Timelike};
|
||||
use serde::{Deserialize, Serialize};
|
||||
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::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)]
|
||||
pub struct Pasta {
|
||||
pub id: u64,
|
||||
pub content: String,
|
||||
pub file: Option<PastaFile>,
|
||||
pub file: String,
|
||||
pub extension: String,
|
||||
pub private: bool,
|
||||
pub editable: bool,
|
||||
@ -48,9 +25,9 @@ impl Pasta {
|
||||
}
|
||||
|
||||
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!(
|
||||
"{:02}-{:02} {:02}:{:02}",
|
||||
"{:02}-{:02} {}:{}",
|
||||
date.month(),
|
||||
date.day(),
|
||||
date.hour(),
|
||||
@ -62,9 +39,10 @@ impl Pasta {
|
||||
if self.expiration == 0 {
|
||||
String::from("Never")
|
||||
} else {
|
||||
let date = Local.timestamp(self.expiration, 0);
|
||||
let date =
|
||||
DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(self.expiration, 0), Utc);
|
||||
format!(
|
||||
"{:02}-{:02} {:02}:{:02}",
|
||||
"{:02}-{:02} {}:{}",
|
||||
date.month(),
|
||||
date.day(),
|
||||
date.hour(),
|
||||
|
@ -22,22 +22,20 @@ pub fn remove_expired(pastas: &mut Vec<Pasta>) {
|
||||
true
|
||||
} else {
|
||||
// remove the file itself
|
||||
if let Some(file) = &p.file {
|
||||
if fs::remove_file(format!(
|
||||
"./pasta_data/public/{}/{}",
|
||||
p.id_as_animals(),
|
||||
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())
|
||||
match fs::remove_file(format!("./pasta_data/{}/{}", p.id_as_animals(), p.file)) {
|
||||
Ok(_) => {}
|
||||
Err(_) => {
|
||||
log::error!("Failed to delete file {}!", p.file)
|
||||
}
|
||||
}
|
||||
// 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
|
||||
}
|
||||
});
|
||||
|
@ -1,12 +1,19 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
{% if args.title.as_ref().is_none() %}
|
||||
{% if args.footer_text.as_ref().is_none() %}
|
||||
<title>MicroBin</title>
|
||||
{%- else %}
|
||||
<title>{{ args.title.as_ref().unwrap() }}</title>
|
||||
{%- 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 name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
{% if !args.pure_html %}
|
||||
@ -39,11 +46,12 @@
|
||||
<b style="margin-right: 0.5rem">
|
||||
|
||||
{% 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 %}
|
||||
|
||||
{% if args.title.as_ref().is_none() %}
|
||||
MicroBin
|
||||
{% if args.footer_text.as_ref().is_none() %}
|
||||
<span>MicroBin</span>
|
||||
{%- else %}
|
||||
{{ args.title.as_ref().unwrap() }}
|
||||
{%- endif %}
|
||||
|
@ -4,10 +4,10 @@
|
||||
<div style="display: grid;
|
||||
grid-gap: 4px;
|
||||
grid-template-columns: repeat(auto-fit, 234px);
|
||||
grid-template-rows: repeat(1, 100px); ">
|
||||
grid-template-rows: repeat(1, 78px); ">
|
||||
<div>
|
||||
<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">
|
||||
<option value="1min">1 minute</option>
|
||||
<option value="10min">10 minutes</option>
|
||||
@ -21,7 +21,7 @@
|
||||
{% if args.highlightsyntax %}
|
||||
<div>
|
||||
<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>
|
||||
<optgroup label="Source Code">
|
||||
<option value="sh">Bash Shell</option>
|
||||
@ -58,13 +58,12 @@
|
||||
{%- else %}
|
||||
<input type="hidden" name="syntax-highlight" value="none">
|
||||
{%- endif %}
|
||||
|
||||
<div>
|
||||
<label>File attachment</label>
|
||||
<br>
|
||||
<input style="width: 100%;" type="file" id="file" name="file">
|
||||
<label>File attachment</label><br>
|
||||
<input style="width: 100%; height: 21px" type="file" id="file" name="file">
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<label>Content</label>
|
||||
<br>
|
||||
<textarea style="width: 100%; min-height: 100px" name="content" autofocus></textarea>
|
||||
|
@ -1,28 +1,19 @@
|
||||
{% include "header.html" %}
|
||||
<div style="float: left">
|
||||
<a style="margin-right: 0.5rem" href="/raw/{{pasta.id_as_animals()}}">Raw Text Content</a>
|
||||
{% if pasta.file.is_some() %}
|
||||
<a style="margin-right: 0.5rem; margin-left: 0.5rem"
|
||||
href="/file/{{pasta.id_as_animals()}}/{{pasta.file.as_ref().unwrap().name()}}">
|
||||
Attached file'{{pasta.file.as_ref().unwrap().name()}}' [{{pasta.file.as_ref().unwrap().size}}]
|
||||
</a>
|
||||
{%- endif %}
|
||||
{% if pasta.editable %}
|
||||
<a style="margin-right: 0.5rem; margin-left: 0.5rem" href="/edit/{{pasta.id_as_animals()}}">Edit</a>
|
||||
{%- endif %}
|
||||
<a style="margin-right: 0.5rem; margin-left: 0.5rem" href="/remove/{{pasta.id_as_animals()}}">Remove</a>
|
||||
</div>
|
||||
<div style="float: right">
|
||||
<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>
|
||||
<a style="margin-right: 0.5rem" href="/raw/{{pasta.id_as_animals()}}">Raw Text Content</a>
|
||||
{% if pasta.file != "no-file" %}
|
||||
<a style="margin-right: 0.5rem; margin-left: 0.5rem" href="/file/{{pasta.id_as_animals()}}/{{pasta.file}}">Attached file
|
||||
'{{pasta.file}}'</a>
|
||||
{%- endif %}
|
||||
{% if pasta.editable %}
|
||||
<a style="margin-right: 0.5rem; margin-left: 0.5rem" href="/edit/{{pasta.id_as_animals()}}">Edit</a>
|
||||
{%- endif %}
|
||||
<a style="margin-right: 0.5rem; margin-left: 0.5rem" href="/remove/{{pasta.id_as_animals()}}">Remove</a>
|
||||
{% if args.highlightsyntax %}
|
||||
<pre><code>{{pasta.content_syntax_highlighted()}}</code></pre>
|
||||
{%- else %}
|
||||
<pre><code>{{pasta.content_not_highlighted()}}</code></pre>
|
||||
{%- endif %}
|
||||
|
||||
<style>
|
||||
code-line {
|
||||
counter-increment: listing;
|
||||
|
@ -48,8 +48,8 @@
|
||||
</td>
|
||||
<td>
|
||||
<a style="margin-right:1rem" href="/raw/{{pasta.id_as_animals()}}">Raw</a>
|
||||
{% if pasta.file.is_some() %}
|
||||
<a style="margin-right:1rem" href="/file/{{pasta.id_as_animals()}}/{{pasta.file.as_ref().unwrap().name()}}">File</a>
|
||||
{% if pasta.file != "no-file" %}
|
||||
<a style="margin-right:1rem" href="/file/{{pasta.id_as_animals()}}/{{pasta.file}}">File</a>
|
||||
{%- endif %}
|
||||
{% if pasta.editable %}
|
||||
<a style="margin-right:1rem" href="/edit/{{pasta.id_as_animals()}}">Edit</a>
|
||||
|
BIN
templates/static/apple-touch-icon.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
templates/static/icon-16x16.png
Normal file
After Width: | Height: | Size: 875 B |
BIN
templates/static/icon-192x192.png
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
templates/static/icon-32x32.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
templates/static/icon-512x512.png
Normal file
After Width: | Height: | Size: 135 KiB |
BIN
templates/static/icon.ico
Normal file
After Width: | Height: | Size: 15 KiB |