Files
bit/app/controllers/link.cr
T

180 lines
5.2 KiB
Crystal

require "uuid"
require "user_agent_parser"
UserAgent.load_regexes(File.read("data/regexes.yaml"))
require "../lib/controller.cr"
module App::Controllers::Link
class Create < App::Lib::BaseController
include App::Models
include App::Lib
include App::Services
def call(env)
user = env.get("user").as(User)
body = parse_body(env, ["url"])
url = body["url"].to_s
query = Database::Query.where(url: url, user_id: user.id.as(String)).limit(1)
existing_link = Database.all(Link, query, preload: [:clicks]).first?
if existing_link
response = {"data" => App::Serializers::Link.new(existing_link)}
return response.to_json
end
link = Link.new
link.id = UUID.v4.to_s
link.url = url
link.user = user
link.slug = SlugService.shorten_url(url, user.id.to_s)
changeset = Database.insert(link)
if !changeset.valid?
raise App::UnprocessableEntityException.new(env, map_changeset_errors(changeset.errors))
end
link.clicks = [] of App::Models::Click
response = {"data" => App::Serializers::Link.new(link)}
response.to_json
end
end
class Index < App::Lib::BaseController
include App::Models
include App::Lib
def call(env)
slug = env.params.url["slug"]
link = Database.get_by(Link, slug: slug)
raise App::NotFoundException.new(env) if !link
spawn do
user_agent_str = env.request.headers["User-Agent"]? || "Unknown"
user_agent = user_agent_str != "Unknown" ? UserAgent.new(user_agent_str) : nil
language_header = env.request.headers["Accept-Language"]? || "Unknown"
language = language_header.split(',').first.split(';').first
referer = env.request.headers["Referer"]?
click = Click.new
click.id = UUID.v4.to_s
click.link = link
click.language = language
click.user_agent = user_agent_str
click.browser = user_agent ? user_agent.family : "Unknown"
click.os = user_agent ? (user_agent.os.try &.family || "Unknown") : "Unknown"
click.source = referer ? URI.parse(referer).host : "Unknown"
changeset = Database.insert(click)
if changeset.errors.any?
Log.error { "Logging click event failed: #{changeset.errors}" }
end
end
env.response.status_code = 301
env.response.headers["Location"] = link.url!
env.response.headers["Content-Type"] = "text/html"
env.response.print("Redirecting...")
end
end
class All < App::Lib::BaseController
include App::Models
include App::Lib
def call(env)
user = env.get("user").as(User)
query = Database::Query.where(user_id: user.id.as(String))
links = Database.all(Link, query, preload: [:clicks])
response = {"data" => links.map { |link| App::Serializers::Link.new(link) }}
response.to_json
end
end
class Get < App::Lib::BaseController
include App::Models
include App::Lib
def call(env)
user = env.get("user").as(User)
link_id = env.params.url["id"]
query = Database::Query.where(id: link_id.as(String), user_id: user.id.as(String)).limit(1)
link = Database.all(Link, query, preload: [:clicks]).first?
raise App::NotFoundException.new(env) if link.nil?
response = {"data" => App::Serializers::Link.new(link)}
response.to_json
end
end
class Update < App::Lib::BaseController
include App::Models
include App::Lib
include App::Services
def call(env)
user = env.get("user").as(User)
id = env.params.url["id"]
body = parse_body(env, ["url"])
query = Database::Query.where(id: id).limit(1)
link = Database.all(Link, query, preload: [:clicks]).first?
raise App::NotFoundException.new(env) if link.nil?
raise App::ForbiddenException.new(env) if link.user_id != user.id
new_url = body["url"].to_s
existing_query = Database::Query.where(url: new_url, user_id: user.id.to_s).limit(1)
existing_link = Database.all(Link, existing_query).first?
if existing_link
raise App::UnprocessableEntityException.new(env, { "url" => ["URL already exists"] })
end
link.url = new_url
link.slug = SlugService.shorten_url(new_url, user.id.to_s)
changeset = Database.update(link)
if !changeset.valid?
raise App::UnprocessableEntityException.new(env, map_changeset_errors(changeset.errors))
end
response = {"data" => App::Serializers::Link.new(link)}
response.to_json
end
end
class Delete < App::Lib::BaseController
include App::Models
include App::Lib
def call(env)
user = env.get("user").as(User)
id = env.params.url["id"]
link = Database.get(Link, id)
raise App::NotFoundException.new(env) if !link
if link.user_id != user.id
raise App::ForbiddenException.new(env)
end
result = Database.raw_exec("DELETE FROM links WHERE id = (?)", link.id) # tempfix: Database.delete does not work
if result.rows_affected == 0
raise App::UnprocessableEntityException.new(env, { "id" => ["Row delete failed"] })
end
env.response.status_code = 204
end
end
end