Compare commits
30 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
f55a5eba96 | ||
![]() |
011cc25490 | ||
![]() |
d44a3081bc | ||
![]() |
51f7f54be7 | ||
![]() |
a3fc97a460 | ||
![]() |
05941f0d6f | ||
![]() |
7b4cd7c26e | ||
![]() |
f54d5bd780 | ||
![]() |
435c07d75e | ||
![]() |
cc09d1b529 | ||
![]() |
60c3a1f9ac | ||
![]() |
d4d94b61da | ||
![]() |
9053211904 | ||
![]() |
35a512680c | ||
![]() |
bcd620ed43 | ||
![]() |
556f4e87df | ||
![]() |
fa88bce917 | ||
![]() |
a5d326b679 | ||
![]() |
465873e095 | ||
![]() |
39233e9447 | ||
![]() |
738e036cb5 | ||
![]() |
de2cc48f88 | ||
![]() |
0687f44137 | ||
![]() |
fec933c5ec | ||
![]() |
cc2dd1e1fe | ||
![]() |
85ed1b2b92 | ||
![]() |
73ec59ccda | ||
![]() |
f5b9036a2a | ||
![]() |
0d43f2f60a | ||
![]() |
aa9246da4e |
13
.github/FUNDING.yml
vendored
13
.github/FUNDING.yml
vendored
@ -1,13 +0,0 @@
|
|||||||
# 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']
|
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name="microbin"
|
name="microbin"
|
||||||
version="1.0.2"
|
version="1.1.0"
|
||||||
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,12 +18,13 @@ 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"] }
|
clap={ version = "3.1.12", features = ["derive", "env"] }
|
||||||
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"
|
||||||
|
@ -19,8 +19,5 @@ 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"]
|
||||||
|
57
README.MD
57
README.MD
@ -105,11 +105,14 @@ Remember, MicroBin will create your database and file storage wherever you execu
|
|||||||
|
|
||||||
`cd ~/microbin/`
|
`cd ~/microbin/`
|
||||||
|
|
||||||
`microbin --port 8080 --highlightsyntax --editable`
|
`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`.
|
||||||
|
|
||||||
### 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 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).
|
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).
|
||||||
|
|
||||||
```
|
```
|
||||||
git clone https://github.com/szabodanika/microbin.git
|
git clone https://github.com/szabodanika/microbin.git
|
||||||
@ -118,6 +121,49 @@ 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`.
|
||||||
@ -247,6 +293,13 @@ 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.
|
||||||
|
19
SECURITY.md
Normal file
19
SECURITY.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# 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.
|
40
src/args.rs
40
src/args.rs
@ -1,3 +1,4 @@
|
|||||||
|
use std::net::IpAddr;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
@ -8,51 +9,54 @@ 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)]
|
#[clap(long, env="MICROBIN_AUTH_USERNAME")]
|
||||||
pub auth_username: Option<String>,
|
pub auth_username: Option<String>,
|
||||||
|
|
||||||
#[clap(long)]
|
#[clap(long, env="MICROBIN_AUTH_PASSWORD")]
|
||||||
pub auth_password: Option<String>,
|
pub auth_password: Option<String>,
|
||||||
|
|
||||||
#[clap(long)]
|
#[clap(long, env="MICROBIN_EDITABLE")]
|
||||||
pub editable: bool,
|
pub editable: bool,
|
||||||
|
|
||||||
#[clap(long)]
|
#[clap(long, env="MICROBIN_FOOTER_TEXT")]
|
||||||
pub footer_text: Option<String>,
|
pub footer_text: Option<String>,
|
||||||
|
|
||||||
#[clap(long)]
|
#[clap(long, env="MICROBIN_HIDE_FOOTER")]
|
||||||
pub hide_footer: bool,
|
pub hide_footer: bool,
|
||||||
|
|
||||||
#[clap(long)]
|
#[clap(long, env="MICROBIN_HIDE_HEADER")]
|
||||||
pub hide_header: bool,
|
pub hide_header: bool,
|
||||||
|
|
||||||
#[clap(long)]
|
#[clap(long, env="MICROBIN_HIDE_LOGO")]
|
||||||
pub hide_logo: bool,
|
pub hide_logo: bool,
|
||||||
|
|
||||||
#[clap(long)]
|
#[clap(long, env="MICROBIN_NO_LISTING")]
|
||||||
pub no_listing: bool,
|
pub no_listing: bool,
|
||||||
|
|
||||||
#[clap(long)]
|
#[clap(long, env="MICROBIN_HIGHLIGHTSYNTAX")]
|
||||||
pub highlightsyntax: bool,
|
pub highlightsyntax: bool,
|
||||||
|
|
||||||
#[clap(short, long, default_value_t = 8080)]
|
#[clap(short, long, env="MICROBIN_PORT", default_value_t = 8080)]
|
||||||
pub port: u32,
|
pub port: u16,
|
||||||
|
|
||||||
#[clap(long)]
|
#[clap(short, long, env="MICROBIN_BIND", default_value_t = IpAddr::from([0, 0, 0, 0]))]
|
||||||
|
pub bind: IpAddr,
|
||||||
|
|
||||||
|
#[clap(long, env="MICROBIN_PRIVATE")]
|
||||||
pub private: bool,
|
pub private: bool,
|
||||||
|
|
||||||
#[clap(long)]
|
#[clap(long, env="MICROBIN_PURE_HTML")]
|
||||||
pub pure_html: bool,
|
pub pure_html: bool,
|
||||||
|
|
||||||
#[clap(long)]
|
#[clap(long, env="MICROBIN_READONLY")]
|
||||||
pub readonly: bool,
|
pub readonly: bool,
|
||||||
|
|
||||||
#[clap(long)]
|
#[clap(long, env="MICROBIN_TITLE")]
|
||||||
pub title: Option<String>,
|
pub title: Option<String>,
|
||||||
|
|
||||||
#[clap(short, long, default_value_t = 1)]
|
#[clap(short, long, env="MICROBIN_THREADS", default_value_t = 1)]
|
||||||
pub threads: u8,
|
pub threads: u8,
|
||||||
|
|
||||||
#[clap(long)]
|
#[clap(long, env="MICROBIN_WIDE")]
|
||||||
pub wide: bool,
|
pub wide: bool,
|
||||||
}
|
}
|
@ -1,11 +1,14 @@
|
|||||||
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};
|
||||||
@ -46,7 +49,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: String::from("no-file"),
|
file: None,
|
||||||
extension: String::from(""),
|
extension: String::from(""),
|
||||||
private: false,
|
private: false,
|
||||||
editable: false,
|
editable: false,
|
||||||
@ -103,27 +106,41 @@ pub async fn create(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
"file" => {
|
"file" => {
|
||||||
let content_disposition = field.content_disposition();
|
let path = field.content_disposition().get_filename();
|
||||||
|
|
||||||
let filename = match content_disposition.get_filename() {
|
let path = match path {
|
||||||
Some("") => continue,
|
Some("") => continue,
|
||||||
Some(filename) => filename.replace(' ', "_").to_string(),
|
Some(p) => p,
|
||||||
None => continue,
|
None => continue,
|
||||||
};
|
};
|
||||||
|
|
||||||
std::fs::create_dir_all(format!("./pasta_data/{}", &new_pasta.id_as_animals()))
|
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()))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let filepath = format!("./pasta_data/{}/{}", &new_pasta.id_as_animals(), &filename);
|
let filepath = format!(
|
||||||
|
"./pasta_data/public/{}/{}",
|
||||||
new_pasta.file = filename;
|
&new_pasta.id_as_animals(),
|
||||||
|
&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");
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
@ -2,10 +2,12 @@ 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 {
|
||||||
@ -19,10 +21,22 @@ 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"))
|
||||||
@ -30,6 +44,8 @@ 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())
|
||||||
|
13
src/main.rs
13
src/main.rs
@ -58,15 +58,16 @@ async fn main() -> std::io::Result<()> {
|
|||||||
.init();
|
.init();
|
||||||
|
|
||||||
log::info!(
|
log::info!(
|
||||||
"MicroBin starting on http://127.0.0.1:{}",
|
"MicroBin starting on http://{}:{}",
|
||||||
|
ARGS.bind.to_string(),
|
||||||
ARGS.port.to_string()
|
ARGS.port.to_string()
|
||||||
);
|
);
|
||||||
|
|
||||||
match fs::create_dir_all("./pasta_data") {
|
match fs::create_dir_all("./pasta_data/public") {
|
||||||
Ok(dir) => dir,
|
Ok(dir) => dir,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
log::error!("Couldn't create data directory ./pasta_data: {:?}", error);
|
log::error!("Couldn't create data directory ./pasta_data/public/: {:?}", error);
|
||||||
panic!("Couldn't create data directory ./pasta_data: {:?}", error);
|
panic!("Couldn't create data directory ./pasta_data/public/: {:?}", error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -86,7 +87,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"))
|
.service(actix_files::Files::new("/file", "./pasta_data/public/"))
|
||||||
.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())
|
||||||
@ -97,7 +98,7 @@ async fn main() -> std::io::Result<()> {
|
|||||||
HttpAuthentication::basic(util::auth::auth_validator),
|
HttpAuthentication::basic(util::auth::auth_validator),
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
.bind(format!("0.0.0.0:{}", ARGS.port.to_string()))?
|
.bind((ARGS.bind, ARGS.port))?
|
||||||
.workers(ARGS.threads as usize)
|
.workers(ARGS.threads as usize)
|
||||||
.run()
|
.run()
|
||||||
.await
|
.await
|
||||||
|
40
src/pasta.rs
40
src/pasta.rs
@ -1,16 +1,39 @@
|
|||||||
use std::fmt;
|
use bytesize::ByteSize;
|
||||||
|
use chrono::{Datelike, Local, TimeZone, Timelike};
|
||||||
use chrono::{DateTime, Datelike, NaiveDateTime, Timelike, Utc};
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::fmt;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
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: String,
|
pub file: Option<PastaFile>,
|
||||||
pub extension: String,
|
pub extension: String,
|
||||||
pub private: bool,
|
pub private: bool,
|
||||||
pub editable: bool,
|
pub editable: bool,
|
||||||
@ -25,9 +48,9 @@ impl Pasta {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn created_as_string(&self) -> String {
|
pub fn created_as_string(&self) -> String {
|
||||||
let date = DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(self.created, 0), Utc);
|
let date = Local.timestamp(self.created, 0);
|
||||||
format!(
|
format!(
|
||||||
"{:02}-{:02} {}:{}",
|
"{:02}-{:02} {:02}:{:02}",
|
||||||
date.month(),
|
date.month(),
|
||||||
date.day(),
|
date.day(),
|
||||||
date.hour(),
|
date.hour(),
|
||||||
@ -39,10 +62,9 @@ impl Pasta {
|
|||||||
if self.expiration == 0 {
|
if self.expiration == 0 {
|
||||||
String::from("Never")
|
String::from("Never")
|
||||||
} else {
|
} else {
|
||||||
let date =
|
let date = Local.timestamp(self.expiration, 0);
|
||||||
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(),
|
||||||
|
@ -22,20 +22,22 @@ pub fn remove_expired(pastas: &mut Vec<Pasta>) {
|
|||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
// remove the file itself
|
// remove the file itself
|
||||||
match fs::remove_file(format!("./pasta_data/{}/{}", p.id_as_animals(), p.file)) {
|
if let Some(file) = &p.file {
|
||||||
Ok(_) => {}
|
if fs::remove_file(format!(
|
||||||
Err(_) => {
|
"./pasta_data/public/{}/{}",
|
||||||
log::error!("Failed to delete file {}!", p.file)
|
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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 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
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
{% if args.footer_text.as_ref().is_none() %}
|
{% if args.title.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>
|
||||||
@ -42,7 +42,7 @@
|
|||||||
<i><span style="font-size:2.2rem; margin-right:1rem">μ</span></i>
|
<i><span style="font-size:2.2rem; margin-right:1rem">μ</span></i>
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
|
||||||
{% if args.footer_text.as_ref().is_none() %}
|
{% if args.title.as_ref().is_none() %}
|
||||||
MicroBin
|
MicroBin
|
||||||
{%- else %}
|
{%- else %}
|
||||||
{{ args.title.as_ref().unwrap() }}
|
{{ args.title.as_ref().unwrap() }}
|
||||||
|
@ -1,19 +1,28 @@
|
|||||||
{% include "header.html" %}
|
{% include "header.html" %}
|
||||||
<a style="margin-right: 0.5rem" href="/raw/{{pasta.id_as_animals()}}">Raw Text Content</a>
|
<div style="float: left">
|
||||||
{% if pasta.file != "no-file" %}
|
<a style="margin-right: 0.5rem" href="/raw/{{pasta.id_as_animals()}}">Raw Text Content</a>
|
||||||
<a style="margin-right: 0.5rem; margin-left: 0.5rem" href="/file/{{pasta.id_as_animals()}}/{{pasta.file}}">Attached file
|
{% if pasta.file.is_some() %}
|
||||||
'{{pasta.file}}'</a>
|
<a style="margin-right: 0.5rem; margin-left: 0.5rem"
|
||||||
{%- endif %}
|
href="/file/{{pasta.id_as_animals()}}/{{pasta.file.as_ref().unwrap().name()}}">
|
||||||
{% if pasta.editable %}
|
Attached file'{{pasta.file.as_ref().unwrap().name()}}' [{{pasta.file.as_ref().unwrap().size}}]
|
||||||
<a style="margin-right: 0.5rem; margin-left: 0.5rem" href="/edit/{{pasta.id_as_animals()}}">Edit</a>
|
</a>
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
<a style="margin-right: 0.5rem; margin-left: 0.5rem" href="/remove/{{pasta.id_as_animals()}}">Remove</a>
|
{% if pasta.editable %}
|
||||||
{% if args.highlightsyntax %}
|
<a style="margin-right: 0.5rem; margin-left: 0.5rem" href="/edit/{{pasta.id_as_animals()}}">Edit</a>
|
||||||
<pre><code>{{pasta.content_syntax_highlighted()}}</code></pre>
|
{%- endif %}
|
||||||
{%- else %}
|
<a style="margin-right: 0.5rem; margin-left: 0.5rem" href="/remove/{{pasta.id_as_animals()}}">Remove</a>
|
||||||
<pre><code>{{pasta.content_not_highlighted()}}</code></pre>
|
</div>
|
||||||
{%- endif %}
|
<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>
|
||||||
<style>
|
<style>
|
||||||
code-line {
|
code-line {
|
||||||
counter-increment: listing;
|
counter-increment: listing;
|
||||||
|
@ -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 != "no-file" %}
|
{% if pasta.file.is_some() %}
|
||||||
<a style="margin-right:1rem" href="/file/{{pasta.id_as_animals()}}/{{pasta.file}}">File</a>
|
<a style="margin-right:1rem" href="/file/{{pasta.id_as_animals()}}/{{pasta.file.as_ref().unwrap().name()}}">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>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user