Compare commits

...

1 commit
main ... backup

Author SHA1 Message Date
c049642ffb
0.9.1.1: Save progress
Some checks failed
File-uploader-crystal CI / build (push) Has been cancelled
2024-09-12 23:40:52 -03:00
7 changed files with 83 additions and 20 deletions

View file

@ -85,4 +85,5 @@ WantedBy=default.target
- Small CLI to upload files (like `rpaste` from rustypaste)
- Add more endpoints to Admin API
-
- Image filters https://github.com/HaschekSolutions/pictshare/blob/master/rtfm/IMAGEFILTERS.md using imagemagick or ffmpeg
- Strip exif

View file

@ -1,6 +1,7 @@
require "../http-errors"
require "http/client"
require "benchmark"
require "../filters"
module Handling
extend self
@ -10,6 +11,7 @@ module Handling
ip_address = Utils.ip_address(env)
protocol = Utils.protocol(env)
host = Utils.host(env)
filter = env.params.query["filter"]?
# You can modify this if you want to allow files smaller than 1MiB.
# This is generally a good way to check the filesize but there is a better way to do it
# which is inspecting the file directly (If I'm not wrong).
@ -46,6 +48,10 @@ module Handling
original_filename = upload.filename
uploaded_at = Time.utc
checksum = Utils.hash_file(file_path)
# Applies filter
if filter
Filters.apply_filter(file_path, filter)
end
end
# X-Forwarded-For if behind a reverse proxy and the header is set in the reverse
# proxy configuration.
@ -88,13 +94,19 @@ module Handling
ip_address = Utils.ip_address(env)
protocol = Utils.protocol(env)
host = Utils.host(env)
files = env.params.json["files"].as((Array(JSON::Any)))
begin
files = env.params.json["files"].as((Array(JSON::Any)))
rescue ex : JSON::ParseException
LOGGER.error "Body malformed: #{ex.message}"
return error400 "Body malformed: #{ex.message}"
rescue ex
LOGGER.error "Unknown error: #{ex.message}"
return error500 "Unknown error"
end
successfull_files = [] of NamedTuple(filename: String, extension: String, original_filename: String, checksum: String, delete_key: String | Nil)
failed_files = [] of String
# X-Forwarded-For if behind a reverse proxy and the header is set in the reverse
# proxy configuration.
if files.empty?
end
files.each do |url|
url = url.to_s
filename = Utils.generate_filename
@ -103,7 +115,9 @@ module Handling
checksum = ""
uploaded_at = Time.utc
extension = File.extname(URI.parse(url).path)
delete_key = nil
if CONFIG.deleteKeyLength > 0
delete_key = Random.base58(CONFIG.deleteKeyLength)
end
file_path = ::File.join ["#{CONFIG.files}", filename + extension]
File.open(file_path, "w") do |output|
begin
@ -168,7 +182,7 @@ module Handling
json
end
# TODO: Add delete url, same for upload_url_bulk
# TODO: If the user
def upload_url(env)
env.response.content_type = "application/json"
ip_address = Utils.ip_address(env)
@ -179,31 +193,29 @@ module Handling
failed_files = [] of String
# X-Forwarded-For if behind a reverse proxy and the header is set in the reverse
# proxy configuration.
if url.empty?
end
# files.each do |url|
url = url.to_s
filename = Utils.generate_filename
original_filename = ""
extension = ""
checksum = ""
uploaded_at = Time.utc
extension = File.extname(URI.parse(url).path)
delete_key = nil
if CONFIG.deleteKeyLength > 0
delete_key = Random.base58(CONFIG.deleteKeyLength)
end
file_path = ::File.join ["#{CONFIG.files}", filename + extension]
File.open(file_path, "w") do |output|
begin
# TODO: Connect timeout to prevent possible Denial of Service spamming requests
# https://crystal-lang.org/api/1.13.2/HTTP/Client.html#connect_timeout
HTTP::Client.get(url) do |res|
IO.copy(res.body_io, output)
end
rescue ex
LOGGER.debug "Failed to download file '#{url}': #{ex.message}"
return error403("Failed to download file '#{url}'")
return error403("Failed to download file '#{url}': #{ex.message}")
failed_files << url
end
end
# successfull_files << url
# end
if extension.empty?
extension = Utils.detect_extension(file_path)
File.rename(file_path, file_path + extension)
@ -231,7 +243,6 @@ module Handling
LOGGER.error "An error ocurred when trying to insert the data into the DB: #{ex.message}"
return error500("An error ocurred when trying to insert the data into the DB")
end
# end
json = JSON.build do |j|
j.array do
successfull_files.each do |fileinfo|
@ -383,4 +394,17 @@ module Handling
"ErrorMessage": "{json:error}"
})
end
def chatterino_config(env)
host = Utils.host(env)
protocol = Utils.protocol(env)
env.response.content_type = "application/json"
return %({
"requestUrl": "#{protocol}://#{host}/upload",
"formField": "data",
"imageLink": "{link}",
"deleteLink": "{deleteLink}"
})
end
end

