diff --git a/public/script.js b/public/script.js
index 281e61e..255e99f 100644
--- a/public/script.js
+++ b/public/script.js
@@ -1,140 +1,169 @@
+// By chatgpt becuase I hate frontend and javascript kill me
document.addEventListener("DOMContentLoaded", () => {
- const dropArea = document.getElementById("drop-area");
- const fileInput = document.getElementById("fileElem");
- const uploadStatus = document.getElementById("upload-status");
+ const dropArea = document.getElementById("drop-area");
+ const fileInput = document.getElementById("fileElem");
+ const uploadStatus = document.getElementById("upload-status");
- // Prevent default drag behaviors
- ["dragenter", "dragover", "dragleave", "drop"].forEach(eventName => {
- dropArea.addEventListener(eventName, preventDefaults, false);
- document.body.addEventListener(eventName, preventDefaults, false);
+ // Prevent default drag behaviors
+ ["dragenter", "dragover", "dragleave", "drop"].forEach((eventName) => {
+ dropArea.addEventListener(eventName, preventDefaults, false);
+ document.body.addEventListener(eventName, preventDefaults, false);
+ });
+
+ // Highlight drop area when item is dragged over
+ ["dragenter", "dragover"].forEach((eventName) => {
+ dropArea.addEventListener(eventName, highlight, false);
+ });
+
+ ["dragleave", "drop"].forEach((eventName) => {
+ dropArea.addEventListener(eventName, unhighlight, false);
+ });
+
+ // Handle dropped files
+ dropArea.addEventListener("drop", handleDrop, false);
+ dropArea.addEventListener("click", () => fileInput.click());
+
+ // Handle file selection
+ fileInput.addEventListener(
+ "change",
+ () => {
+ const files = fileInput.files;
+ handleFiles(files);
+ },
+ false
+ );
+
+ // Handle pasted files
+ document.addEventListener("paste", handlePaste, false);
+
+ function preventDefaults(e) {
+ e.preventDefault();
+ e.stopPropagation();
+ }
+
+ function highlight() {
+ dropArea.classList.add("highlight");
+ }
+
+ function unhighlight() {
+ dropArea.classList.remove("highlight");
+ }
+
+ function handleDrop(e) {
+ const dt = e.dataTransfer;
+ const files = dt.files;
+ handleFiles(files);
+ }
+
+ function handlePaste(e) {
+ const items = e.clipboardData.items;
+ for (let i = 0; i < items.length; i++) {
+ const item = items[i];
+ if (item.kind === "file") {
+ const file = item.getAsFile();
+ handleFiles([file]);
+ }
+ }
+ }
+
+ function handleFiles(files) {
+ if (files.length > 0) {
+ for (const file of files) {
+ uploadFile(file);
+ }
+ }
+ }
+
+ function uploadFile(file) {
+ const url = "upload"; // Replace with your upload URL
+ const xhr = new XMLHttpRequest();
+
+ // Create a new upload status container and link elements
+ const uploadContainer = document.createElement("div");
+ const statusLink = document.createElement("div");
+ const uploadText = document.createElement("span");
+ const buttons = document.createElement("div");
+ const copyButton = document.createElement("button");
+ const deleteButton = document.createElement("button");
+
+ uploadContainer.className = "upload-status"; // Use the existing CSS class for styling
+ uploadContainer.appendChild(uploadText);
+ uploadContainer.appendChild(statusLink);
+ buttons.appendChild(copyButton)
+ buttons.appendChild(deleteButton)
+ uploadContainer.appendChild(buttons)
+ uploadStatus.appendChild(uploadContainer);
+
+ // Update upload text
+ uploadText.innerHTML = "0%";
+ uploadText.className = "percent";
+ statusLink.className = "status";
+ copyButton.className = "copy-button"; // Add class for styling
+ copyButton.innerHTML = "Copiar"; // Set button text
+ deleteButton.className = "delete-button";
+ deleteButton.innerHTML = "Borrar";
+ copyButton.style.display = "none";
+ deleteButton.style.display = "none";
+
+ // Update progress text
+ xhr.upload.addEventListener("progress", (e) => {
+ if (e.lengthComputable) {
+ const percentComplete = Math.round((e.loaded / e.total) * 100);
+ uploadText.innerHTML = `${percentComplete}%`; // Update the text with the percentage
+ }
});
- // Highlight drop area when item is dragged over
- ["dragenter", "dragover"].forEach(eventName => {
- dropArea.addEventListener(eventName, highlight, false);
- });
+ xhr.onerror = () => {
+ console.error("Error:", xhr.status, xhr.statusText, xhr.responseText);
+ statusLink.textContent = "Error desconocido";
+ };
- ["dragleave", "drop"].forEach(eventName => {
- dropArea.addEventListener(eventName, unhighlight, false);
- });
-
- // Handle dropped files
- dropArea.addEventListener("drop", handleDrop, false);
- dropArea.addEventListener("click", () => fileInput.click());
-
- // Handle file selection
- fileInput.addEventListener("change", () => {
- const files = fileInput.files;
- handleFiles(files);
- }, false);
-
- // Handle pasted files
- document.addEventListener("paste", handlePaste, false);
-
- function preventDefaults(e) {
- e.preventDefault();
- e.stopPropagation();
- }
-
- function highlight() {
- dropArea.classList.add("highlight");
- }
-
- function unhighlight() {
- dropArea.classList.remove("highlight");
- }
-
- function handleDrop(e) {
- const dt = e.dataTransfer;
- const files = dt.files;
- handleFiles(files);
- }
-
- function handlePaste(e) {
- const items = e.clipboardData.items;
- for (let i = 0; i < items.length; i++) {
- const item = items[i];
- if (item.kind === "file") {
- const file = item.getAsFile();
- handleFiles([file]);
- }
+ xhr.onload = () => {
+ // console.log("Response Status:", xhr.status);
+ // console.log("Response Text:", xhr.responseText);
+ if (xhr.status === 200) {
+ try {
+ const response = JSON.parse(xhr.responseText);
+ const fileLink = response.link;
+ statusLink.innerHTML = `${fileLink}`;
+ copyButton.style.display = "inline";
+ copyButton.onclick = () => copyToClipboard(fileLink);
+ deleteButton.style.display = "inline";
+ deleteButton.onclick = () => {
+ window.open(response.deleteLink, "_blank");
+ };
+ } catch (error) {
+ statusLink.textContent =
+ "Error desconocido, habla con el administrador";
}
- }
-
- function handleFiles(files) {
- if (files.length > 0) {
- for (const file of files) {
- uploadFile(file);
- }
+ } else if (xhr.status >= 400 && xhr.status < 500) {
+ try {
+ const errorResponse = JSON.parse(xhr.responseText);
+ statusLink.textContent = errorResponse.error || "Error del cliente.";
+ } catch (e) {
+ statusLink.textContent = "Error del cliente.";
}
- }
+ } else {
+ statusLink.textContent = "Error del servidor.";
+ }
+ };
- function uploadFile(file) {
- const url = "upload"; // Replace with your upload URL
- const xhr = new XMLHttpRequest();
+ // Send file
+ const formData = new FormData();
+ formData.append("file", file);
+ xhr.open("POST", url, true);
+ xhr.send(formData);
+ }
- // Create a new upload status container and link elements
- const uploadContainer = document.createElement("div");
- const statusLink = document.createElement("div");
- const uploadText = document.createElement("span");
- const copyButton = document.createElement("button");
-
- uploadContainer.className = "upload-status"; // Use the existing CSS class for styling
- uploadContainer.appendChild(uploadText);
- uploadContainer.appendChild(statusLink);
- uploadContainer.appendChild(copyButton);
- uploadStatus.appendChild(uploadContainer); // Append to the main upload status container
-
- // Update upload text
- uploadText.innerHTML = "0%";
- uploadText.className = "percent"
- copyButton.className = "copy-button"; // Add class for styling
- copyButton.innerHTML = "Copiar"; // Set button text
- copyButton.style.display = "none"; // Hide initially
-
- // Update progress text
- xhr.upload.addEventListener("progress", (e) => {
- if (e.lengthComputable) {
- const percentComplete = Math.round((e.loaded / e.total) * 100);
- uploadText.innerHTML = `${percentComplete}%`; // Update the text with the percentage
- }
- });
-
- // Handle response
- xhr.onload = () => {
- if (xhr.status === 200) {
- try {
- const response = JSON.parse(xhr.responseText);
- const fileLink = response.link; // Assuming the response contains a key 'link'
- statusLink.innerHTML = `${fileLink}`;
- copyButton.style.display = "inline"; // Show the copy button
- copyButton.onclick = () => copyToClipboard(fileLink); // Set the copy action
- } catch (error) {
- statusLink.textContent = "File uploaded but failed to parse response.";
- }
- } else {
- statusLink.textContent = "File upload failed.";
- }
- };
-
- // Handle errors
- xhr.onerror = () => {
- statusLink.textContent = "An error occurred during the file upload.";
- };
-
- // Send file
- const formData = new FormData();
- formData.append("file", file);
- xhr.open("POST", url, true);
- xhr.send(formData);
- }
-
- // Function to copy the link to the clipboard
- function copyToClipboard(text) {
- navigator.clipboard.writeText(text).then(() => {
- // alert("Link copied to clipboard!"); // Notify the user
- }).catch(err => {
- console.error("Failed to copy: ", err);
- });
- }
+ // Function to copy the link to the clipboard
+ function copyToClipboard(text) {
+ navigator.clipboard
+ .writeText(text)
+ .then(() => {
+ // alert("Link copied to clipboard!"); // Notify the user
+ })
+ .catch((err) => {
+ console.error("Failed to copy: ", err);
+ });
+ }
});
diff --git a/public/styles.css b/public/styles.css
index c8fb455..46d7799 100644
--- a/public/styles.css
+++ b/public/styles.css
@@ -1,172 +1,228 @@
@font-face {
- font-family: "FG";
- font-weight: 500;
- src: url('framd.ttf');
+ font-family: "FG";
+ font-weight: 500;
+ src: url('framd.ttf');
}
@font-face {
- font-family: "FG";
- font-weight: 900;
- src: url('frahv.ttf');
+ font-family: "FG";
+ font-weight: 900;
+ src: url('frahv.ttf');
}
+
@font-face {
- font-family: "XFG";
- font-weight: 900;
- src: url('frahvmod.ttf');
+ font-family: "XFG";
+ font-weight: 900;
+ src: url('frahvmod.ttf');
}
html {
- font-family: "FG";
- background-image: linear-gradient(to bottom,
- rgba(11, 11, 11, 0.92),
- rgba(11, 11, 11, 0.92)),
- url(./bliss-small.avif);
- background-attachment: fixed;
+ font-family: "FG";
+ background-image: linear-gradient(to bottom,
+ rgba(11, 11, 11, 0.92),
+ rgba(11, 11, 11, 0.92)),
+ url(./bliss-small.avif);
+ background-attachment: fixed;
+ background-repeat: no-repeat;
+ background-size: cover;
}
+
body {
-/* font-family: Arial, sans-serif; */
-/* background-color: #111; */
- margin: 0;
- padding: 20px;
+ /* font-family: Arial, sans-serif; */
+ /* background-color: #111; */
+ margin: 0;
+ padding: 20px;
}
-p, h1, h2, h3, h4, h5 {
- color: aliceblue
+p,
+h1,
+h2,
+h3,
+h4,
+h5 {
+ color: aliceblue
}
h1 {
- font-family: "FG";
- font-weight: 200;
- max-width: 100%;
- overflow-wrap: break-word;
+ font-family: "FG";
+ font-weight: 200;
+ max-width: 100%;
+ overflow-wrap: break-word;
}
a {
- text-decoration: none;
+ text-decoration: none;
}
.bottom {
- font-size: 0.9em;
-/* margin-top: 1ch;*/
- flex: 1;
- text-align: center;
+ font-size: 0.9em;
+ /* margin-top: 1ch;*/
+ flex: 1;
+ text-align: center;
}
-.bottom > p {
- margin: 10px 0px;
+.bottom>p {
+ margin: 10px 0px;
}
.percent {
- color: aliceblue
+ color: aliceblue
}
.container {
- max-width: 700px;
- margin: auto;
-/* background: white; */
- /*! padding: 20px; */
- border-radius: 0px;
- /*! box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); */
+ max-width: 800px;
+ margin: auto;
+ /* background: white; */
+ /*! padding: 20px; */
+ border-radius: 0px;
+ /*! box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); */
}
#drop-area {
- /*! border: 2px solid #00ff00; */
- /*! border-radius: 6px; */
- /*! padding-left: 10px; */
- /*! padding-right: 10px; */
- text-align: center;
- position: relative;
- width: fit-content;
- margin: 0 auto; /* Center the element */
- display: block; /* Ensure it behaves as a block-level element */
- background: rgba(202,230,190,.75);
- border: 1px solid #b7d1a0;
- border-radius: 4px;
- color: #468847;
- cursor: pointer;
- /*! display: inline-block; */
- font-size: 24px;
- padding: 28px 48px;
- text-shadow: 0 1px hsla(0,0%,100%,.5);
- transition: background-color .25s,width .5s,height .5s;
+ /*! border: 2px solid #00ff00; */
+ /*! border-radius: 6px; */
+ /*! padding-left: 10px; */
+ /*! padding-right: 10px; */
+ text-align: center;
+ position: relative;
+ width: fit-content;
+ margin: 0 auto;
+ /* Center the element */
+ display: block;
+ /* Ensure it behaves as a block-level element */
+ background: rgba(202, 230, 190, .75);
+ border: 1px solid #b7d1a0;
+ border-radius: 4px;
+ color: #468847;
+ cursor: pointer;
+ /*! display: inline-block; */
+ font-size: 24px;
+ padding: 28px 48px;
+ text-shadow: 0 1px hsla(0, 0%, 100%, .5);
+ transition: background-color .25s, width .5s, height .5s;
}
.button {
- display: inline-block;
- padding: 10px 20px;
-/* background: #; */
- color: white;
- border-radius: 5px;
- cursor: pointer;
-/* margin-top: 10px; */
+ display: inline-block;
+ padding: 10px 20px;
+ /* background: #; */
+ color: white;
+ border-radius: 5px;
+ cursor: pointer;
+ /* margin-top: 10px; */
}
.upload-status {
- margin-top: 10px;
+ margin-top: 10px;
}
-nav a, nav > ul
-{
- list-style: none;
- margin: 0;
- padding: 0;
- text-align: center;
+nav a,
+nav>ul {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ text-align: center;
}
#upload-status {
- margin: 20px; /* Adjust as needed */
+ margin: 20px;
+ /* Adjust as needed */
}
.upload-status {
- display: flex;
- align-items: center;
- justify-content: space-between;
- border: 2px solid #999; /* Optional styling for the status box */
- padding: 5px; /* Optional padding */
- /*! border-radius: 6px; */ /* Optional rounded corners */
- /*! background-color: #f9f9f9; */ /* Optional background color */
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ border: 2px solid #999;
+ /* Optional styling for the status box */
+ padding: 5px;
+ /* Optional padding */
+ /*! border-radius: 6px; */
+ /* Optional rounded corners */
+ /*! background-color: #f9f9f9; */
+ /* Optional background color */
}
.link-container {
- display: flex;
- align-items: center;
- margin-left: auto; /* Pushes the link and button to the right */
+ display: flex;
+ align-items: center;
+ margin-left: auto;
+ /* Pushes the link and button to the right */
}
.link {
- color: #ffb6c1;
- text-decoration: none; /* Remove underline from link */
- margin-right: 5px; /* Space between link and button */
+ color: #ffb6c1;
+ text-decoration: none;
+ /* Remove underline from link */
+ margin-right: 5px;
+ /* Space between link and button */
}
.link:hover {
- text-decoration: underline; /* Optional: underline on hover */
+ text-decoration: underline;
+ /* Optional: underline on hover */
}
.copy-button {
- display: inline;
- background-color: #5e5e5e; /* Button background color */
- color: white; /* Button text color */
- border: none; /* Remove border */
- border-radius: 3px; /* Rounded corners for the button */
- padding: 5px 10px; /* Button padding */
- cursor: pointer; /* Pointer cursor on hover */
+ display: inline;
+ background-color: #7a6fff;
+ /* Button background color */
+ color: white;
+ /* Button text color */
+ border: none;
+ /* Remove border */
+ border-radius: 3px;
+ /* Rounded corners for the button */
+ padding: 5px 10px;
+ /* Button padding */
+ cursor: pointer;
+ /* Pointer cursor on hover */
+ font-weight: bold;
}
+.delete-button {
+ display: inline;
+ background-color: #ff6f6f;
+ /* Button background color */
+ color: white;
+ /* Button text color */
+ border: none;
+ /* Remove border */
+ border-radius: 3px;
+ /* Rounded corners for the button */
+ padding: 5px 10px;
+ /* Button padding */
+ cursor: pointer;
+ /* Pointer cursor on hover */
+ margin-left: 6px;
+ font-weight: bold;
+}
+
+
.copy-button:hover {
- background-color: #404040; /* Darker shade on hover */
+ background-color: #6057ce;
+ /* Darker shade on hover */
+}
+
+.delete-button:hover {
+ background-color: #ce5757;
+ /* Darker shade on hover */
+}
+
+.status {
+ color: rgb(255, 132, 0);
}
a:link {
- color: #ffb6c1
+ color: #ffb6c1
}
a:visited {
- color: #ffb6c1
+ color: #ffb6c1
}
a:hover {
- color: #ffb6c1
+ color: #ffb6c1
}
\ No newline at end of file
diff --git a/src/handling/admin.cr b/src/handling/admin.cr
index 0035f53..e3ad075 100644
--- a/src/handling/admin.cr
+++ b/src/handling/admin.cr
@@ -56,7 +56,7 @@ module Handling::Admin
# Delete entry from db
SQL.exec "DELETE FROM #{CONFIG.ipTableName} WHERE ip = ?", ip
LOGGER.debug "Rate limit for '#{ip}' was deleted"
- successfull_ips << ip
+ successfull_ips << ip
rescue ex : DB::NoResultsError
LOGGER.error("Rate limit for '#{ip}' doesn't exist or is not registered in the database: #{ex.message}")
failed_ips << ip
diff --git a/src/jobs.cr b/src/jobs.cr
index 15cac1f..f24a5f9 100644
--- a/src/jobs.cr
+++ b/src/jobs.cr
@@ -20,6 +20,8 @@ module Jobs
spawn do
loop do
Utils.retrieve_tor_exit_nodes
+ # Updates the @@exit_nodes array instantly
+ Routing.reload_exit_nodes
sleep CONFIG.torExitNodesCheck
end
end
@@ -28,9 +30,7 @@ module Jobs
def self.kemal
spawn do
if !CONFIG.unix_socket.nil?
- Kemal.run do |config|
- config.server.not_nil!.bind_unix "#{CONFIG.unix_socket}"
- end
+ Kemal.run &.server.not_nil!.bind_unix "#{CONFIG.unix_socket}"
else
Kemal.run
end
diff --git a/src/routing.cr b/src/routing.cr
index 9211b43..6e7b2d8 100644
--- a/src/routing.cr
+++ b/src/routing.cr
@@ -3,19 +3,10 @@ require "./http-errors"
module Routing
extend self
@@exit_nodes = Array(String).new
- if CONFIG.blockTorAddresses
- spawn do
- # Wait a little for Utils.retrieve_tor_exit_nodes to execute first
- # or it will load an old exit node list
- # I think this can be replaced by channels which makes me able to
- # receive data from fibers
- sleep 5
- loop do
- LOGGER.debug "Updating Tor exit nodes array"
- @@exit_nodes = Utils.load_tor_exit_nodes
- sleep CONFIG.torExitNodesCheck + 5
- end
- end
+
+ def reload_exit_nodes
+ LOGGER.debug "Updating Tor exit nodes array"
+ @@exit_nodes = Utils.load_tor_exit_nodes
end
before_post "/api/admin/*" do |env|
@@ -86,20 +77,21 @@ module Routing
Handling.sharex_config(env)
end
- self.register_admin
+ if CONFIG.adminEnabled
+ self.register_admin
+ end
end
def register_admin
- if CONFIG.adminEnabled
- # post "/api/admin/upload" do |env|
- # Handling::Admin.delete_ip_limit(env)
- # end
- post "/api/admin/delete" do |env|
- Handling::Admin.delete_file(env)
- end
- end
- post "/api/admin/deleteiplimit" do |env|
- Handling::Admin.delete_ip_limit(env)
+ # post "/api/admin/upload" do |env|
+ # Handling::Admin.delete_ip_limit(env)
+ # end
+ post "/api/admin/delete" do |env|
+ Handling::Admin.delete_file(env)
end
end
+
+ post "/api/admin/deleteiplimit" do |env|
+ Handling::Admin.delete_ip_limit(env)
+ end
end
diff --git a/src/utils.cr b/src/utils.cr
index 7f22cf8..879412a 100644
--- a/src/utils.cr
+++ b/src/utils.cr
@@ -135,7 +135,8 @@ module Utils
end
end
- # Delete socket if the server has not been previously cleaned by the server (Due to unclean exits, crashes, etc.)
+ # Delete socket if the server has not been previously cleaned by the server
+ # (Due to unclean exits, crashes, etc.)
def delete_socket
if File.exists?("#{CONFIG.unix_socket}")
LOGGER.info "Deleting old unix socket"
@@ -167,21 +168,43 @@ module Utils
msg("File '#{fileinfo[:filename]}' deleted successfully")
end
+ MAGIC_BYTES = {
+ # Images
+ ".png" => "89504e470d0a1a0a",
+ ".heic" => "6674797068656963",
+ ".jpg" => "ffd8ff",
+ ".gif" => "474946383",
+ # Videos
+ ".mp4" => "66747970",
+ ".webm" => "1a45dfa3",
+ ".mov" => "6d6f6f76",
+ ".wmv" => "3026b2758e66cf11",
+ ".flv" => "464c5601",
+ ".mpeg" => "000001bx",
+ # Audio
+ ".mp3" => "494433",
+ ".aac" => "fff1",
+ ".wav" => "57415645666d7420",
+ ".flac" => "664c614300000022",
+ ".ogg" => "4f67675300020000000000000000",
+ ".wma" => "3026b2758e66cf11a6d900aa0062ce6c",
+ ".aiff" => "464f524d00",
+ # Whatever
+ ".7z" => "377abcaf271c",
+ ".gz" => "1f8b",
+ ".iso" => "4344303031",
+ # Documents
+ "pdf" => "25504446",
+ "html" => "",
+ }
+
def detect_extension(file) : String
- magic_bytes = {
- ".png" => "89504e470d0a1a0a",
- ".jpg" => "ffd8ff",
- ".webm" => "1a45dfa3",
- ".mp4" => "66747970",
- ".gif" => "474946383",
- ".7z" => "377abcaf271c",
- ".gz" => "1f8b",
- }
file = File.open(file)
- slice = Bytes.new(8)
+ slice = Bytes.new(16)
hex = IO::Hexdump.new(file)
+ # Reads the first 16 bytes of the file in Heap
hex.read(slice)
- magic_bytes.each do |ext, mb|
+ MAGIC_BYTES.each do |ext, mb|
if slice.hexstring.includes?(mb)
return ext
end
@@ -191,17 +214,21 @@ module Utils
def retrieve_tor_exit_nodes
LOGGER.debug "Retrieving Tor exit nodes list"
- resp = HTTP::Client.get(CONFIG.torExitNodesUrl) do |res|
- if res.success? && res.status_code == 200
- begin
- File.open(CONFIG.torExitNodesFile, "w") do |output|
- IO.copy(res.body_io, output)
+ HTTP::Client.get(CONFIG.torExitNodesUrl) do |res|
+ begin
+ if res.success? && res.status_code == 200
+ begin
+ File.open(CONFIG.torExitNodesFile, "w") { |output| IO.copy(res.body_io, output) }
+ rescue ex
+ LOGGER.error "Failed to write to file: #{ex.message}"
end
- rescue ex
- LOGGER.error "Failed to write to file: #{ex.message}"
+ else
+ LOGGER.error "Failed to retrieve exit nodes list. Status Code: #{res.status_code}"
end
- else
- LOGGER.error "Failed to retrieve exit nodes list. Status Code: #{res.status_code}"
+ rescue ex : Socket::ConnectError
+ LOGGER.error "Failed to connect to #{CONFIG.torExitNodesUrl}: #{ex.message}"
+ rescue ex
+ LOGGER.error "Unknown error: #{ex.message}"
end
end
end
@@ -227,7 +254,7 @@ module Utils
end
def host(env) : String
- begin
+ begin
return env.request.headers.try &.["X-Forwarded-Host"]
rescue
return env.request.headers["Host"]