0.8.2: Tor exit node blocking.
This commit is contained in:
parent
3173a36a99
commit
9cd9bc689a
13 changed files with 149 additions and 348 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -3,3 +3,5 @@
|
||||||
/bin/
|
/bin/
|
||||||
/.shards/
|
/.shards/
|
||||||
*.dwarf
|
*.dwarf
|
||||||
|
data
|
||||||
|
torexitnodes.txt
|
||||||
|
|
|
@ -74,9 +74,11 @@ WantedBy=default.target
|
||||||
- ~~Add file size limit~~ ADDED
|
- ~~Add file size limit~~ ADDED
|
||||||
- ~~Fix error when accessing `http://127.0.0.1:8080` with an empty DB.~~ Fixed somehow.
|
- ~~Fix error when accessing `http://127.0.0.1:8080` with an empty DB.~~ Fixed somehow.
|
||||||
- Better frontend...
|
- Better frontend...
|
||||||
- ~~Disable file deletion if `delete_files_after_check_seconds` or `delete_files_after` is set to `0`~~ DONE
|
- ~~Disable file deletion if `deleteFilesCheck` or `deleteFilesAfter` is set to `0`~~ DONE
|
||||||
- ~~Disable delete key if `delete_key_length` is `0`~~ DONE (But I think there is a better way to do it)
|
- ~~Disable delete key if `deleteKeyLength` is `0`~~ DONE (But I think there is a better way to do it)
|
||||||
- ~~Exit if `filename_length` is `0`~~ DONE
|
- ~~Exit if `fileameLength` is `0`~~ DONE
|
||||||
- ~~Disable file limit if `size_limit` is `0`~~ DONE
|
- ~~Disable file limit if `size_limit` is `0`~~ DONE
|
||||||
- ~~Prevent files from being overwritten in the event of a name collision~~ DONE
|
- ~~Prevent files from being overwritten in the event of a name collision~~ DONE
|
||||||
- Dockerfile and Docker image (Crystal doesn't has dependency hell like other languages so is not really necessary to do, but useful for people that want instant deploy)
|
- Dockerfile and Docker image (Crystal doesn't has dependency hell like other languages so is not really necessary to do, but useful for people that want instant deploy)
|
||||||
|
- Custom file expiration using headers (Like rustypaste)
|
||||||
|
- Small CLI to upload files (like `rpaste` from rustypaste)
|
||||||
|
|
|
@ -1,30 +1,36 @@
|
||||||
files: "./files"
|
files: "./files"
|
||||||
thumbnails: "./thumbnails"
|
thumbnails: "./thumbnails"
|
||||||
generate_thumbnails: false
|
generateThumbnails: false
|
||||||
db: "./db.sqlite3"
|
db: "./db.sqlite3"
|
||||||
db_table_name: "files"
|
dbTableName: "files"
|
||||||
adminEnabled: false
|
adminEnabled: false
|
||||||
adminApiKey: "asd"
|
adminApiKey: "asd"
|
||||||
filename_length: 3
|
fileameLength: 3
|
||||||
# In MiB
|
# In MiB
|
||||||
size_limit: 512
|
size_limit: 512
|
||||||
port: 8080
|
port: 8080
|
||||||
|
blockTorAddresses: true
|
||||||
|
# Every hour
|
||||||
|
torExitNodesCheck: 3600
|
||||||
|
torExitNodesUrl: "https://www.dan.me.uk/torlist/?exit"
|
||||||
|
torExitNodesFile: "./torexitnodes.txt"
|
||||||
|
torMessage: "TOR IS BLOCKED!"
|
||||||
# If you define the unix socket, it will only listen on the socket and not the port.
|
# If you define the unix socket, it will only listen on the socket and not the port.
|
||||||
#unix_socket: "/tmp/file-uploader.sock"
|
#unix_socket: "/tmp/file-uploader.sock"
|
||||||
# In days
|
# In days
|
||||||
delete_files_after: 7
|
deleteFilesAfter: 7
|
||||||
# In seconds
|
# In seconds
|
||||||
delete_files_after_check_seconds: 1800
|
deleteFilesCheck: 1800
|
||||||
delete_key_length: 4
|
deleteKeyLength: 4
|
||||||
siteInfo: "Whatever you want to put here"
|
siteInfo: "Whatever you want to put here"
|
||||||
siteWarning: "WARNING!"
|
siteWarning: "WARNING!"
|
||||||
log_level: "debug"
|
log_level: "debug"
|
||||||
|
|
||||||
blocked_extensions:
|
blockedExtensions:
|
||||||
- "exe"
|
- "exe"
|
||||||
|
|
||||||
# List of useragents that use OpenGraph to gather file information
|
# List of useragents that use OpenGraph to gather file information
|
||||||
opengraph_useragents:
|
opengraphUseragents:
|
||||||
- "chatterino-api-cache/"
|
- "chatterino-api-cache/"
|
||||||
- "FFZBot/"
|
- "FFZBot/"
|
||||||
- "Twitterbot/"
|
- "Twitterbot/"
|
||||||
|
|
|
@ -5,24 +5,30 @@ class Config
|
||||||
|
|
||||||
property files : String = "./files"
|
property files : String = "./files"
|
||||||
property thumbnails : String = "./thumbnails"
|
property thumbnails : String = "./thumbnails"
|
||||||
property generate_thumbnails : Bool = false
|
property generateThumbnails : Bool = false
|
||||||
property db : String = "./db.sqlite3"
|
property db : String = "./db.sqlite3"
|
||||||
property db_table_name : String = "files"
|
property dbTableName : String = "files"
|
||||||
property adminEnabled : Bool = false
|
property adminEnabled : Bool = false
|
||||||
property adminApiKey : String = ""
|
property adminApiKey : String? = ""
|
||||||
property incremental_filename_length : Bool = true
|
property incremental_fileameLength : Bool = true
|
||||||
property filename_length : Int32 = 3
|
property fileameLength : Int32 = 3
|
||||||
# In MiB
|
# In MiB
|
||||||
property size_limit : Int16 = 512
|
property size_limit : Int16 = 512
|
||||||
property port : Int32 = 8080
|
property port : Int32 = 8080
|
||||||
property unix_socket : String?
|
property unix_socket : String?
|
||||||
property delete_files_after : Int32 = 7
|
property blockTorAddresses : Bool? = false
|
||||||
|
property torExitNodesCheck : Int32 = 3600
|
||||||
|
# The list needs to contain a IP address per line
|
||||||
|
property torExitNodesUrl : String = "https://www.dan.me.uk/torlist/?exit"
|
||||||
|
property torExitNodesFile : String = "./torexitnodes.txt"
|
||||||
|
property torMessage : String? = "Tor is blocked!"
|
||||||
|
property deleteFilesAfter : Int32 = 7
|
||||||
# How often should the check of old files be performed? (in seconds)
|
# How often should the check of old files be performed? (in seconds)
|
||||||
property delete_files_after_check_seconds : Int32 = 1800
|
property deleteFilesCheck : Int32 = 1800
|
||||||
property delete_key_length : Int32 = 4
|
property deleteKeyLength : Int32 = 4
|
||||||
# Blocked extensions that are not allowed to be uploaded to the server
|
# Blocked extensions that are not allowed to be uploaded to the server
|
||||||
property blocked_extensions : Array(String) = [] of String
|
property blockedExtensions : Array(String) = [] of String
|
||||||
property opengraph_useragents : Array(String) = [] of String
|
property opengraphUseragents : Array(String) = [] of String
|
||||||
property siteInfo : String = "xd"
|
property siteInfo : String = "xd"
|
||||||
property siteWarning : String? = ""
|
property siteWarning : String? = ""
|
||||||
property log_level : LogLevel = LogLevel::Info
|
property log_level : LogLevel = LogLevel::Info
|
||||||
|
@ -36,8 +42,8 @@ class Config
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.check_config(config : Config)
|
def self.check_config(config : Config)
|
||||||
if config.filename_length <= 0
|
if config.fileameLength <= 0
|
||||||
puts "Config: filename_length cannot be #{config.filename_length}"
|
puts "Config: fileameLength cannot be #{config.fileameLength}"
|
||||||
exit(1)
|
exit(1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -20,7 +20,7 @@ Kemal.config.app_name = "file-uploader-crystal"
|
||||||
# https://github.com/iv-org/invidious/blob/90e94d4e6cc126a8b7a091d12d7a5556bfe369d5/src/invidious.cr#L136C1-L136C61
|
# https://github.com/iv-org/invidious/blob/90e94d4e6cc126a8b7a091d12d7a5556bfe369d5/src/invidious.cr#L136C1-L136C61
|
||||||
LOGGER = LogHandler.new(STDOUT, CONFIG.log_level)
|
LOGGER = LogHandler.new(STDOUT, CONFIG.log_level)
|
||||||
# Give me a 128 bit CPU
|
# Give me a 128 bit CPU
|
||||||
# MAX_FILES = 58**CONFIG.filename_length
|
# MAX_FILES = 58**CONFIG.fileameLength
|
||||||
SQL = DB.open("sqlite3://#{CONFIG.db}")
|
SQL = DB.open("sqlite3://#{CONFIG.db}")
|
||||||
|
|
||||||
# https://github.com/iv-org/invidious/blob/90e94d4e6cc126a8b7a091d12d7a5556bfe369d5/src/invidious.cr#L78
|
# https://github.com/iv-org/invidious/blob/90e94d4e6cc126a8b7a091d12d7a5556bfe369d5/src/invidious.cr#L78
|
||||||
|
|
268
src/handling.cr
268
src/handling.cr
|
@ -1,268 +0,0 @@
|
||||||
require "./http-errors"
|
|
||||||
require "http/client"
|
|
||||||
|
|
||||||
module Handling
|
|
||||||
extend self
|
|
||||||
|
|
||||||
def upload(env)
|
|
||||||
env.response.content_type = "application/json"
|
|
||||||
# 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).
|
|
||||||
if CONFIG.size_limit > 0
|
|
||||||
if env.request.headers["Content-Length"].to_i > 1048576*CONFIG.size_limit
|
|
||||||
error413("File is too big. The maximum size allowed is #{CONFIG.size_limit}MiB")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
filename = ""
|
|
||||||
extension = ""
|
|
||||||
original_filename = ""
|
|
||||||
uploaded_at = ""
|
|
||||||
checksum = ""
|
|
||||||
ip_address = ""
|
|
||||||
delete_key = nil
|
|
||||||
# TODO: Return the file that matches a checksum inside the database
|
|
||||||
HTTP::FormData.parse(env.request) do |upload|
|
|
||||||
if upload.filename.nil? || upload.filename.to_s.empty?
|
|
||||||
LOGGER.debug "No file provided by the user"
|
|
||||||
error403("No file provided")
|
|
||||||
end
|
|
||||||
# TODO: upload.body is emptied when is copied or read
|
|
||||||
# Utils.check_duplicate(upload.dup)
|
|
||||||
extension = File.extname("#{upload.filename}")
|
|
||||||
if CONFIG.blocked_extensions.includes?(extension.split(".")[1])
|
|
||||||
error401("Extension '#{extension}' is not allowed")
|
|
||||||
end
|
|
||||||
filename = Utils.generate_filename
|
|
||||||
file_path = ::File.join ["#{CONFIG.files}", filename + extension]
|
|
||||||
File.open(file_path, "w") do |output|
|
|
||||||
IO.copy(upload.body, output)
|
|
||||||
end
|
|
||||||
original_filename = upload.filename
|
|
||||||
uploaded_at = Time::Format::HTTP_DATE.format(Time.utc)
|
|
||||||
checksum = Utils.hash_file(file_path)
|
|
||||||
end
|
|
||||||
protocol = env.request.headers.try &.["X-Forwarded-Proto"]? ? env.request.headers["X-Forwarded-Proto"] : "http"
|
|
||||||
host = env.request.headers.try &.["X-Forwarded-Host"]? ? env.request.headers["X-Forwarded-Host"] : env.request.headers["Host"]
|
|
||||||
# X-Forwarded-For if behind a reverse proxy and the header is set in the reverse
|
|
||||||
# proxy configuration.
|
|
||||||
ip_address = env.request.headers.try &.["X-Forwarded-For"]? ? env.request.headers.["X-Forwarded-For"] : env.request.remote_address.to_s.split(":").first
|
|
||||||
json = JSON.build do |j|
|
|
||||||
j.object do
|
|
||||||
j.field "link", "#{protocol}://#{host}/#{filename}"
|
|
||||||
j.field "linkExt", "#{protocol}://#{host}/#{filename}#{extension}"
|
|
||||||
j.field "id", filename
|
|
||||||
j.field "ext", extension
|
|
||||||
j.field "name", original_filename
|
|
||||||
j.field "checksum", checksum
|
|
||||||
if CONFIG.delete_key_length > 0
|
|
||||||
delete_key = Random.base58(CONFIG.delete_key_length)
|
|
||||||
j.field "deleteKey", delete_key
|
|
||||||
j.field "deleteLink", "#{protocol}://#{host}/delete?key=#{delete_key}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
begin
|
|
||||||
spawn { Utils.generate_thumbnail(filename, extension) }
|
|
||||||
rescue ex
|
|
||||||
LOGGER.error "An error ocurred when trying to generate a thumbnail: #{ex.message}"
|
|
||||||
end
|
|
||||||
begin
|
|
||||||
# Insert SQL data just before returning the upload information
|
|
||||||
SQL.exec "INSERT INTO #{CONFIG.db_table_name} VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
|
|
||||||
original_filename, filename, extension, uploaded_at, checksum, ip_address, delete_key, nil
|
|
||||||
rescue ex
|
|
||||||
LOGGER.error "An error ocurred when trying to insert the data into the DB: #{ex.message}"
|
|
||||||
error500("An error ocurred when trying to insert the data into the DB")
|
|
||||||
end
|
|
||||||
return json
|
|
||||||
end
|
|
||||||
|
|
||||||
# The most unoptimized and unstable feature lol
|
|
||||||
def upload_url(env)
|
|
||||||
env.response.content_type = "application/json"
|
|
||||||
extension = ""
|
|
||||||
filename = Utils.generate_filename
|
|
||||||
original_filename = ""
|
|
||||||
uploaded_at = Time::Format::HTTP_DATE.format(Time.utc)
|
|
||||||
checksum = ""
|
|
||||||
ip_address = env.request.headers.try &.["X-Forwarded-For"]? ? env.request.headers.["X-Forwarded-For"] : env.request.remote_address.to_s.split(":").first
|
|
||||||
delete_key = nil
|
|
||||||
# X-Forwarded-For if behind a reverse proxy and the header is set in the reverse
|
|
||||||
# proxy configuration.
|
|
||||||
if !env.params.body.nil? || env.params.body["url"].empty?
|
|
||||||
url = env.params.body["url"]
|
|
||||||
extension = File.extname(URI.parse(url).path)
|
|
||||||
file_path = ::File.join ["#{CONFIG.files}", filename + extension]
|
|
||||||
File.open(file_path, "w") do |output|
|
|
||||||
begin
|
|
||||||
HTTP::Client.get(url) do |res|
|
|
||||||
IO.copy(res.body_io, output)
|
|
||||||
end
|
|
||||||
rescue ex
|
|
||||||
LOGGER.debug "Failed to download file '#{url}': #{ex.message}"
|
|
||||||
error403("Failed to download file '#{url}'")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if extension.empty?
|
|
||||||
extension = Utils.detect_extension(file_path)
|
|
||||||
File.rename(file_path, file_path + extension)
|
|
||||||
file_path = ::File.join ["#{CONFIG.files}", filename + extension]
|
|
||||||
end
|
|
||||||
# TODO: Benchmark this:
|
|
||||||
# original_filename = URI.parse("https://ayaya.beauty/PqC").path.split("/").last
|
|
||||||
original_filename = url.split("/").last
|
|
||||||
checksum = Utils.hash_file(file_path)
|
|
||||||
if !filename.empty?
|
|
||||||
protocol = env.request.headers.try &.["X-Forwarded-Proto"]? ? env.request.headers["X-Forwarded-Proto"] : "http"
|
|
||||||
host = env.request.headers.try &.["X-Forwarded-Host"]? ? env.request.headers["X-Forwarded-Host"] : env.request.headers["Host"]
|
|
||||||
json = JSON.build do |j|
|
|
||||||
j.object do
|
|
||||||
j.field "link", "#{protocol}://#{host}/#{filename}"
|
|
||||||
j.field "linkExt", "#{protocol}://#{host}/#{filename}#{extension}"
|
|
||||||
j.field "id", filename
|
|
||||||
j.field "ext", extension
|
|
||||||
j.field "name", original_filename
|
|
||||||
j.field "checksum", checksum
|
|
||||||
if CONFIG.delete_key_length > 0
|
|
||||||
delete_key = Random.base58(CONFIG.delete_key_length)
|
|
||||||
j.field "deleteKey", delete_key
|
|
||||||
j.field "deleteLink", "#{protocol}://#{host}/delete?key=#{delete_key}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
begin
|
|
||||||
spawn { Utils.generate_thumbnail(filename, extension) }
|
|
||||||
rescue ex
|
|
||||||
LOGGER.error "An error ocurred when trying to generate a thumbnail: #{ex.message}"
|
|
||||||
end
|
|
||||||
begin
|
|
||||||
# Insert SQL data just before returning the upload information
|
|
||||||
SQL.exec "INSERT INTO #{CONFIG.db_table_name} VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
|
|
||||||
original_filename, filename, extension, uploaded_at, checksum, ip_address, delete_key, nil
|
|
||||||
rescue ex
|
|
||||||
LOGGER.error "An error ocurred when trying to insert the data into the DB: #{ex.message}"
|
|
||||||
error500("An error ocurred when trying to insert the data into the DB")
|
|
||||||
end
|
|
||||||
return json
|
|
||||||
end
|
|
||||||
else
|
|
||||||
end
|
|
||||||
error403("Data malformed")
|
|
||||||
end
|
|
||||||
|
|
||||||
def retrieve_file(env)
|
|
||||||
protocol = env.request.headers.try &.["X-Forwarded-Proto"]? ? env.request.headers["X-Forwarded-Proto"] : "http"
|
|
||||||
host = env.request.headers.try &.["X-Forwarded-Host"]? ? env.request.headers["X-Forwarded-Host"] : env.request.headers["Host"]
|
|
||||||
begin
|
|
||||||
fileinfo = SQL.query_all("SELECT filename, original_filename, uploaded_at, extension, checksum, thumbnail
|
|
||||||
FROM #{CONFIG.db_table_name}
|
|
||||||
WHERE filename = ?",
|
|
||||||
env.params.url["filename"].split(".").first,
|
|
||||||
as: {filename: String, ofilename: String, up_at: String, ext: String, checksum: String, thumbnail: String | Nil})[0]
|
|
||||||
|
|
||||||
headers(env, {"Content-Disposition" => "inline; filename*=UTF-8''#{fileinfo[:ofilename]}"})
|
|
||||||
headers(env, {"Last-Modified" => "#{fileinfo[:up_at]}"})
|
|
||||||
headers(env, {"ETag" => "#{fileinfo[:checksum]}"})
|
|
||||||
|
|
||||||
if env.request.headers.try &.["User-Agent"].includes?("chatterino-api-cache/") || env.request.headers.try &.["User-Agent"].includes?("FFZBot/") || env.request.headers.try &.["User-Agent"].includes?("Twitterbot/")
|
|
||||||
env.response.content_type = "text/html"
|
|
||||||
return %(
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta property="og:title" content="#{fileinfo[:ofilename]}">
|
|
||||||
<meta property="og:url" content="#{protocol}://#{host}/#{fileinfo[:filename]}">
|
|
||||||
#{if fileinfo[:thumbnail]
|
|
||||||
%(<meta property="og:image" content="#{protocol}://#{host}/thumbnail/#{fileinfo[:filename]}.jpg">)
|
|
||||||
end}
|
|
||||||
</head>
|
|
||||||
</html>
|
|
||||||
)
|
|
||||||
end
|
|
||||||
send_file env, "#{CONFIG.files}/#{fileinfo[:filename]}#{fileinfo[:ext]}"
|
|
||||||
rescue ex
|
|
||||||
LOGGER.debug "File '#{env.params.url["filename"]}' does not exist: #{ex.message}"
|
|
||||||
error403("File '#{env.params.url["filename"]}' does not exist")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def retrieve_thumbnail(env)
|
|
||||||
begin
|
|
||||||
send_file env, "#{CONFIG.thumbnails}/#{env.params.url["thumbnail"]}"
|
|
||||||
rescue ex
|
|
||||||
LOGGER.debug "Thumbnail '#{env.params.url["thumbnail"]}' does not exist: #{ex.message}"
|
|
||||||
error403("Thumbnail '#{env.params.url["thumbnail"]}' does not exist")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def stats(env)
|
|
||||||
env.response.content_type = "application/json"
|
|
||||||
begin
|
|
||||||
json_data = JSON.build do |json|
|
|
||||||
json.object do
|
|
||||||
json.field "stats" do
|
|
||||||
json.object do
|
|
||||||
json.field "filesHosted", SQL.query_one "SELECT COUNT (filename) FROM #{CONFIG.db_table_name}", as: Int32
|
|
||||||
json.field "maxUploadSize", CONFIG.size_limit
|
|
||||||
json.field "thumbnailGeneration", CONFIG.generate_thumbnails
|
|
||||||
json.field "filenameLength", CONFIG.filename_length
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
rescue ex
|
|
||||||
LOGGER.error "Unknown error: #{ex.message}"
|
|
||||||
error500("Unknown error")
|
|
||||||
end
|
|
||||||
json_data
|
|
||||||
end
|
|
||||||
|
|
||||||
def delete_file(env)
|
|
||||||
if SQL.query_one "SELECT EXISTS(SELECT 1 FROM #{CONFIG.db_table_name} WHERE delete_key = ?)", env.params.query["key"], as: Bool
|
|
||||||
begin
|
|
||||||
fileinfo = SQL.query_all("SELECT filename, extension, thumbnail
|
|
||||||
FROM #{CONFIG.db_table_name}
|
|
||||||
WHERE delete_key = ?",
|
|
||||||
env.params.query["key"],
|
|
||||||
as: {filename: String, extension: String, thumbnail: String | Nil})[0]
|
|
||||||
|
|
||||||
# Delete file
|
|
||||||
File.delete("#{CONFIG.files}/#{fileinfo[:filename]}#{fileinfo[:extension]}")
|
|
||||||
if fileinfo[:thumbnail]
|
|
||||||
# Delete thumbnail
|
|
||||||
File.delete("#{CONFIG.thumbnails}/#{fileinfo[:thumbnail]}")
|
|
||||||
end
|
|
||||||
# Delete entry from db
|
|
||||||
SQL.exec "DELETE FROM #{CONFIG.db_table_name} WHERE delete_key = ?", env.params.query["key"]
|
|
||||||
LOGGER.debug "File '#{fileinfo[:filename]}' was deleted using key '#{env.params.query["key"]}'}"
|
|
||||||
msg("File '#{fileinfo[:filename]}' deleted successfully")
|
|
||||||
rescue ex
|
|
||||||
LOGGER.error("Unknown error: #{ex.message}")
|
|
||||||
error500("Unknown error")
|
|
||||||
end
|
|
||||||
else
|
|
||||||
LOGGER.debug "Key '#{env.params.query["key"]}' does not exist"
|
|
||||||
error401("Delete key '#{env.params.query["key"]}' does not exist. No files were deleted")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def sharex_config(env)
|
|
||||||
protocol = env.request.headers.try &.["X-Forwarded-Proto"]? ? env.request.headers["X-Forwarded-Proto"] : "http"
|
|
||||||
host = env.request.headers.try &.["X-Forwarded-Host"]? ? env.request.headers["X-Forwarded-Host"] : env.request.headers["Host"]
|
|
||||||
env.response.content_type = "application/json"
|
|
||||||
env.response.headers["Content-Disposition"] = "attachment; filename=\"#{host}.sxcu\""
|
|
||||||
return %({
|
|
||||||
"Version": "14.0.1",
|
|
||||||
"DestinationType": "ImageUploader, FileUploader",
|
|
||||||
"RequestMethod": "POST",
|
|
||||||
"RequestURL": "#{protocol}://#{host}/upload",
|
|
||||||
"Body": "MultipartFormData",
|
|
||||||
"FileFormName": "file",
|
|
||||||
"URL": "{json:link}",
|
|
||||||
"DeletionURL": "{json:deleteLink}",
|
|
||||||
"ErrorMessage": "{json:error}"
|
|
||||||
})
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -14,7 +14,7 @@ module Handling::Admin
|
||||||
file = file.to_s
|
file = file.to_s
|
||||||
begin
|
begin
|
||||||
fileinfo = SQL.query_one("SELECT filename, extension, thumbnail
|
fileinfo = SQL.query_one("SELECT filename, extension, thumbnail
|
||||||
FROM #{CONFIG.db_table_name}
|
FROM #{CONFIG.dbTableName}
|
||||||
WHERE filename = ?",
|
WHERE filename = ?",
|
||||||
file,
|
file,
|
||||||
as: {filename: String, extension: String, thumbnail: String | Nil})
|
as: {filename: String, extension: String, thumbnail: String | Nil})
|
||||||
|
@ -26,11 +26,11 @@ module Handling::Admin
|
||||||
File.delete("#{CONFIG.thumbnails}/#{fileinfo[:thumbnail]}")
|
File.delete("#{CONFIG.thumbnails}/#{fileinfo[:thumbnail]}")
|
||||||
end
|
end
|
||||||
# Delete entry from db
|
# Delete entry from db
|
||||||
SQL.exec "DELETE FROM #{CONFIG.db_table_name} WHERE filename = ?", file
|
SQL.exec "DELETE FROM #{CONFIG.dbTableName} WHERE filename = ?", file
|
||||||
LOGGER.debug "File '#{fileinfo[:filename]}' was deleted"
|
LOGGER.debug "File '#{fileinfo[:filename]}' was deleted"
|
||||||
successfull_files << file
|
successfull_files << file
|
||||||
rescue ex : DB::NoResultsError
|
rescue ex : DB::NoResultsError
|
||||||
LOGGER.error("File '#{file}' doesn't exist: #{ex.message}")
|
LOGGER.error("File '#{file}' doesn't exist or is not registered in the database: #{ex.message}")
|
||||||
failed_files << file
|
failed_files << file
|
||||||
rescue ex
|
rescue ex
|
||||||
LOGGER.error "Unknown error: #{ex.message}"
|
LOGGER.error "Unknown error: #{ex.message}"
|
||||||
|
|
|
@ -30,7 +30,7 @@ module Handling
|
||||||
# TODO: upload.body is emptied when is copied or read
|
# TODO: upload.body is emptied when is copied or read
|
||||||
# Utils.check_duplicate(upload.dup)
|
# Utils.check_duplicate(upload.dup)
|
||||||
extension = File.extname("#{upload.filename}")
|
extension = File.extname("#{upload.filename}")
|
||||||
if CONFIG.blocked_extensions.includes?(extension.split(".")[1])
|
if CONFIG.blockedExtensions.includes?(extension.split(".")[1])
|
||||||
error401("Extension '#{extension}' is not allowed")
|
error401("Extension '#{extension}' is not allowed")
|
||||||
end
|
end
|
||||||
filename = Utils.generate_filename
|
filename = Utils.generate_filename
|
||||||
|
@ -55,8 +55,8 @@ module Handling
|
||||||
j.field "ext", extension
|
j.field "ext", extension
|
||||||
j.field "name", original_filename
|
j.field "name", original_filename
|
||||||
j.field "checksum", checksum
|
j.field "checksum", checksum
|
||||||
if CONFIG.delete_key_length > 0
|
if CONFIG.deleteKeyLength > 0
|
||||||
delete_key = Random.base58(CONFIG.delete_key_length)
|
delete_key = Random.base58(CONFIG.deleteKeyLength)
|
||||||
j.field "deleteKey", delete_key
|
j.field "deleteKey", delete_key
|
||||||
j.field "deleteLink", "#{protocol}://#{host}/delete?key=#{delete_key}"
|
j.field "deleteLink", "#{protocol}://#{host}/delete?key=#{delete_key}"
|
||||||
end
|
end
|
||||||
|
@ -69,7 +69,7 @@ module Handling
|
||||||
end
|
end
|
||||||
begin
|
begin
|
||||||
# Insert SQL data just before returning the upload information
|
# Insert SQL data just before returning the upload information
|
||||||
SQL.exec "INSERT INTO #{CONFIG.db_table_name} VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
|
SQL.exec "INSERT INTO #{CONFIG.dbTableName} VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
|
||||||
original_filename, filename, extension, uploaded_at, checksum, ip_address, delete_key, nil
|
original_filename, filename, extension, uploaded_at, checksum, ip_address, delete_key, nil
|
||||||
rescue ex
|
rescue ex
|
||||||
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}"
|
||||||
|
@ -124,8 +124,8 @@ module Handling
|
||||||
j.field "ext", extension
|
j.field "ext", extension
|
||||||
j.field "name", original_filename
|
j.field "name", original_filename
|
||||||
j.field "checksum", checksum
|
j.field "checksum", checksum
|
||||||
if CONFIG.delete_key_length > 0
|
if CONFIG.deleteKeyLength > 0
|
||||||
delete_key = Random.base58(CONFIG.delete_key_length)
|
delete_key = Random.base58(CONFIG.deleteKeyLength)
|
||||||
j.field "deleteKey", delete_key
|
j.field "deleteKey", delete_key
|
||||||
j.field "deleteLink", "#{protocol}://#{host}/delete?key=#{delete_key}"
|
j.field "deleteLink", "#{protocol}://#{host}/delete?key=#{delete_key}"
|
||||||
end
|
end
|
||||||
|
@ -138,7 +138,7 @@ module Handling
|
||||||
end
|
end
|
||||||
begin
|
begin
|
||||||
# Insert SQL data just before returning the upload information
|
# Insert SQL data just before returning the upload information
|
||||||
SQL.exec "INSERT INTO #{CONFIG.db_table_name} VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
|
SQL.exec "INSERT INTO #{CONFIG.dbTableName} VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
|
||||||
original_filename, filename, extension, uploaded_at, checksum, ip_address, delete_key, nil
|
original_filename, filename, extension, uploaded_at, checksum, ip_address, delete_key, nil
|
||||||
rescue ex
|
rescue ex
|
||||||
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}"
|
||||||
|
@ -156,7 +156,7 @@ module Handling
|
||||||
host = env.request.headers.try &.["X-Forwarded-Host"]? ? env.request.headers["X-Forwarded-Host"] : env.request.headers["Host"]
|
host = env.request.headers.try &.["X-Forwarded-Host"]? ? env.request.headers["X-Forwarded-Host"] : env.request.headers["Host"]
|
||||||
begin
|
begin
|
||||||
fileinfo = SQL.query_all("SELECT filename, original_filename, uploaded_at, extension, checksum, thumbnail
|
fileinfo = SQL.query_all("SELECT filename, original_filename, uploaded_at, extension, checksum, thumbnail
|
||||||
FROM #{CONFIG.db_table_name}
|
FROM #{CONFIG.dbTableName}
|
||||||
WHERE filename = ?",
|
WHERE filename = ?",
|
||||||
env.params.url["filename"].split(".").first,
|
env.params.url["filename"].split(".").first,
|
||||||
as: {filename: String, ofilename: String, up_at: String, ext: String, checksum: String, thumbnail: String | Nil})[0]
|
as: {filename: String, ofilename: String, up_at: String, ext: String, checksum: String, thumbnail: String | Nil})[0]
|
||||||
|
@ -165,7 +165,7 @@ module Handling
|
||||||
headers(env, {"Last-Modified" => "#{fileinfo[:up_at]}"})
|
headers(env, {"Last-Modified" => "#{fileinfo[:up_at]}"})
|
||||||
headers(env, {"ETag" => "#{fileinfo[:checksum]}"})
|
headers(env, {"ETag" => "#{fileinfo[:checksum]}"})
|
||||||
|
|
||||||
CONFIG.opengraph_useragents.each do |useragent|
|
CONFIG.opengraphUseragents.each do |useragent|
|
||||||
if env.request.headers.try &.["User-Agent"].includes?(useragent)
|
if env.request.headers.try &.["User-Agent"].includes?(useragent)
|
||||||
env.response.content_type = "text/html"
|
env.response.content_type = "text/html"
|
||||||
return %(
|
return %(
|
||||||
|
@ -206,10 +206,10 @@ module Handling
|
||||||
json.object do
|
json.object do
|
||||||
json.field "stats" do
|
json.field "stats" do
|
||||||
json.object do
|
json.object do
|
||||||
json.field "filesHosted", SQL.query_one "SELECT COUNT (filename) FROM #{CONFIG.db_table_name}", as: Int32
|
json.field "filesHosted", SQL.query_one "SELECT COUNT (filename) FROM #{CONFIG.dbTableName}", as: Int32
|
||||||
json.field "maxUploadSize", CONFIG.size_limit
|
json.field "maxUploadSize", CONFIG.size_limit
|
||||||
json.field "thumbnailGeneration", CONFIG.generate_thumbnails
|
json.field "thumbnailGeneration", CONFIG.generateThumbnails
|
||||||
json.field "filenameLength", CONFIG.filename_length
|
json.field "filenameLength", CONFIG.fileameLength
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -222,10 +222,10 @@ module Handling
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete_file(env)
|
def delete_file(env)
|
||||||
if SQL.query_one "SELECT EXISTS(SELECT 1 FROM #{CONFIG.db_table_name} WHERE delete_key = ?)", env.params.query["key"], as: Bool
|
if SQL.query_one "SELECT EXISTS(SELECT 1 FROM #{CONFIG.dbTableName} WHERE delete_key = ?)", env.params.query["key"], as: Bool
|
||||||
begin
|
begin
|
||||||
fileinfo = SQL.query_all("SELECT filename, extension, thumbnail
|
fileinfo = SQL.query_all("SELECT filename, extension, thumbnail
|
||||||
FROM #{CONFIG.db_table_name}
|
FROM #{CONFIG.dbTableName}
|
||||||
WHERE delete_key = ?",
|
WHERE delete_key = ?",
|
||||||
env.params.query["key"],
|
env.params.query["key"],
|
||||||
as: {filename: String, extension: String, thumbnail: String | Nil})[0]
|
as: {filename: String, extension: String, thumbnail: String | Nil})[0]
|
||||||
|
@ -237,7 +237,7 @@ module Handling
|
||||||
File.delete("#{CONFIG.thumbnails}/#{fileinfo[:thumbnail]}")
|
File.delete("#{CONFIG.thumbnails}/#{fileinfo[:thumbnail]}")
|
||||||
end
|
end
|
||||||
# Delete entry from db
|
# Delete entry from db
|
||||||
SQL.exec "DELETE FROM #{CONFIG.db_table_name} WHERE delete_key = ?", env.params.query["key"]
|
SQL.exec "DELETE FROM #{CONFIG.dbTableName} WHERE delete_key = ?", env.params.query["key"]
|
||||||
LOGGER.debug "File '#{fileinfo[:filename]}' was deleted using key '#{env.params.query["key"]}'}"
|
LOGGER.debug "File '#{fileinfo[:filename]}' was deleted using key '#{env.params.query["key"]}'}"
|
||||||
msg("File '#{fileinfo[:filename]}' deleted successfully")
|
msg("File '#{fileinfo[:filename]}' deleted successfully")
|
||||||
rescue ex
|
rescue ex
|
||||||
|
|
|
@ -2,39 +2,39 @@ 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
|
||||||
error_message = {"error" => {{message}}}.to_json
|
error_message = {"error" => {{message}}}.to_json
|
||||||
return error_message
|
error_message
|
||||||
end
|
end
|
||||||
|
|
||||||
macro error403(message)
|
macro error403(message)
|
||||||
env.response.content_type = "application/json"
|
env.response.content_type = "application/json"
|
||||||
env.response.status_code = 403
|
env.response.status_code = 403
|
||||||
error_message = {"error" => {{message}}}.to_json
|
error_message = {"error" => {{message}}}.to_json
|
||||||
return error_message
|
error_message
|
||||||
end
|
end
|
||||||
|
|
||||||
macro error404(message)
|
macro error404(message)
|
||||||
env.response.content_type = "application/json"
|
env.response.content_type = "application/json"
|
||||||
env.response.status_code = 404
|
env.response.status_code = 404
|
||||||
error_message = {"error" => {{message}}}.to_json
|
error_message = {"error" => {{message}}}.to_json
|
||||||
return error_message
|
error_message
|
||||||
end
|
end
|
||||||
|
|
||||||
macro error413(message)
|
macro error413(message)
|
||||||
env.response.content_type = "application/json"
|
env.response.content_type = "application/json"
|
||||||
env.response.status_code = 413
|
env.response.status_code = 413
|
||||||
error_message = {"error" => {{message}}}.to_json
|
error_message = {"error" => {{message}}}.to_json
|
||||||
return error_message
|
error_message
|
||||||
end
|
end
|
||||||
|
|
||||||
macro error500(message)
|
macro error500(message)
|
||||||
env.response.content_type = "application/json"
|
env.response.content_type = "application/json"
|
||||||
env.response.status_code = 500
|
env.response.status_code = 500
|
||||||
error_message = {"error" => {{message}}}.to_json
|
error_message = {"error" => {{message}}}.to_json
|
||||||
return error_message
|
error_message
|
||||||
end
|
end
|
||||||
|
|
||||||
macro msg(message)
|
macro msg(message)
|
||||||
env.response.content_type = "application/json"
|
env.response.content_type = "application/json"
|
||||||
msg = {"message" => {{message}}}.to_json
|
msg = {"message" => {{message}}}.to_json
|
||||||
return msg
|
msg
|
||||||
end
|
end
|
||||||
|
|
17
src/jobs.cr
17
src/jobs.cr
|
@ -1,14 +1,26 @@
|
||||||
# Pretty cool way to write background jobs! :)
|
# Pretty cool way to write background jobs! :)
|
||||||
module Jobs
|
module Jobs
|
||||||
def self.check_old_files
|
def self.check_old_files
|
||||||
if CONFIG.delete_files_after_check_seconds <= 0
|
if CONFIG.deleteFilesCheck <= 0
|
||||||
LOGGER.info "File deletion is disabled"
|
LOGGER.info "File deletion is disabled"
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
spawn do
|
spawn do
|
||||||
loop do
|
loop do
|
||||||
Utils.check_old_files
|
Utils.check_old_files
|
||||||
sleep CONFIG.delete_files_after_check_seconds
|
sleep CONFIG.deleteFilesCheck
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.retrieve_tor_exit_nodes
|
||||||
|
if !CONFIG.blockTorAddresses
|
||||||
|
return
|
||||||
|
end
|
||||||
|
spawn do
|
||||||
|
loop do
|
||||||
|
Utils.retrieve_tor_exit_nodes
|
||||||
|
sleep CONFIG.torExitNodesCheck
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -27,6 +39,7 @@ module Jobs
|
||||||
|
|
||||||
def self.run
|
def self.run
|
||||||
check_old_files
|
check_old_files
|
||||||
|
retrieve_tor_exit_nodes
|
||||||
kemal
|
kemal
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,11 +1,27 @@
|
||||||
|
require "./http-errors"
|
||||||
|
|
||||||
module Routing
|
module Routing
|
||||||
# @@ip : String = ""
|
@@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
|
||||||
|
before_post do |env|
|
||||||
|
ip_address = env.request.headers.try &.["X-Forwarded-For"]? ? env.request.headers.["X-Forwarded-For"] : env.request.remote_address.to_s.split(":").first
|
||||||
|
error401 CONFIG.torMessage if ip_address.includes?(ip_address)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def self.register_all
|
def self.register_all
|
||||||
# before_get "*" do |env|
|
|
||||||
# @@ip = env.request.headers["X-Real-IP"]
|
|
||||||
# end
|
|
||||||
|
|
||||||
get "/" do |env|
|
get "/" do |env|
|
||||||
files_hosted = SQL.query_one "SELECT COUNT (filename) FROM files", as: Int32
|
files_hosted = SQL.query_one "SELECT COUNT (filename) FROM files", as: Int32
|
||||||
host = env.request.headers["Host"]
|
host = env.request.headers["Host"]
|
||||||
|
|
71
src/utils.cr
71
src/utils.cr
|
@ -2,10 +2,10 @@ module Utils
|
||||||
extend self
|
extend self
|
||||||
|
|
||||||
def create_db
|
def create_db
|
||||||
if !SQL.query_one "SELECT EXISTS (SELECT name FROM sqlite_schema WHERE type='table' AND name='#{CONFIG.db_table_name}');", as: Bool
|
if !SQL.query_one "SELECT EXISTS (SELECT name FROM sqlite_schema WHERE type='table' AND name='#{CONFIG.dbTableName}');", as: Bool
|
||||||
LOGGER.info "Creating sqlite3 database at '#{CONFIG.db}'"
|
LOGGER.info "Creating sqlite3 database at '#{CONFIG.db}'"
|
||||||
begin
|
begin
|
||||||
SQL.exec "CREATE TABLE IF NOT EXISTS #{CONFIG.db_table_name}
|
SQL.exec "CREATE TABLE IF NOT EXISTS #{CONFIG.dbTableName}
|
||||||
(original_filename text, filename text, extension text, uploaded_at text, checksum text, ip text, delete_key text, thumbnail text)"
|
(original_filename text, filename text, extension text, uploaded_at text, checksum text, ip text, delete_key text, thumbnail text)"
|
||||||
rescue ex
|
rescue ex
|
||||||
LOGGER.fatal "#{ex.message}"
|
LOGGER.fatal "#{ex.message}"
|
||||||
|
@ -44,10 +44,10 @@ module Utils
|
||||||
LOGGER.info "Deleting old files"
|
LOGGER.info "Deleting old files"
|
||||||
dir = Dir.new("#{CONFIG.files}")
|
dir = Dir.new("#{CONFIG.files}")
|
||||||
# Delete entries from DB
|
# Delete entries from DB
|
||||||
SQL.exec "DELETE FROM #{CONFIG.db_table_name} WHERE uploaded_at < date('now', '-#{CONFIG.delete_files_after} days');"
|
SQL.exec "DELETE FROM #{CONFIG.dbTableName} WHERE uploaded_at < date('now', '-#{CONFIG.deleteFilesAfter} days');"
|
||||||
# Delete files
|
# Delete files
|
||||||
dir.each_child do |file|
|
dir.each_child do |file|
|
||||||
if (Time.utc - File.info("#{CONFIG.files}/#{file}").modification_time).days >= CONFIG.delete_files_after
|
if (Time.utc - File.info("#{CONFIG.files}/#{file}").modification_time).days >= CONFIG.deleteFilesAfter
|
||||||
LOGGER.debug "Deleting file '#{file}'"
|
LOGGER.debug "Deleting file '#{file}'"
|
||||||
begin
|
begin
|
||||||
File.delete("#{CONFIG.files}/#{file}")
|
File.delete("#{CONFIG.files}/#{file}")
|
||||||
|
@ -64,7 +64,7 @@ module Utils
|
||||||
def check_dependencies
|
def check_dependencies
|
||||||
dependencies = ["ffmpeg"]
|
dependencies = ["ffmpeg"]
|
||||||
dependencies.each do |dep|
|
dependencies.each do |dep|
|
||||||
next if !CONFIG.generate_thumbnails
|
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")
|
||||||
exit(1)
|
exit(1)
|
||||||
|
@ -74,7 +74,7 @@ module Utils
|
||||||
|
|
||||||
# TODO:
|
# TODO:
|
||||||
# def check_duplicate(upload)
|
# def check_duplicate(upload)
|
||||||
# file_checksum = SQL.query_all("SELECT checksum FROM #{CONFIG.db_table_name} WHERE original_filename = ?", upload.filename, as:String).try &.[0]?
|
# file_checksum = SQL.query_all("SELECT checksum FROM #{CONFIG.dbTableName} WHERE original_filename = ?", upload.filename, as:String).try &.[0]?
|
||||||
# if file_checksum.nil?
|
# if file_checksum.nil?
|
||||||
# return
|
# return
|
||||||
# else
|
# else
|
||||||
|
@ -97,20 +97,20 @@ module Utils
|
||||||
|
|
||||||
# TODO: Check if there are no other possibilities to get a random filename and exit
|
# TODO: Check if there are no other possibilities to get a random filename and exit
|
||||||
def generate_filename
|
def generate_filename
|
||||||
filename = Random.base58(CONFIG.filename_length)
|
filename = Random.base58(CONFIG.fileameLength)
|
||||||
loop do
|
loop do
|
||||||
if SQL.query_one("SELECT COUNT(filename) FROM #{CONFIG.db_table_name} WHERE filename = ?", filename, as: Int32) == 0
|
if SQL.query_one("SELECT COUNT(filename) FROM #{CONFIG.dbTableName} WHERE filename = ?", filename, as: Int32) == 0
|
||||||
return filename
|
return filename
|
||||||
else
|
else
|
||||||
LOGGER.debug "Filename collision! Generating a new filename"
|
LOGGER.debug "Filename collision! Generating a new filename"
|
||||||
filename = Random.base58(CONFIG.filename_length)
|
filename = Random.base58(CONFIG.fileameLength)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def generate_thumbnail(filename, extension)
|
def generate_thumbnail(filename, extension)
|
||||||
# Disable generation if false
|
# Disable generation if false
|
||||||
return if !CONFIG.generate_thumbnails
|
return if !CONFIG.generateThumbnails
|
||||||
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",
|
||||||
[
|
[
|
||||||
|
@ -127,7 +127,7 @@ module Utils
|
||||||
])
|
])
|
||||||
if process.normal_exit?
|
if process.normal_exit?
|
||||||
LOGGER.debug "Thumbnail for #{filename + extension} generated successfully"
|
LOGGER.debug "Thumbnail for #{filename + extension} generated successfully"
|
||||||
SQL.exec "UPDATE #{CONFIG.db_table_name} SET thumbnail = ? WHERE filename = ?", filename + ".jpg", filename
|
SQL.exec "UPDATE #{CONFIG.dbTableName} SET thumbnail = ? WHERE filename = ?", filename + ".jpg", filename
|
||||||
else
|
else
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -146,22 +146,22 @@ module Utils
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete_file(env)
|
def delete_file(env)
|
||||||
fileinfo = SQL.query_all("SELECT filename, extension, thumbnail
|
fileinfo = SQL.query_all("SELECT filename, extension, thumbnail
|
||||||
FROM #{CONFIG.db_table_name}
|
FROM #{CONFIG.dbTableName}
|
||||||
WHERE delete_key = ?",
|
WHERE delete_key = ?",
|
||||||
env.params.query["key"],
|
env.params.query["key"],
|
||||||
as: {filename: String, extension: String, thumbnail: String | Nil})[0]
|
as: {filename: String, extension: String, thumbnail: String | Nil})[0]
|
||||||
|
|
||||||
# Delete file
|
# Delete file
|
||||||
File.delete("#{CONFIG.files}/#{fileinfo[:filename]}#{fileinfo[:extension]}")
|
File.delete("#{CONFIG.files}/#{fileinfo[:filename]}#{fileinfo[:extension]}")
|
||||||
if fileinfo[:thumbnail]
|
if fileinfo[:thumbnail]
|
||||||
# Delete thumbnail
|
# Delete thumbnail
|
||||||
File.delete("#{CONFIG.thumbnails}/#{fileinfo[:thumbnail]}")
|
File.delete("#{CONFIG.thumbnails}/#{fileinfo[:thumbnail]}")
|
||||||
end
|
end
|
||||||
# Delete entry from db
|
# Delete entry from db
|
||||||
SQL.exec "DELETE FROM #{CONFIG.db_table_name} WHERE delete_key = ?", env.params.query["key"]
|
SQL.exec "DELETE FROM #{CONFIG.dbTableName} WHERE delete_key = ?", env.params.query["key"]
|
||||||
LOGGER.debug "File '#{fileinfo[:filename]}' was deleted using key '#{env.params.query["key"]}'}"
|
LOGGER.debug "File '#{fileinfo[:filename]}' was deleted using key '#{env.params.query["key"]}'}"
|
||||||
msg("File '#{fileinfo[:filename]}' deleted successfully")
|
msg("File '#{fileinfo[:filename]}' deleted successfully")
|
||||||
end
|
end
|
||||||
|
|
||||||
def detect_extension(file) : String
|
def detect_extension(file) : String
|
||||||
|
@ -185,4 +185,25 @@ module Utils
|
||||||
end
|
end
|
||||||
""
|
""
|
||||||
end
|
end
|
||||||
|
|
||||||
|
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)
|
||||||
|
end
|
||||||
|
rescue ex
|
||||||
|
LOGGER.error "Failed to write to file: #{ex.message}"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
LOGGER.error "Failed to retrieve exit nodes list. Status Code: #{res.status_code}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_tor_exit_nodes
|
||||||
|
exit_nodes = File.read_lines(CONFIG.torExitNodesFile)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -25,6 +25,9 @@
|
||||||
<a href='./chatterino.png'>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 %>)</a>
|
<a href='./chatterino.png'>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 %>)</a>
|
||||||
</p>
|
</p>
|
||||||
<p>Archivos alojados: <%= files_hosted %></p>
|
<p>Archivos alojados: <%= files_hosted %></p>
|
||||||
|
<% if CONFIG.blockTorAddresses %>
|
||||||
|
<p style="color: red"><%= CONFIG.torMessage %></p>
|
||||||
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
Loading…
Reference in a new issue