Compare commits

...

2 Commits

2 changed files with 129 additions and 99 deletions

View File

@@ -6,17 +6,18 @@ use Urupam::Utils qw(sanitize_input get_error_status sanitize_error_message);
sub shorten {
my $c = shift;
$c->render_later;
my $responded = 0;
my $url_service = $c->url_service;
my $validator = $c->validator;
my $json = $c->req->json;
unless ( defined $json && ref $json eq 'HASH' ) {
return if $responded;
$responded = 1;
$c->render(
json => { error => 'Invalid JSON format' },
status => 400
$c->respond_once(
sub {
$c->render(
json => { error => 'Invalid JSON format' },
status => 400
);
}
);
return;
}
@@ -24,11 +25,13 @@ sub shorten {
my $original_url = sanitize_input( $json->{url} || '' );
unless ($original_url) {
return if $responded;
$responded = 1;
$c->render(
json => { error => 'URL is required' },
status => 400
$c->respond_once(
sub {
$c->render(
json => { error => 'URL is required' },
status => 400
);
}
);
return;
}
@@ -43,29 +46,34 @@ sub shorten {
)->then(
sub {
my $short_code = shift;
return if $responded;
$responded = 1;
my $short_url = $c->url_for("/$short_code")->to_abs;
$c->render(
json => {
success => 1,
short_url => $short_url,
short_code => $short_code,
original_url => $normalized_url
$c->respond_once(
sub {
my $short_url = $c->url_for("/$short_code")->to_abs;
$c->render(
json => {
success => 1,
short_url => $short_url,
short_code => $short_code,
original_url => $normalized_url
}
);
}
);
}
)->catch(
sub {
my $err = shift;
return if $responded;
$responded = 1;
$c->app->log->error("API URL validation/creation error: $err");
my $status = get_error_status($err);
my $sanitized_error = sanitize_error_message($err);
$c->render(
json => { error => $sanitized_error },
status => $status
$c->respond_once(
sub {
$c->app->log->error(
"API URL validation/creation error: $err");
my $status = get_error_status($err);
my $sanitized_error = sanitize_error_message($err);
$c->render(
json => { error => $sanitized_error },
status => $status
);
}
);
}
);
@@ -74,17 +82,18 @@ sub shorten {
sub get_url {
my $c = shift;
$c->render_later;
my $responded = 0;
my $short_code = $c->param('short_code') // '';
my $url_service = $c->url_service;
my $validator = $c->validator;
unless ( $short_code && $validator->validate_short_code($short_code) ) {
return if $responded;
$responded = 1;
$c->render(
json => { error => 'Invalid short code format' },
status => 400
$c->respond_once(
sub {
$c->render(
json => { error => 'Invalid short code format' },
status => 400
);
}
);
return;
}
@@ -92,37 +101,41 @@ sub get_url {
return $url_service->get_original_url($short_code)->then(
sub {
my $original_url = shift;
return if $responded;
$responded = 1;
if ($original_url) {
my $short_url = $c->url_for("/$short_code")->to_abs;
$c->render(
json => {
success => 1,
short_code => $short_code,
original_url => $original_url,
short_url => $short_url
$c->respond_once(
sub {
if ($original_url) {
my $short_url = $c->url_for("/$short_code")->to_abs;
$c->render(
json => {
success => 1,
short_code => $short_code,
original_url => $original_url,
short_url => $short_url
}
);
}
);
}
else {
$c->render(
json => { error => 'Short code not found' },
status => 404
);
}
else {
$c->render(
json => { error => 'Short code not found' },
status => 404
);
}
}
);
}
)->catch(
sub {
my $err = shift;
return if $responded;
$responded = 1;
$c->app->log->error("API URL retrieval error: $err");
my $status = get_error_status($err);
my $sanitized_error = sanitize_error_message($err);
$c->render(
json => { error => $sanitized_error },
status => $status
$c->respond_once(
sub {
$c->app->log->error("API URL retrieval error: $err");
my $status = get_error_status($err);
my $sanitized_error = sanitize_error_message($err);
$c->render(
json => { error => $sanitized_error },
status => $status
);
}
);
}
);

View File

@@ -37,36 +37,48 @@ sub startup {
}
);
$self->helper(
respond_once => sub {
my $c = shift;
my $callback = shift;
return if $c->rendered;
$callback->($c);
}
);
my $r = $self->routes;
$r->get('/health')->to(
cb => sub {
my $c = shift;
$c->render_later;
my $responded = 0;
$c->db->ping->then(
sub {
return if $responded;
$responded = 1;
$c->render(
json => {
status => 'ok',
version => $c->version
$c->respond_once(
sub {
$c->render(
json => {
status => 'ok',
version => $c->version
}
);
}
);
}
)->catch(
sub {
my $err = shift;
return if $responded;
$responded = 1;
$c->app->log->error("Health check DB error: $err");
$c->render(
json => {
status => 'error',
error => 'Database connection failed'
},
status => 503
$c->respond_once(
sub {
$c->app->log->error("Health check DB error: $err");
$c->render(
json => {
status => 'error',
error => 'Database connection failed'
},
status => 503
);
}
);
}
);
@@ -99,7 +111,6 @@ sub startup {
cb => sub {
my $c = shift;
$c->render_later;
my $responded = 0;
my $short_code = $c->param('short_code') // '';
my $url_service = $c->url_service;
my $validator = $c->validator;
@@ -107,11 +118,13 @@ sub startup {
unless ( $short_code
&& $validator->validate_short_code($short_code) )
{
return if $responded;
$responded = 1;
$c->render(
template => '404',
status => 404
$c->respond_once(
sub {
$c->render(
template => '404',
status => 404
);
}
);
return;
}
@@ -119,27 +132,31 @@ sub startup {
return $url_service->get_original_url($short_code)->then(
sub {
my $original_url = shift;
return if $responded;
$responded = 1;
if ($original_url) {
$c->redirect_to($original_url);
}
else {
$c->render(
template => '404',
status => 404
);
}
$c->respond_once(
sub {
if ($original_url) {
$c->redirect_to($original_url);
}
else {
$c->render(
template => '404',
status => 404
);
}
}
);
}
)->catch(
sub {
my $err = shift;
return if $responded;
$responded = 1;
$c->app->log->error("Redirect lookup error: $err");
$c->render(
template => '500',
status => 500
$c->respond_once(
sub {
$c->app->log->error("Redirect lookup error: $err");
$c->render(
template => '500',
status => 500
);
}
);
}
);