diff --git a/README.md b/README.md index 418fe89..0033370 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,6 @@ A simple, light and standalone pastebin, URL shortener and file-sharing service * A tracking tool. No stats are stored to increase speed and reduce resource usage, if this is what you are looking for this tool is not for you. -![Screenshot of filite UI](screenshot.png) - ## Installation 1. Get the binary either from the [releases page](https://github.com/raftario/filite/releases) or [using Cargo](https://crates.io/crates/filite) @@ -59,6 +57,7 @@ pool_size = 4 files_dir = "files" # Max allowed size for file uploads, in bytes max_filesize = 10000000 + # Highlight.js configuration [highlight] # Theme to use diff --git a/resources/index.html b/resources/index.html index ad85bbb..9c669af 100644 --- a/resources/index.html +++ b/resources/index.html @@ -3,77 +3,449 @@ + + filite - -
-
- - - - -
- + +
+
+
+
+ +
+ /f/ + + +
+

Press space to randomize

+
+
+ + +
+
+ +
+
+ +
+ /t/ + + +
+

Press space to randomize

+
+
+ + +
+
+ +
+
+ diff --git a/resources/script.js b/resources/script.js deleted file mode 100644 index c6c5907..0000000 --- a/resources/script.js +++ /dev/null @@ -1,252 +0,0 @@ -const tabs = { - files: [ - document.querySelector("#files-tab"), - document.querySelector("#files-form"), - ], - links: [ - document.querySelector("#links-tab"), - document.querySelector("#links-form"), - ], - texts: [ - document.querySelector("#texts-tab"), - document.querySelector("#texts-form"), - ], -}; - -const inputs = { - files: [ - document.querySelector("#files-url"), - document.querySelector("#files-file"), - document.querySelector("#files-submit"), - ], - links: [ - document.querySelector("#links-url"), - document.querySelector("#links-forward"), - document.querySelector("#links-submit"), - ], - texts: [ - document.querySelector("#texts-url"), - document.querySelector("#texts-contents"), - document.querySelector("#texts-submit"), - ], -}; - -const used = { - files: [], - links: [], - texts: [], -}; - -let baseUrl = `${location.protocol}//${location.host}${location.pathname}`; -if (!baseUrl.endsWith("/")) { - baseUrl += "/"; -} - -const fetchUsed = () => { - fetch(`${baseUrl}f`) - .then((response) => response.json()) - .then((json) => (used.files = json)); - fetch(`${baseUrl}l`) - .then((response) => response.json()) - .then((json) => (used.links = json)); - fetch(`${baseUrl}t`) - .then((response) => response.json()) - .then((json) => (used.texts = json)); -}; -fetchUsed(); - -const randomUrl = () => { - return Math.floor(Math.random() * 2147483647).toString(36); -}; - -const updateClipboard = (data) => { - navigator.permissions - .query({ name: "clipboard-write" }) - .then((result) => { - if (result.state === "denied") { - throw new Error(); - } - }) - .then(() => navigator.clipboard.writeText(data)) - .then(() => alert("URL copied to clipboard")) - .catch(() => alert(data)); -}; - -for (const group in tabs) { - tabs[group][0].onclick = () => { - const active = document.querySelectorAll(".active"); - for (const el of active) { - el.classList.remove("active"); - } - for (const el of tabs[group]) { - el.classList.add("active"); - } - }; -} - -for (const group in inputs) { - const submitButton = inputs[group][inputs[group].length - 1]; - - const urlInput = inputs[group][0]; - urlInput.addEventListener("input", (e) => { - if (urlInput.value[urlInput.value.length - 1] === " ") { - urlInput.value = randomUrl(); - checkValidity(); - e.preventDefault(); - return; - } - - urlInput.value = urlInput.value - .replace(/[^0-9A-Za-z]/g, "") - .toLowerCase(); - if (parseInt(urlInput.value, 36) > 2147483647) { - urlInput.setCustomValidity( - "Base 36 integer below or equal to zik0zj" - ); - } else { - urlInput.setCustomValidity(""); - } - }); - - const checkValidity = () => { - if (used[group].some((x) => x.id === parseInt(urlInput.value, 36))) { - urlInput.classList.add("conflict"); - } else { - urlInput.classList.remove("conflict"); - } - submitButton.disabled = inputs[group].some( - (input) => input.validity != undefined && !input.validity.valid - ); - }; - - for (const input of inputs[group].filter( - (input) => - input instanceof HTMLInputElement || - input instanceof HTMLTextAreaElement - )) { - input.addEventListener("input", () => checkValidity()); - input.addEventListener("change", () => checkValidity()); - } - - const clearInputs = () => { - for (const input of inputs[group].filter( - (input) => - input instanceof HTMLInputElement || - input instanceof HTMLTextAreaElement - )) { - input.value = ""; - } - submitButton.disabled = true; - }; - - if (group === "files") { - const filesFileInput = inputs.files[1]; - const filesBrowseButton = document.querySelector("#files-browse"); - const filesValueInput = document.querySelector("#files-value"); - filesFileInput.addEventListener("change", () => { - filesValueInput.value = filesFileInput.files[0].name || ""; - }); - filesBrowseButton.onclick = () => { - filesFileInput.click(); - }; - filesValueInput.onfocus = (e) => { - e.preventDefault(); - filesValueInput.blur(); - return false; - }; - - submitButton.addEventListener("click", () => { - const file = filesFileInput.files[0]; - - if (!file) { - alert(new Error("No file selected")); - return; - } - - let fileReader = new FileReader(); - fileReader.onload = () => { - const id = urlInput.value; - const url = `${baseUrl}f/${id}`; - - const base64 = btoa(fileReader.result); - const filename = file.name; - let status; - fetch(url, { - method: "PUT", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ base64, filename }), - }) - .then((response) => { - status = response.status; - return response.text(); - }) - .then((text) => { - if (status !== 201) { - throw new Error(text); - } else { - updateClipboard(url); - clearInputs(); - filesValueInput.value = ""; - fetchUsed(); - } - }) - .catch((error) => alert(error)); - }; - fileReader.readAsBinaryString(file); - }); - } else if (group === "links") { - submitButton.addEventListener("click", () => { - const id = urlInput.value; - const forward = inputs.links[1].value; - - const url = `${baseUrl}l/${id}`; - let status; - fetch(url, { - method: "PUT", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ forward }), - }) - .then((response) => { - status = response.status; - return response.text(); - }) - .then((text) => { - if (status !== 201) { - throw new Error(text); - } else { - updateClipboard(url); - clearInputs(); - fetchUsed(); - } - }) - .catch((error) => alert(error)); - }); - } else if (group === "texts") { - submitButton.addEventListener("click", () => { - const id = urlInput.value; - const contents = inputs.texts[1].value; - - const url = `${baseUrl}t/${id}`; - let status; - fetch(url, { - method: "PUT", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ contents, highlight: true }), - }) - .then((response) => { - status = response.status; - return response.text(); - }) - .then((text) => { - if (status !== 201) { - throw new Error(text); - } else { - updateClipboard(url); - clearInputs(); - fetchUsed(); - } - }) - .catch((error) => alert(error)); - }); - } -} diff --git a/resources/style.css b/resources/style.css deleted file mode 100644 index 6cd8818..0000000 --- a/resources/style.css +++ /dev/null @@ -1,171 +0,0 @@ -html, -body { - margin: 0px; - padding: 0px; - width: 100vw; - height: 100vh; - overflow-x: hidden; -} - -html { - font-family: sans-serif; - font-size: 200%; -} - -body { - display: grid; - grid-template: - [r1s] "nav" 2rem [r1e] - [r2s] "main" auto [r2e] - / auto; - background-color: #002b36; - color: #839496; -} - -::placeholder { - color: #586e75; -} - -nav { - display: flex; - justify-content: space-evenly; - grid-area: nav; - background-color: #073642; -} - -nav div { - display: flex; - justify-content: center; - align-items: center; - flex-grow: 1; - font-size: 1rem; - border-bottom: 0.0625rem solid #586e75; -} - -nav div:hover { - background-color: #002b36; - color: #93a1a1; - cursor: pointer; -} - -nav div.active { - background-color: #002b36; - color: #93a1a1; - border-top: 0.0625rem solid #586e75; - border-right: 0.0625rem solid #586e75; - border-left: 0.0625rem solid #586e75; - border-bottom: none; -} - -main { - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - grid-area: main; -} - -main div { - display: none; -} - -main div.active { - display: block; -} - -main div label { - display: block; - margin-bottom: 0.5rem; - padding-left: 1rem; - font-size: 1rem; -} - -main div input[type="text"], -main div input[type="url"] { - display: block; - padding: 0.5rem 1rem; - width: calc(75vw - 2rem); - background-color: #073642; - color: #839496; - font-size: 1rem; - border: none; -} - -main div textarea { - display: block; - padding: 0.5rem 1rem; - width: calc(75vw - 2rem); - height: 25vh; - background-color: #073642; - color: #839496; - font-family: monospace; - font-size: 0.5rem; - border: none; -} - -main div input[type="text"]:invalid, -main div input[type="url"]:invalid, -main div textarea:invalid { - border: 0.0625rem solid #dc322f; -} - -.conflict { - border: 0.0625rem solid #b58900 !important; -} - -main div button { - display: block; - padding: 0.5rem 1rem; - width: 75vw; - background-color: #073642; - color: #839496; - font-size: 1rem; - border: 0.0625rem solid #586e75; -} - -main div button:hover, -main div button:focus { - background-color: #002b36; - color: #93a1a1; - cursor: pointer; -} - -main div button:disabled { - background-color: #073642; - color: #586e75; - border: none; - cursor: default; -} - -main div div { - display: flex; - width: 75vw; -} - -main div div button { - width: auto; -} - -main div div input { - flex: 1; - padding: 0.5rem 1rem; - background-color: #073642; -} - -.mb1 { - margin-bottom: 2rem; -} - -.mb2 { - margin-bottom: 4rem; -} - -@media screen and (max-height: 720px) { - .mb1 { - margin-bottom: 1rem; - } - - .mb2 { - margin-bottom: 2rem; - } -} diff --git a/screenshot.png b/screenshot.png deleted file mode 100644 index d5a5a18..0000000 Binary files a/screenshot.png and /dev/null differ diff --git a/src/routes.rs b/src/routes.rs index 6acf90b..44a4ffc 100644 --- a/src/routes.rs +++ b/src/routes.rs @@ -219,39 +219,17 @@ macro_rules! random_id { #[cfg(feature = "dev")] lazy_static! { - static ref RESOURCES_DIR: PathBuf = { - let mut ressources_dir = PathBuf::new(); - ressources_dir.push(get_env!("CARGO_MANIFEST_DIR")); - ressources_dir.push("resources"); - ressources_dir - }; - static ref HTML_PATH: PathBuf = { - let mut html_path = RESOURCES_DIR.clone(); - html_path.push("index.html"); - html_path - }; - static ref JS_PATH: PathBuf = { - let mut js_path = RESOURCES_DIR.clone(); - js_path.push("script.js"); - js_path - }; - static ref CSS_PATH: PathBuf = { - let mut css_path = RESOURCES_DIR.clone(); - css_path.push("style.css"); - css_path + static ref INDEX_PATH: PathBuf = { + let mut index_path = PathBuf::new(); + index_path.push(get_env!("CARGO_MANIFEST_DIR")); + index_path.push("resources"); + index_path.push("index.html"); + index_path }; } #[cfg(not(feature = "dev"))] -lazy_static! { - static ref INDEX_CONTENTS: String = { - let html = include_str!("../resources/index.html"); - let js = include_str!("../resources/script.js"); - let css = include_str!("../resources/style.css"); - - html.replace("{{ js }}", js).replace("{{ css }}", css) - }; -} +static INDEX_CONTENTS: &str = include_str!("../resources/index.html"); static HIGHLIGHT_CONTENTS: &str = include_str!("../resources/highlight.html"); const HIGHLIGHT_LANGUAGE: &str = r#""#; @@ -269,15 +247,11 @@ pub async fn index( let contents = { #[cfg(feature = "dev")] { - let html = fs::read_to_string(&*HTML_PATH).expect("Can't read index.html"); - let js = fs::read_to_string(&*JS_PATH).expect("Can't read script.js"); - let css = fs::read_to_string(&*CSS_PATH).expect("Can't read style.css"); - - html.replace("{{ js }}", &js).replace("{{ css }}", &css) + fs::read_to_string(&*INDEX_PATH).expect("Can't read index.html") } #[cfg(not(feature = "dev"))] { - (&*INDEX_CONTENTS).clone() + INDEX_CONTENTS.to_owned() } }; @@ -670,7 +644,7 @@ pub mod texts { ) } - /// PUT a new text entry + /// POST a new text entry pub async fn post( request: HttpRequest, body: web::Json,