0.9.1.1: Save progress
Some checks failed
File-uploader-crystal CI / build (push) Has been cancelled
Some checks failed
File-uploader-crystal CI / build (push) Has been cancelled
This commit is contained in:
parent
4803700cab
commit
c049642ffb
7 changed files with 83 additions and 20 deletions
|
@ -85,4 +85,5 @@ WantedBy=default.target
|
||||||
- Small CLI to upload files (like `rpaste` from rustypaste)
|
- Small CLI to upload files (like `rpaste` from rustypaste)
|
||||||
- Add more endpoints to Admin API
|
- Add more endpoints to Admin API
|
||||||
|
|
||||||
-
|
- Image filters https://github.com/HaschekSolutions/pictshare/blob/master/rtfm/IMAGEFILTERS.md using imagemagick or ffmpeg
|
||||||
|
- Strip exif
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
require "../http-errors"
|
require "../http-errors"
|
||||||
require "http/client"
|
require "http/client"
|
||||||
require "benchmark"
|
require "benchmark"
|
||||||
|
require "../filters"
|
||||||
|
|
||||||
module Handling
|
module Handling
|
||||||
extend self
|
extend self
|
||||||
|
@ -10,6 +11,7 @@ module Handling
|
||||||
ip_address = Utils.ip_address(env)
|
ip_address = Utils.ip_address(env)
|
||||||
protocol = Utils.protocol(env)
|
protocol = Utils.protocol(env)
|
||||||
host = Utils.host(env)
|
host = Utils.host(env)
|
||||||
|
filter = env.params.query["filter"]?
|
||||||
# You can modify this if you want to allow files smaller than 1MiB.
|
# 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
|
# 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).
|
# which is inspecting the file directly (If I'm not wrong).
|
||||||
|
@ -46,6 +48,10 @@ module Handling
|
||||||
original_filename = upload.filename
|
original_filename = upload.filename
|
||||||
uploaded_at = Time.utc
|
uploaded_at = Time.utc
|
||||||
checksum = Utils.hash_file(file_path)
|
checksum = Utils.hash_file(file_path)
|
||||||
|
# Applies filter
|
||||||
|
if filter
|
||||||
|
Filters.apply_filter(file_path, filter)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
# X-Forwarded-For if behind a reverse proxy and the header is set in the reverse
|
# X-Forwarded-For if behind a reverse proxy and the header is set in the reverse
|
||||||
# proxy configuration.
|
# proxy configuration.
|
||||||
|
@ -88,13 +94,19 @@ module Handling
|
||||||
ip_address = Utils.ip_address(env)
|
ip_address = Utils.ip_address(env)
|
||||||
protocol = Utils.protocol(env)
|
protocol = Utils.protocol(env)
|
||||||
host = Utils.host(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)
|
successfull_files = [] of NamedTuple(filename: String, extension: String, original_filename: String, checksum: String, delete_key: String | Nil)
|
||||||
failed_files = [] of String
|
failed_files = [] of String
|
||||||
# X-Forwarded-For if behind a reverse proxy and the header is set in the reverse
|
# X-Forwarded-For if behind a reverse proxy and the header is set in the reverse
|
||||||
# proxy configuration.
|
# proxy configuration.
|
||||||
if files.empty?
|
|
||||||
end
|
|
||||||
files.each do |url|
|
files.each do |url|
|
||||||
url = url.to_s
|
url = url.to_s
|
||||||
filename = Utils.generate_filename
|
filename = Utils.generate_filename
|
||||||
|
@ -103,7 +115,9 @@ module Handling
|
||||||
checksum = ""
|
checksum = ""
|
||||||
uploaded_at = Time.utc
|
uploaded_at = Time.utc
|
||||||
extension = File.extname(URI.parse(url).path)
|
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_path = ::File.join ["#{CONFIG.files}", filename + extension]
|
||||||
File.open(file_path, "w") do |output|
|
File.open(file_path, "w") do |output|
|
||||||
begin
|
begin
|
||||||
|
@ -168,7 +182,7 @@ module Handling
|
||||||
json
|
json
|
||||||
end
|
end
|
||||||
|
|
||||||
# TODO: Add delete url, same for upload_url_bulk
|
# TODO: If the user
|
||||||
def upload_url(env)
|
def upload_url(env)
|
||||||
env.response.content_type = "application/json"
|
env.response.content_type = "application/json"
|
||||||
ip_address = Utils.ip_address(env)
|
ip_address = Utils.ip_address(env)
|
||||||
|
@ -179,31 +193,29 @@ module Handling
|
||||||
failed_files = [] of String
|
failed_files = [] of String
|
||||||
# X-Forwarded-For if behind a reverse proxy and the header is set in the reverse
|
# X-Forwarded-For if behind a reverse proxy and the header is set in the reverse
|
||||||
# proxy configuration.
|
# proxy configuration.
|
||||||
if url.empty?
|
|
||||||
end
|
|
||||||
# files.each do |url|
|
|
||||||
url = url.to_s
|
|
||||||
filename = Utils.generate_filename
|
filename = Utils.generate_filename
|
||||||
original_filename = ""
|
original_filename = ""
|
||||||
extension = ""
|
extension = ""
|
||||||
checksum = ""
|
checksum = ""
|
||||||
uploaded_at = Time.utc
|
uploaded_at = Time.utc
|
||||||
extension = File.extname(URI.parse(url).path)
|
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_path = ::File.join ["#{CONFIG.files}", filename + extension]
|
||||||
File.open(file_path, "w") do |output|
|
File.open(file_path, "w") do |output|
|
||||||
begin
|
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|
|
HTTP::Client.get(url) do |res|
|
||||||
IO.copy(res.body_io, output)
|
IO.copy(res.body_io, output)
|
||||||
end
|
end
|
||||||
rescue ex
|
rescue ex
|
||||||
LOGGER.debug "Failed to download file '#{url}': #{ex.message}"
|
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
|
failed_files << url
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
# successfull_files << url
|
|
||||||
# end
|
|
||||||
if extension.empty?
|
if extension.empty?
|
||||||
extension = Utils.detect_extension(file_path)
|
extension = Utils.detect_extension(file_path)
|
||||||
File.rename(file_path, file_path + extension)
|
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}"
|
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")
|
return error500("An error ocurred when trying to insert the data into the DB")
|
||||||
end
|
end
|
||||||
# end
|
|
||||||
json = JSON.build do |j|
|
json = JSON.build do |j|
|
||||||
j.array do
|
j.array do
|
||||||
successfull_files.each do |fileinfo|
|
successfull_files.each do |fileinfo|
|
||||||
|
@ -383,4 +394,17 @@ module Handling
|
||||||
"ErrorMessage": "{json:error}"
|
"ErrorMessage": "{json:error}"
|
||||||
})
|
})
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
|
|
|
@ -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)
|
macro error401(message)
|
||||||
env.response.content_type = "application/json"
|
env.response.content_type = "application/json"
|
||||||
env.response.status_code = 401
|
env.response.status_code = 401
|
||||||
|
|
|
@ -50,6 +50,13 @@ module Routing
|
||||||
render "src/views/index.ecr"
|
render "src/views/index.ecr"
|
||||||
end
|
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|
|
post "/upload" do |env|
|
||||||
Handling.upload(env)
|
Handling.upload(env)
|
||||||
end
|
end
|
||||||
|
@ -82,6 +89,10 @@ module Routing
|
||||||
Handling.sharex_config(env)
|
Handling.sharex_config(env)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
get "/chatterinoconfig" do |env|
|
||||||
|
Handling.chatterino_config(env)
|
||||||
|
end
|
||||||
|
|
||||||
if CONFIG.adminEnabled
|
if CONFIG.adminEnabled
|
||||||
self.register_admin
|
self.register_admin
|
||||||
end
|
end
|
||||||
|
|
|
@ -30,7 +30,7 @@ module Utils
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_thumbnails_dir
|
def create_thumbnails_dir
|
||||||
if !CONFIG.thumbnails
|
if CONFIG.thumbnails
|
||||||
if !Dir.exists?("#{CONFIG.thumbnails}")
|
if !Dir.exists?("#{CONFIG.thumbnails}")
|
||||||
LOGGER.info "Creating thumbnails folder under '#{CONFIG.thumbnails}'"
|
LOGGER.info "Creating thumbnails folder under '#{CONFIG.thumbnails}'"
|
||||||
begin
|
begin
|
||||||
|
@ -69,7 +69,7 @@ module Utils
|
||||||
dependencies.each do |dep|
|
dependencies.each do |dep|
|
||||||
next if !CONFIG.generateThumbnails
|
next if !CONFIG.generateThumbnails
|
||||||
if !Process.find_executable(dep)
|
if !Process.find_executable(dep)
|
||||||
LOGGER.fatal("'#{dep}' was not found")
|
LOGGER.fatal("'#{dep}' was not found, this is necessary to")
|
||||||
exit(1)
|
exit(1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -113,7 +113,7 @@ module Utils
|
||||||
|
|
||||||
def generate_thumbnail(filename, extension)
|
def generate_thumbnail(filename, extension)
|
||||||
# Disable generation if false
|
# Disable generation if false
|
||||||
return if !CONFIG.generateThumbnails
|
return if !CONFIG.generateThumbnails || !CONFIG.thumbnails
|
||||||
LOGGER.debug "Generating thumbnail for #{filename + extension} in background"
|
LOGGER.debug "Generating thumbnail for #{filename + extension} in background"
|
||||||
process = Process.run("ffmpeg",
|
process = Process.run("ffmpeg",
|
||||||
[
|
[
|
||||||
|
@ -128,7 +128,7 @@ module Utils
|
||||||
"-update", "1",
|
"-update", "1",
|
||||||
"#{CONFIG.thumbnails}/#{filename}.jpg",
|
"#{CONFIG.thumbnails}/#{filename}.jpg",
|
||||||
])
|
])
|
||||||
if process.normal_exit?
|
if process.exit_code == 0
|
||||||
LOGGER.debug "Thumbnail for #{filename + extension} generated successfully"
|
LOGGER.debug "Thumbnail for #{filename + extension} generated successfully"
|
||||||
SQL.exec "UPDATE #{CONFIG.dbTableName} SET thumbnail = ? WHERE filename = ?", filename + ".jpg", filename
|
SQL.exec "UPDATE #{CONFIG.dbTableName} SET thumbnail = ? WHERE filename = ?", filename + ".jpg", filename
|
||||||
else
|
else
|
||||||
|
|
20
src/views/chatterino.ecr
Normal file
20
src/views/chatterino.ecr
Normal 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>
|
|
@ -22,7 +22,7 @@
|
||||||
<div>
|
<div>
|
||||||
<div style="text-align:center;">
|
<div style="text-align:center;">
|
||||||
<p>
|
<p>
|
||||||
<a href='./chatterino.png'>Chatterino Config</a> |
|
<a href='./chatterino'>Chatterino Config</a> |
|
||||||
<a href='./sharex.sxcu'>ShareX Config</a> |
|
<a href='./sharex.sxcu'>ShareX Config</a> |
|
||||||
<a href='https://codeberg.org/Fijxu/file-uploader-crystal'>
|
<a href='https://codeberg.org/Fijxu/file-uploader-crystal'>
|
||||||
file-uploader-crystal (BETA <%= CURRENT_TAG %> - <%= CURRENT_VERSION %> @ <%= CURRENT_BRANCH %>)
|
file-uploader-crystal (BETA <%= CURRENT_TAG %> - <%= CURRENT_VERSION %> @ <%= CURRENT_BRANCH %>)
|
||||||
|
|
Loading…
Reference in a new issue