refactor: replace ClickController class with struct
This commit is contained in:
+37
-34
@@ -4,52 +4,55 @@ UserAgent.load_regexes(File.read("data/uap_core_regexes.yaml"))
|
||||
IpLookup.load_mmdb("data/GeoLite2-Country.mmdb")
|
||||
|
||||
module App::Controllers
|
||||
class ClickController
|
||||
struct ClickController
|
||||
include App::Models
|
||||
include App::Lib
|
||||
include App::Services
|
||||
|
||||
def initialize(@env : HTTP::Server::Context); end
|
||||
def self.redirect_handler
|
||||
->(env : HTTP::Server::Context) {
|
||||
slug = env.params.url["slug"]
|
||||
|
||||
def redirect
|
||||
slug = @env.params.url["slug"]
|
||||
|
||||
link_data = nil
|
||||
Database.raw_query("SELECT id, url FROM links WHERE slug = (?) LIMIT 1", slug) do |result|
|
||||
if result.move_next
|
||||
link_data = {result.read(Int64), result.read(String)}
|
||||
link_data = nil
|
||||
Database.raw_query("SELECT id, url FROM links WHERE slug = (?) LIMIT 1", slug) do |result|
|
||||
if result.move_next
|
||||
link_data = {result.read(Int64), result.read(String)}
|
||||
end
|
||||
end
|
||||
end
|
||||
raise App::NotFoundException.new(@env) unless link_data
|
||||
raise App::NotFoundException.new(env) unless link_data
|
||||
|
||||
link_id, url = link_data
|
||||
client_ip = IpLookup.ip_from_address(@env.request.headers["Cf-Connecting-Ip"]? || @env.request.remote_address.to_s)
|
||||
link_id, url = link_data
|
||||
client_ip = IpLookup.ip_from_address(env.request.headers["Cf-Connecting-Ip"]? || env.request.remote_address.to_s)
|
||||
|
||||
@env.response.status_code = 301
|
||||
@env.response.headers["Location"] = url
|
||||
@env.response.headers["X-Forwarded-For"] = client_ip.to_s
|
||||
@env.response.headers["Connection"] = "close"
|
||||
spawn do
|
||||
begin
|
||||
user_agent_str = env.request.headers["User-Agent"]?
|
||||
referer = env.request.headers["Referer"]?.try { |r| URI.parse(r).host rescue r } || env.params.query["utm_source"]? || "Direct"
|
||||
|
||||
spawn do
|
||||
begin
|
||||
user_agent_str = @env.request.headers["User-Agent"]?
|
||||
referer = @env.request.headers["Referer"]?.try { |r| URI.parse(r).host rescue r } || @env.params.query["utm_source"]? || "Direct"
|
||||
ua_parser = user_agent_str ? UserAgent.new(user_agent_str) : nil
|
||||
|
||||
ua_parser = user_agent_str ? UserAgent.new(user_agent_str) : nil
|
||||
click = App::Models::Click.new
|
||||
click.link_id = link_id
|
||||
click.country = client_ip ? IpLookup.new(client_ip).try(&.country.try(&.code)) : nil
|
||||
click.user_agent = ua_parser.to_s
|
||||
click.browser = ua_parser.try(&.family)
|
||||
click.os = ua_parser.try(&.os.try(&.family))
|
||||
click.referer = referer
|
||||
|
||||
click = App::Models::Click.new
|
||||
click.link_id = link_id
|
||||
click.country = client_ip ? IpLookup.new(client_ip).try(&.country.try(&.code)) : nil
|
||||
click.user_agent = ua_parser.to_s
|
||||
click.browser = ua_parser.try(&.family)
|
||||
click.os = ua_parser.try(&.os.try(&.family))
|
||||
click.referer = referer
|
||||
|
||||
Database.insert(click)
|
||||
rescue ex
|
||||
Log.error { "Click tracking error: #{ex.message}" }
|
||||
Database.insert(click)
|
||||
rescue ex
|
||||
Log.error { "Click tracking error: #{ex.message}" }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
env.response.status_code = 301
|
||||
env.response.headers.add("Location", url)
|
||||
env.response.headers.add("X-Forwarded-For", client_ip.to_s)
|
||||
env.response.print ""
|
||||
env.response.close
|
||||
|
||||
return
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -55,12 +55,3 @@ module App
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
error 500 do |env|
|
||||
App::InternalServerErrorException.new(env)
|
||||
""
|
||||
end
|
||||
|
||||
error 404 do |env|
|
||||
""
|
||||
end
|
||||
|
||||
+36
-17
@@ -1,26 +1,40 @@
|
||||
require "./controllers/**"
|
||||
|
||||
module App
|
||||
# CORS handling middleware
|
||||
before_all do |env|
|
||||
if env.request.path.starts_with?("/api/")
|
||||
env.response.headers["Access-Control-Allow-Origin"] = "*"
|
||||
env.response.headers["Access-Control-Allow-Methods"] = "GET, POST, PUT, DELETE, OPTIONS"
|
||||
env.response.headers["Access-Control-Allow-Headers"] = "Content-Type, Accept, Origin, X-Api-Key"
|
||||
require "kemal"
|
||||
|
||||
class CORSHandler < Kemal::Handler
|
||||
exclude ["/:slug"]
|
||||
|
||||
def initialize(
|
||||
@allow_origin = "*",
|
||||
@allow_methods = "GET, POST, PUT, DELETE, OPTIONS",
|
||||
@allow_headers = "Content-Type, Accept, Origin, X-Api-Key"
|
||||
)
|
||||
end
|
||||
|
||||
def call(context)
|
||||
return call_next(context) if exclude_match?(context)
|
||||
|
||||
context.response.headers["Access-Control-Allow-Origin"] = @allow_origin
|
||||
context.response.headers["Access-Control-Allow-Methods"] = @allow_methods
|
||||
context.response.headers["Access-Control-Allow-Headers"] = @allow_headers
|
||||
|
||||
# If this is a preflight OPTIONS request, we return immediately with 200
|
||||
if context.request.method == "OPTIONS"
|
||||
context.response.status_code = 200
|
||||
context.response.content_type = "text/plain"
|
||||
context.response.print("")
|
||||
return context
|
||||
end
|
||||
end
|
||||
|
||||
# Error handling middleware
|
||||
error 404 do |env|
|
||||
{error: "Not Found"}.to_json
|
||||
end
|
||||
error 500 do |env|
|
||||
{error: "Internal Server Error"}.to_json
|
||||
call_next(context)
|
||||
end
|
||||
end
|
||||
|
||||
get "/:slug" do |env|
|
||||
Controllers::ClickController.new(env).redirect
|
||||
end
|
||||
add_handler CORSHandler.new
|
||||
|
||||
module App
|
||||
get "/:slug", &App::Controllers::ClickController.redirect_handler
|
||||
|
||||
# Namespace /api
|
||||
get "/api/ping" do |env|
|
||||
@@ -50,4 +64,9 @@ module App
|
||||
delete "/api/links/:id" do |env|
|
||||
Controllers::LinkController.new(env).delete
|
||||
end
|
||||
|
||||
error 500 do |env|
|
||||
App::InternalServerErrorException.new(env)
|
||||
""
|
||||
end
|
||||
end
|
||||
|
||||
+3
-5
@@ -10,12 +10,11 @@ API_URL = "#{SERVER_URL}/api/links"
|
||||
API_KEY = "secure_api_key_1"
|
||||
TIME = "59s"
|
||||
|
||||
RESOURCE_USAGE_INTERVAL = 1
|
||||
CONTAINER_NAME = "bit"
|
||||
STATS_FILE = "resource_usage.txt"
|
||||
|
||||
class ResourceMonitor
|
||||
def initialize(@container_name : String, @interval : Float64)
|
||||
def initialize(@container_name : String)
|
||||
@running = false
|
||||
@stats = [] of {timestamp: Time, cpu: Float64, memory: Float64}
|
||||
end
|
||||
@@ -36,7 +35,6 @@ class ResourceMonitor
|
||||
end
|
||||
@stats << stat
|
||||
end
|
||||
sleep @interval.seconds
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -159,7 +157,7 @@ def run_benchmark
|
||||
sleep 2.seconds
|
||||
process = Process.new(
|
||||
"bombardier",
|
||||
["-d", TIME.to_s, "-c", "30", "-l", "--fasthttp", random_link],
|
||||
["-d", TIME.to_s, "-c", "30", "-l", "--disableKeepAlives", random_link],
|
||||
output: Process::Redirect::Inherit,
|
||||
error: Process::Redirect::Inherit
|
||||
)
|
||||
@@ -245,7 +243,7 @@ def main
|
||||
check_dependencies
|
||||
setup_containers
|
||||
|
||||
monitor = ResourceMonitor.new(CONTAINER_NAME, RESOURCE_USAGE_INTERVAL)
|
||||
monitor = ResourceMonitor.new(CONTAINER_NAME)
|
||||
monitor.start
|
||||
|
||||
begin
|
||||
|
||||
Reference in New Issue
Block a user