View file

@ -1,3 +1,10 @@
macro error400(message)
env.response.content_type = "application/json"
env.response.status_code = 400
error_message = {"error" => {{message}}}.to_json
error_message
end
macro error401(message)
env.response.content_type = "application/json"
env.response.status_code = 401

View file

@ -50,6 +50,13 @@ module Routing
render "src/views/index.ecr"
end
get "/chatterino" do |env|
host = Utils.host(env)
protocol = Utils.protocol(env)
files_hosted = SQL.query_one "SELECT COUNT (filename) FROM #{CONFIG.dbTableName}", as: Int32
render "src/views/chatterino.ecr"
end
post "/upload" do |env|
Handling.upload(env)
end
@ -82,6 +89,10 @@ module Routing
Handling.sharex_config(env)
end
get "/chatterinoconfig" do |env|
Handling.chatterino_config(env)
end
if CONFIG.adminEnabled
self.register_admin
end

View file

@ -30,7 +30,7 @@ module Utils
end
def create_thumbnails_dir
if !CONFIG.thumbnails
if CONFIG.thumbnails
if !Dir.exists?("#{CONFIG.thumbnails}")
LOGGER.info "Creating thumbnails folder under '#{CONFIG.thumbnails}'"
begin
@ -69,7 +69,7 @@ module Utils
dependencies.each do |dep|
next if !CONFIG.generateThumbnails
if !Process.find_executable(dep)
LOGGER.fatal("'#{dep}' was not found")
LOGGER.fatal("'#{dep}' was not found, this is necessary to")
exit(1)
end
end
@ -113,7 +113,7 @@ module Utils
def generate_thumbnail(filename, extension)
# Disable generation if false
return if !CONFIG.generateThumbnails
return if !CONFIG.generateThumbnails || !CONFIG.thumbnails
LOGGER.debug "Generating thumbnail for #{filename + extension} in background"
process = Process.run("ffmpeg",
[
@ -128,7 +128,7 @@ module Utils
"-update", "1",
"#{CONFIG.thumbnails}/#{filename}.jpg",
])
if process.normal_exit?
if process.exit_code == 0
LOGGER.debug "Thumbnail for #{filename + extension} generated successfully"
SQL.exec "UPDATE #{CONFIG.dbTableName} SET thumbnail = ? WHERE filename = ?", filename + ".jpg", filename
else

20
src/views/chatterino.ecr Normal file
View file

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title> <%= host %> </title>
<link rel="stylesheet" href="styles.css">
<link rel="icon" href="./favicon.gif" type="image/gif" />
<script src="script.js"></script>
</head>
<body>
<div class="container">
<h1 style="font-size: 68px; text-align: center; margin: 20px;">Chatterino config</h1>
<p>Request URL: <a style="color: #2cca00"><%= protocol %>://<%= host %>/upload</a></p>
<p>Form field: <a style="color: #2cca00">data</a></p>
<p>Image link: <a style="color: #2cca00">link</a></p>
<p>Delete link: <a style="color: #2cca00">deleteLink</a></p>
</div>
</body>
</html>

View file

@ -22,7 +22,7 @@
<div>
<div style="text-align:center;">
<p>
<a href='./chatterino.png'>Chatterino Config</a> |
<a href='./chatterino'>Chatterino Config</a> |
<a href='./sharex.sxcu'>ShareX Config</a> |
<a href='https://codeberg.org/Fijxu/file-uploader-crystal'>
file-uploader-crystal (BETA <%= CURRENT_TAG %> - <%= CURRENT_VERSION %> @ <%= CURRENT_BRANCH %>)