mirror of https://github.com/zer0bin-dev/zer0bin
Merge 4114749e0d
into de58aa84c2
This commit is contained in:
commit
1a636b001b
|
@ -15,7 +15,7 @@ use config::Config;
|
|||
use sqlx::{migrate::Migrator, postgres::PgPoolOptions, PgPool};
|
||||
|
||||
use crate::routes::{
|
||||
get_paste, get_raw_paste, get_stats, get_total_pastes_badge, get_version_badge, new_paste,
|
||||
download_paste, get_paste, get_raw_paste, get_stats, get_total_pastes_badge, get_version_badge, new_paste,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -79,6 +79,7 @@ async fn main() -> io::Result<()> {
|
|||
.wrap(Governor::new(&paste_governor))
|
||||
.service(get_paste)
|
||||
.service(new_paste)
|
||||
.service(download_paste)
|
||||
.service(get_raw_paste),
|
||||
)
|
||||
.service(
|
||||
|
|
|
@ -2,6 +2,7 @@ use actix_web::{
|
|||
get, post,
|
||||
web::{self},
|
||||
HttpResponse, Responder,
|
||||
http::header
|
||||
};
|
||||
|
||||
use badge_maker::{BadgeBuilder, Style};
|
||||
|
@ -138,6 +139,69 @@ pub async fn get_raw_paste(state: web::Data<AppState>, id: web::Path<String>) ->
|
|||
}
|
||||
}
|
||||
|
||||
#[get("/d/{id}")]
|
||||
pub async fn download_paste(state: web::Data<AppState>, id: web::Path<String>) -> impl Responder {
|
||||
let id = id.into_inner();
|
||||
|
||||
let res: Result<Paste, sqlx::Error> =
|
||||
sqlx::query_as::<_, Paste>(r#"SELECT * FROM pastes WHERE "id" = $1"#)
|
||||
.bind(id.clone())
|
||||
.fetch_one(&state.pool)
|
||||
.await;
|
||||
|
||||
match res {
|
||||
Ok(p) => {
|
||||
if p.single_view {
|
||||
let _ = sqlx::query(r#"DELETE FROM pastes WHERE "id" = $1"#)
|
||||
.bind(id.clone())
|
||||
.execute(&state.pool)
|
||||
.await;
|
||||
} else {
|
||||
let _ = sqlx::query(r#"UPDATE pastes SET "views" = "views" + 1 WHERE "id" = $1"#)
|
||||
.bind(id.clone())
|
||||
.execute(&state.pool)
|
||||
.await;
|
||||
}
|
||||
|
||||
if state.config.logging.on_get_paste {
|
||||
println!("[GET] download id={} views={} single_view={}", id, p.views + 1, p.single_view);
|
||||
}
|
||||
|
||||
let markdown = p.content.starts_with("md ") || p.content.starts_with("md\n") || p.content.starts_with("---");
|
||||
|
||||
HttpResponse::Ok()
|
||||
.insert_header(header::ContentType::octet_stream())
|
||||
.insert_header(header::ContentDisposition {
|
||||
disposition: header::DispositionType::Attachment,
|
||||
parameters: vec![header::DispositionParam::Filename(format!("{}.{}", id, if markdown { "md" } else { "txt" }))]
|
||||
})
|
||||
.body(p.content)
|
||||
}
|
||||
Err(e) => match e {
|
||||
sqlx::Error::RowNotFound => {
|
||||
return HttpResponse::NotFound().json(ApiResponse {
|
||||
success: false,
|
||||
data: ApiError {
|
||||
message: format!("Paste {id} wasn't found."),
|
||||
},
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
eprintln!("Error occured while getting paste: {:?}", e);
|
||||
|
||||
HttpResponse::InternalServerError().json(ApiResponse {
|
||||
success: false,
|
||||
data: ApiError {
|
||||
message: "Unknown error occured, please try again.".to_string(),
|
||||
},
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[post("/n")]
|
||||
pub async fn new_paste(
|
||||
state: web::Data<AppState>,
|
||||
|
|
|
@ -52,7 +52,7 @@ renderIcon(markdownButton, FileMarkdownOutlined)
|
|||
renderIcon(singleViewButton, FireOutlined)
|
||||
renderIcon(shareButton, ShareAltOutlined)
|
||||
|
||||
tippy("#save-button", {
|
||||
export const saveTippy = tippy("#save-button", {
|
||||
content: "Save paste<br><span class='keybind'>Ctrl + S</span>",
|
||||
placement: "bottom",
|
||||
animation: "scale",
|
||||
|
|
|
@ -10,14 +10,15 @@ import hljs from "highlight.js/lib/common"
|
|||
|
||||
import "../min/rosepine.min.css"
|
||||
import config from "../config.json"
|
||||
import { toggleHiddenIcon } from "./icons"
|
||||
import { toggleHiddenIcon, saveTippy } from "./icons"
|
||||
const apiUrl = config.api_url
|
||||
const confettiChance = parseInt(config.confetti_chance)
|
||||
let rawContent = ""
|
||||
let buttonPaneHidden = false
|
||||
let isMarkdown = false
|
||||
let singleView = false
|
||||
|
||||
let inView = false
|
||||
let viewId = ""
|
||||
const jsConfetti = new JSConfetti()
|
||||
|
||||
const lineNumbers = <HTMLElement>document.querySelector(".line-numbers")
|
||||
|
@ -121,6 +122,8 @@ function newPaste() {
|
|||
disable(copyButton)
|
||||
disable(shareButton)
|
||||
enable(singleViewButton)
|
||||
saveTippy.setContent("Save paste<br><span class='keybind'>Ctrl + S</span>")
|
||||
|
||||
|
||||
editor.value = ""
|
||||
rawContent = ""
|
||||
|
@ -149,6 +152,7 @@ function addMessage(message: string) {
|
|||
}
|
||||
|
||||
function viewPaste(content: string, views: string, singleView: boolean) {
|
||||
inView = true
|
||||
lineNumbers.innerHTML = ""
|
||||
if (
|
||||
content.startsWith("---") ||
|
||||
|
@ -182,12 +186,13 @@ function viewPaste(content: string, views: string, singleView: boolean) {
|
|||
addMessage("Copied URL to clipboard!")
|
||||
}
|
||||
})
|
||||
|
||||
disable(saveButton)
|
||||
|
||||
enable(saveButton)
|
||||
disable(markdownButton)
|
||||
enable(newButton)
|
||||
enable(copyButton)
|
||||
disable(singleViewButton)
|
||||
saveTippy.setContent("Download paste<br><span class='keybind'>Ctrl + S</span>")
|
||||
|
||||
hide(editor)
|
||||
show(codeViewPre)
|
||||
|
@ -206,6 +211,11 @@ function viewPaste(content: string, views: string, singleView: boolean) {
|
|||
}
|
||||
|
||||
async function savePaste() {
|
||||
if (inView) {
|
||||
window.location = `${apiUrl}/p/d/${viewId}`
|
||||
return
|
||||
}
|
||||
|
||||
if (editor.value === "") {
|
||||
return
|
||||
}
|
||||
|
@ -218,6 +228,7 @@ async function savePaste() {
|
|||
window.history.pushState(null, "", `/${res["data"]["id"]}`)
|
||||
|
||||
rawContent = res["data"]["content"]
|
||||
viewId = res["data"]["id"]
|
||||
viewPaste(rawContent, "0", res["data"]["single_view"])
|
||||
|
||||
const rand = Math.floor(Math.random() * confettiChance * 6)
|
||||
|
|
Loading…
Reference in New Issue