2011-03-10 15:19:34 +00:00
|
|
|
package Explain::Controller;
|
|
|
|
|
|
|
|
use Mojo::Base 'Mojolicious::Controller';
|
|
|
|
|
|
|
|
use English -no_match_vars;
|
|
|
|
|
|
|
|
use Pg::Explain;
|
2011-08-25 12:43:49 +02:00
|
|
|
use Encode;
|
2011-03-10 15:19:34 +00:00
|
|
|
use Email::Valid;
|
2014-10-31 22:03:11 +01:00
|
|
|
use Config;
|
2019-05-21 09:29:21 +02:00
|
|
|
use Digest::MD5 qw( md5_hex );
|
2011-03-10 15:19:34 +00:00
|
|
|
|
2013-10-28 14:58:17 +01:00
|
|
|
sub logout {
|
|
|
|
my $self = shift;
|
2013-10-30 12:47:28 +01:00
|
|
|
delete $self->session->{ 'user' };
|
2014-10-31 22:03:11 +01:00
|
|
|
delete $self->session->{ 'admin' };
|
2013-10-28 14:58:17 +01:00
|
|
|
$self->redirect_to( 'new-explain' );
|
|
|
|
}
|
|
|
|
|
2013-10-29 14:35:04 +01:00
|
|
|
sub user_history {
|
|
|
|
my $self = shift;
|
2013-10-30 12:47:28 +01:00
|
|
|
$self->redirect_to( 'history' ) unless $self->session->{ 'user' };
|
|
|
|
|
|
|
|
my @args = ( $self->session->{ 'user' } );
|
|
|
|
if ( ( $self->param( 'direction' ) )
|
|
|
|
&& ( $self->param( 'direction' ) =~ m{\A(?:before|after)\z} )
|
|
|
|
&& ( $self->param( 'key' ) ) )
|
|
|
|
{
|
|
|
|
push @args, $self->param( 'direction' ) eq 'before' ? 'DESC' : 'ASC';
|
|
|
|
push @args, $self->param( 'key' );
|
2013-10-29 14:35:04 +01:00
|
|
|
}
|
|
|
|
my $data = $self->database->get_user_history( @args );
|
2013-10-30 12:47:28 +01:00
|
|
|
$self->stash->{ 'plans' } = $data;
|
2013-10-29 14:35:04 +01:00
|
|
|
return $self->render();
|
|
|
|
}
|
|
|
|
|
2013-10-28 14:58:17 +01:00
|
|
|
sub user {
|
|
|
|
my $self = shift;
|
|
|
|
|
2013-10-30 12:47:28 +01:00
|
|
|
my $old = $self->req->param( 'old-pw' );
|
|
|
|
my $new = $self->req->param( 'new-pw' );
|
|
|
|
my $new2 = $self->req->param( 'new-pw2' );
|
2013-10-28 14:58:17 +01:00
|
|
|
|
|
|
|
return $self->render unless defined $old;
|
2013-10-30 12:47:28 +01:00
|
|
|
|
|
|
|
if ( ( !defined $new )
|
|
|
|
|| ( !defined $new2 )
|
|
|
|
|| ( $new ne $new2 ) )
|
|
|
|
{
|
|
|
|
$self->stash->{ 'message' } = 'You have to provide two identical copies of new password!';
|
2013-10-28 14:58:17 +01:00
|
|
|
return;
|
|
|
|
}
|
2013-10-30 12:47:28 +01:00
|
|
|
my $status = $self->database->user_change_password( $self->session->{ 'user' }, $old, $new );
|
2013-10-28 14:58:17 +01:00
|
|
|
if ( $status ) {
|
2013-10-30 12:47:28 +01:00
|
|
|
$self->flash( 'message' => 'Password changed.' );
|
2013-10-28 14:58:17 +01:00
|
|
|
$self->redirect_to( 'new-explain' );
|
|
|
|
}
|
2013-10-30 12:47:28 +01:00
|
|
|
$self->stash->{ 'message' } = 'Changing the password failed.';
|
2013-10-28 14:58:17 +01:00
|
|
|
}
|
|
|
|
|
2013-10-29 14:35:04 +01:00
|
|
|
sub plan_change {
|
|
|
|
my $self = shift;
|
2013-10-30 12:47:28 +01:00
|
|
|
unless ( $self->session->{ 'user' } ) {
|
2013-10-29 14:35:04 +01:00
|
|
|
$self->app->log->error( 'User tried to access plan change without being logged!' );
|
|
|
|
$self->redirect_to( 'new-explain' );
|
|
|
|
}
|
2013-10-30 12:47:28 +01:00
|
|
|
$self->redirect_to( 'new-explain' ) unless $self->req->param( 'return' );
|
|
|
|
|
|
|
|
my $plan = $self->database->get_plan_data( $self->param( 'id' ) );
|
|
|
|
if ( ( !defined $plan->{ 'added_by' } )
|
|
|
|
|| ( $plan->{ 'added_by' } ne $self->session->{ 'user' } ) )
|
|
|
|
{
|
|
|
|
$self->app->log->error( 'User tried to access plan change for plan [' . $plan->{ 'id' } . '] of another user: ' . $self->session->{ 'user' } );
|
2013-10-29 14:35:04 +01:00
|
|
|
$self->redirect_to( 'logout' );
|
|
|
|
}
|
|
|
|
|
|
|
|
# All looks fine. Current plan data are in $plan.
|
2013-10-30 12:47:28 +01:00
|
|
|
if ( ( $self->req->param( 'delete' ) )
|
|
|
|
&& ( $self->req->param( 'delete' ) eq 'yes' ) )
|
|
|
|
{
|
|
|
|
$self->database->delete_plan( $plan->{ 'id' }, $plan->{ 'delete_key' } );
|
|
|
|
return $self->redirect_to( $self->req->param( 'return' ) );
|
2013-10-29 14:35:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
my %changes = ();
|
2013-10-30 12:47:28 +01:00
|
|
|
if ( $plan->{ 'title' } ne ( $self->req->param( 'title' ) // '' ) ) {
|
|
|
|
$changes{ 'title' } = ( $self->req->param( 'title' ) // '' );
|
2013-10-29 14:35:04 +01:00
|
|
|
}
|
2013-10-30 12:47:28 +01:00
|
|
|
if ( ( $plan->{ 'is_public' } )
|
|
|
|
&& ( !$self->req->param( 'is_public' ) ) )
|
|
|
|
{
|
2013-10-29 14:35:04 +01:00
|
|
|
$changes{ 'is_public' } = 0;
|
2013-10-30 12:47:28 +01:00
|
|
|
}
|
|
|
|
elsif (( !$plan->{ 'is_public' } )
|
|
|
|
&& ( $self->req->param( 'is_public' ) ) )
|
|
|
|
{
|
2013-10-29 14:35:04 +01:00
|
|
|
$changes{ 'is_public' } = 1;
|
|
|
|
}
|
|
|
|
|
2013-10-30 12:47:28 +01:00
|
|
|
if ( ( !$plan->{ 'is_anonymized' } )
|
|
|
|
&& ( $self->req->param( 'is_anonymized' ) ) )
|
|
|
|
{
|
|
|
|
my $explain = Pg::Explain->new( source => $plan->{ 'plan' } );
|
2013-10-29 14:35:04 +01:00
|
|
|
$explain->anonymize();
|
2013-10-30 12:47:28 +01:00
|
|
|
$changes{ 'plan' } = $explain->as_text();
|
2013-10-29 14:35:04 +01:00
|
|
|
$changes{ 'is_anonymized' } = 1;
|
|
|
|
}
|
2013-10-30 12:44:20 +01:00
|
|
|
|
2013-10-30 12:47:28 +01:00
|
|
|
return $self->redirect_to( $self->req->param( 'return' ) ) if 0 == scalar keys %changes;
|
2013-10-29 14:35:04 +01:00
|
|
|
|
2013-10-30 12:47:28 +01:00
|
|
|
$self->database->update_plan( $plan->{ 'id' }, \%changes );
|
2013-10-29 14:35:04 +01:00
|
|
|
|
2013-10-30 12:47:28 +01:00
|
|
|
return $self->redirect_to( $self->req->param( 'return' ) );
|
2013-10-29 14:35:04 +01:00
|
|
|
}
|
|
|
|
|
2013-10-28 14:58:17 +01:00
|
|
|
sub login {
|
|
|
|
my $self = shift;
|
|
|
|
|
|
|
|
# If there is no username - there is nothing to do
|
2013-10-30 12:47:28 +01:00
|
|
|
my $username = $self->req->param( 'username' );
|
2013-10-28 14:58:17 +01:00
|
|
|
return $self->render unless defined $username;
|
|
|
|
|
|
|
|
if ( 30 < length( $username ) ) {
|
2013-10-30 12:47:28 +01:00
|
|
|
$self->stash->{ 'message' } = 'Username cannot be longer than 30 characters. Really?!';
|
|
|
|
return;
|
2013-10-28 14:58:17 +01:00
|
|
|
}
|
|
|
|
|
2013-10-30 12:47:28 +01:00
|
|
|
my $password = $self->req->param( 'password' );
|
|
|
|
my $password2 = $self->req->param( 'password2' );
|
2013-10-28 14:58:17 +01:00
|
|
|
|
2013-10-30 12:47:28 +01:00
|
|
|
if ( ( !defined $password ) || ( '' eq $password ) ) {
|
|
|
|
$self->stash->{ 'message' } = 'There has to be some password!';
|
2013-10-28 14:58:17 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
# Registration
|
2013-10-30 12:47:28 +01:00
|
|
|
if ( $self->req->param( 'is_registration' ) ) {
|
|
|
|
if ( ( !defined $password2 )
|
|
|
|
|| ( $password2 ne $password ) )
|
|
|
|
{
|
|
|
|
$self->stash->{ 'message' } = 'You have to repeat password correctly!';
|
2013-10-28 14:58:17 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
my $status = $self->database->user_register( $username, $password );
|
|
|
|
if ( $status ) {
|
2013-10-30 12:47:28 +01:00
|
|
|
$self->flash( 'message' => 'User registered.' );
|
2013-10-28 14:58:17 +01:00
|
|
|
$self->session( 'user' => $username );
|
|
|
|
$self->redirect_to( 'new-explain' );
|
|
|
|
}
|
2013-10-30 12:47:28 +01:00
|
|
|
$self->stash->{ 'message' } = 'Registration failed.';
|
2013-10-28 14:58:17 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-10-31 22:03:11 +01:00
|
|
|
if ( my $user = $self->database->user_login( $username, $password ) ) {
|
2013-10-30 12:47:28 +01:00
|
|
|
$self->flash( 'message' => 'User logged in.' );
|
2019-05-14 13:05:34 +02:00
|
|
|
$self->session( 'user' => $username );
|
2014-10-31 22:03:11 +01:00
|
|
|
$self->session( 'admin' => $user->{ 'admin' } );
|
2013-10-28 14:58:17 +01:00
|
|
|
$self->redirect_to( 'new-explain' );
|
|
|
|
}
|
2013-10-30 12:47:28 +01:00
|
|
|
$self->stash->{ 'message' } = 'Bad username or password.';
|
2013-10-28 14:58:17 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-05-19 18:45:41 +02:00
|
|
|
sub new_optimization {
|
|
|
|
my $self = shift;
|
|
|
|
|
|
|
|
my $original_plan_id = $self->req->param( 'original' ) // '';
|
|
|
|
|
|
|
|
return $self->redirect_to( 'new-explain' ) unless $original_plan_id =~ m{\A[a-zA-Z0-9]+\z};
|
|
|
|
|
|
|
|
my ( $original_plan, $original_title ) = $self->database->get_plan( $original_plan_id );
|
|
|
|
|
|
|
|
return $self->redirect_to( 'new-explain', status => 404 ) unless $original_plan;
|
|
|
|
|
2019-05-14 13:05:34 +02:00
|
|
|
$self->stash->{ 'optimization' } = 1;
|
2017-05-19 18:45:41 +02:00
|
|
|
$self->stash->{ 'original_plan_id' } = $original_plan_id;
|
2019-05-14 13:05:34 +02:00
|
|
|
$self->stash->{ 'original_title' } = $original_title;
|
2017-05-19 18:45:41 +02:00
|
|
|
|
|
|
|
return $self->render( 'controller/index' );
|
|
|
|
}
|
|
|
|
|
2011-03-10 15:19:34 +00:00
|
|
|
sub index {
|
|
|
|
my $self = shift;
|
|
|
|
|
|
|
|
# plan
|
2017-02-08 15:39:08 +01:00
|
|
|
my $plan = $self->req->param( 'plan' );
|
2011-03-10 15:19:34 +00:00
|
|
|
|
|
|
|
# nothing to do...
|
|
|
|
return $self->render unless $plan;
|
|
|
|
|
|
|
|
# request entity too large
|
2011-06-28 15:25:14 +02:00
|
|
|
return $self->render( message => 'Your plan is too long.', status => 413 )
|
2011-03-10 15:19:34 +00:00
|
|
|
if 10_000_000 < length $plan;
|
|
|
|
|
2017-05-19 18:45:41 +02:00
|
|
|
# Get id of parent plan
|
|
|
|
my $parent_id = $self->req->param( 'optimization_for' );
|
|
|
|
if ( defined $parent_id ) {
|
|
|
|
$parent_id = undef unless $self->database->plan_exists( $parent_id );
|
|
|
|
}
|
|
|
|
|
2011-06-11 04:44:41 +02:00
|
|
|
# public
|
|
|
|
my $is_public = $self->req->param( 'is_public' ) ? 1 : 0;
|
|
|
|
|
|
|
|
# anonymization
|
|
|
|
my $is_anon = $self->req->param( 'is_anon' ) ? 1 : 0;
|
|
|
|
|
2011-06-28 15:25:14 +02:00
|
|
|
# plan title
|
|
|
|
my $title = $self->req->param( 'title' );
|
|
|
|
$title = '' unless defined $title;
|
|
|
|
$title = '' if 'Optional title' eq $title;
|
|
|
|
|
2011-05-04 07:12:41 +00:00
|
|
|
# try
|
2011-03-10 15:19:34 +00:00
|
|
|
eval {
|
2011-05-04 07:12:41 +00:00
|
|
|
|
|
|
|
# make "explain"
|
2011-03-10 15:19:34 +00:00
|
|
|
my $explain = Pg::Explain->new( source => $plan );
|
2011-05-04 07:12:41 +00:00
|
|
|
|
|
|
|
# something goes wrong...
|
|
|
|
die q|Can't create explain! Explain "top_node" is undef!|
|
|
|
|
unless defined $explain->top_node;
|
2011-06-11 04:44:41 +02:00
|
|
|
|
|
|
|
# Anonymize plan, when requested.
|
|
|
|
if ( $is_anon ) {
|
|
|
|
$explain->anonymize();
|
|
|
|
$plan = $explain->as_text();
|
|
|
|
}
|
|
|
|
|
2011-03-10 15:19:34 +00:00
|
|
|
};
|
|
|
|
|
2011-05-04 07:12:41 +00:00
|
|
|
# catch
|
|
|
|
if ( $EVAL_ERROR ) {
|
|
|
|
|
|
|
|
# log message
|
|
|
|
$self->app->log->info( $EVAL_ERROR );
|
|
|
|
|
|
|
|
# leave...
|
2019-12-17 20:09:28 +01:00
|
|
|
return $self->render( 'message' => q|Failed to parse your plan:|, 'details' => $EVAL_ERROR );
|
2011-05-04 07:12:41 +00:00
|
|
|
}
|
2011-03-10 15:19:34 +00:00
|
|
|
|
|
|
|
# save to database
|
2017-05-19 18:45:41 +02:00
|
|
|
my ( $id, $delete_key ) = $self->database->save_with_random_name( $title, $plan, $is_public, $is_anon, $self->session->{ 'user' }, $parent_id, );
|
2011-03-10 15:19:34 +00:00
|
|
|
|
2011-03-11 06:19:50 +00:00
|
|
|
# redirect to /show/:id
|
2013-03-30 20:18:27 +01:00
|
|
|
$self->flash( delete_key => $delete_key );
|
2011-03-11 06:19:50 +00:00
|
|
|
return $self->redirect_to( 'show', id => $id );
|
2011-03-10 15:19:34 +00:00
|
|
|
}
|
|
|
|
|
2013-03-30 20:18:27 +01:00
|
|
|
sub delete {
|
|
|
|
my $self = shift;
|
|
|
|
|
|
|
|
# value of "/:id" param
|
|
|
|
my $id = defined $self->stash->{ id } ? $self->stash->{ id } : '';
|
|
|
|
|
|
|
|
# value of "/:key" param
|
|
|
|
my $key = defined $self->stash->{ key } ? $self->stash->{ key } : '';
|
|
|
|
|
|
|
|
# missing or invalid
|
|
|
|
return $self->redirect_to( 'new-explain' ) unless $id =~ m{\A[a-zA-Z0-9]+\z};
|
|
|
|
return $self->redirect_to( 'new-explain' ) unless $key =~ m{\A[a-zA-Z0-9]+\z};
|
|
|
|
|
|
|
|
# delete plan in database
|
|
|
|
my $delete_worked = $self->database->delete_plan( $id, $key );
|
|
|
|
|
|
|
|
# not found in database
|
|
|
|
return $self->redirect_to( 'new-explain', status => 404 ) unless $delete_worked;
|
|
|
|
|
|
|
|
$self->flash( message => sprintf( 'Plan %s deleted.', $id ) );
|
|
|
|
return $self->redirect_to( 'new-explain' );
|
|
|
|
}
|
|
|
|
|
2011-03-10 15:19:34 +00:00
|
|
|
sub show {
|
|
|
|
my $self = shift;
|
|
|
|
|
2011-03-11 06:19:50 +00:00
|
|
|
# value of "/:id" param
|
2019-12-03 18:55:41 +01:00
|
|
|
my $id = defined $self->stash->{ id } ? $self->stash->{ id } : '';
|
2011-03-10 15:19:34 +00:00
|
|
|
|
|
|
|
# missing or invalid
|
2011-03-11 06:19:50 +00:00
|
|
|
return $self->redirect_to( 'new-explain' ) unless $id =~ m{\A[a-zA-Z0-9]+\z};
|
2011-03-10 15:19:34 +00:00
|
|
|
|
|
|
|
# get plan source from database
|
2011-06-28 15:25:14 +02:00
|
|
|
my ( $plan, $title ) = $self->database->get_plan( $id );
|
2011-03-10 15:19:34 +00:00
|
|
|
|
|
|
|
# not found in database
|
2011-03-11 06:19:50 +00:00
|
|
|
return $self->redirect_to( 'new-explain', status => 404 ) unless $plan;
|
2011-03-10 15:19:34 +00:00
|
|
|
|
|
|
|
# make explanation
|
|
|
|
my $explain = eval { Pg::Explain->new( source => $plan ); };
|
|
|
|
|
|
|
|
# plans are validated before save, so this should never happen
|
|
|
|
if ( $EVAL_ERROR ) {
|
|
|
|
$self->app->log->error( $EVAL_ERROR );
|
2011-03-11 06:19:50 +00:00
|
|
|
return $self->redirect_to( 'new-explain' );
|
2011-03-10 15:19:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
# validate explain
|
|
|
|
eval { $explain->top_node; };
|
|
|
|
|
|
|
|
# as above, should never happen
|
|
|
|
if ( $EVAL_ERROR ) {
|
|
|
|
$self->app->log->error( $EVAL_ERROR );
|
2011-03-11 06:19:50 +00:00
|
|
|
return $self->redirect_to( 'new-explain' );
|
2011-03-10 15:19:34 +00:00
|
|
|
}
|
|
|
|
|
2011-07-13 02:29:41 +02:00
|
|
|
# Get stats from plan
|
2019-12-03 18:55:41 +01:00
|
|
|
my $stats = { 'tables' => {} };
|
2011-07-13 02:29:41 +02:00
|
|
|
my @elements = ( $explain->top_node );
|
|
|
|
while ( my $e = shift @elements ) {
|
2011-07-13 13:23:15 +02:00
|
|
|
push @elements, values %{ $e->ctes } if $e->ctes;
|
2011-07-13 02:29:41 +02:00
|
|
|
push @elements, @{ $e->sub_nodes } if $e->sub_nodes;
|
|
|
|
push @elements, @{ $e->initplans } if $e->initplans;
|
|
|
|
push @elements, @{ $e->subplans } if $e->subplans;
|
|
|
|
|
2013-03-30 20:18:27 +01:00
|
|
|
$stats->{ 'nodes' }->{ $e->type }->{ 'count' }++;
|
|
|
|
$stats->{ 'nodes' }->{ $e->type }->{ 'time' } += $e->total_exclusive_time if $e->total_exclusive_time;
|
2011-07-13 02:29:41 +02:00
|
|
|
|
|
|
|
next unless $e->scan_on;
|
|
|
|
next unless $e->scan_on->{ 'table_name' };
|
|
|
|
$stats->{ 'tables' }->{ $e->scan_on->{ 'table_name' } } ||= {};
|
|
|
|
my $S = $stats->{ 'tables' }->{ $e->scan_on->{ 'table_name' } };
|
|
|
|
$S->{ $e->{ 'type' } }->{ 'count' }++;
|
|
|
|
$S->{ ':total' }->{ 'count' }++;
|
|
|
|
if ( defined( my $t = $e->total_exclusive_time ) ) {
|
|
|
|
$S->{ $e->type }->{ 'time' } += $t;
|
|
|
|
$S->{ ':total' }->{ 'time' } += $t;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-06-28 15:25:14 +02:00
|
|
|
# put explain and title to stash
|
2011-03-10 15:19:34 +00:00
|
|
|
$self->stash->{ explain } = $explain;
|
2011-07-13 02:29:41 +02:00
|
|
|
$self->stash->{ title } = $title;
|
|
|
|
$self->stash->{ stats } = $stats;
|
2011-03-10 15:19:34 +00:00
|
|
|
|
2017-05-19 18:45:41 +02:00
|
|
|
# Fetch path of optimizations
|
|
|
|
$self->stash->{ optimization_path } = $self->database->get_optimization_path( $id );
|
2019-05-14 13:05:34 +02:00
|
|
|
$self->stash->{ suboptimizations } = $self->database->get_optimizations_for( $id );
|
2017-05-19 18:45:41 +02:00
|
|
|
|
2011-03-10 15:19:34 +00:00
|
|
|
# render will be called automatically
|
2019-12-03 18:55:41 +01:00
|
|
|
|
|
|
|
return $self->render( 'controller/iframe' ) if 'iframe' eq $self->match->endpoint->name;
|
|
|
|
|
2011-03-10 15:19:34 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub history {
|
|
|
|
my $self = shift;
|
|
|
|
|
2011-03-11 06:19:50 +00:00
|
|
|
# date
|
|
|
|
my $date = $self->param( 'date' );
|
2011-03-10 15:19:34 +00:00
|
|
|
|
2013-08-26 13:52:46 +02:00
|
|
|
if ( ( $date ) && ( $date lt '2008-12-01' ) ) {
|
2013-03-30 17:44:30 +01:00
|
|
|
return $self->redirect_to( '/' );
|
|
|
|
}
|
|
|
|
|
2011-03-10 15:19:34 +00:00
|
|
|
# get result set from database
|
2011-03-11 06:19:50 +00:00
|
|
|
my $rs = $self->database->get_public_list_paged( $date );
|
2011-03-10 15:19:34 +00:00
|
|
|
|
|
|
|
# put result set to stash
|
|
|
|
$self->stash( rs => $rs );
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-05-21 09:29:21 +02:00
|
|
|
sub contact_post {
|
2011-03-10 15:19:34 +00:00
|
|
|
my $self = shift;
|
|
|
|
|
2019-05-21 09:29:21 +02:00
|
|
|
return $self->redirect_to( 'contact' ) unless $self->req->param( 'message' );
|
|
|
|
|
|
|
|
my $session_nonce = $self->session->{ 'nonce' };
|
|
|
|
my $param_nonce = $self->req->param( 'nonce' );
|
|
|
|
my $prefix = $self->req->param( 'nonceprefix' );
|
|
|
|
|
|
|
|
if ( ( !defined $session_nonce )
|
|
|
|
|| ( !defined $param_nonce )
|
|
|
|
|| ( $session_nonce ne $param_nonce ) )
|
|
|
|
{
|
|
|
|
$self->flash( error => "Please don't hack me" );
|
|
|
|
return $self->redirect_to( 'contact' );
|
|
|
|
}
|
|
|
|
if ( ( !defined $prefix )
|
|
|
|
|| ( $prefix eq '' ) )
|
|
|
|
{
|
|
|
|
$self->flash( error => "Sorry, due to contact spam, you need JavaScript to contact me." );
|
|
|
|
return $self->redirect_to( 'contact' );
|
|
|
|
}
|
|
|
|
|
|
|
|
my ( $level, $nonce ) = split( /:/, $session_nonce );
|
|
|
|
my $md5_prefix = substr( md5_hex( $prefix . $nonce ), 0, $level );
|
|
|
|
$md5_prefix =~ s/0//g;
|
|
|
|
if ( $md5_prefix ne '' ) {
|
|
|
|
$self->flash( error => "Please don't hack me" );
|
|
|
|
return $self->redirect_to( 'contact' );
|
|
|
|
}
|
2011-03-10 15:19:34 +00:00
|
|
|
|
|
|
|
# invalid email address
|
|
|
|
return $self->render( error => 'Invalid email address' )
|
|
|
|
unless Email::Valid->address( $self->req->param( 'email' ) || '' );
|
|
|
|
|
2019-05-14 13:02:21 +02:00
|
|
|
my $client_ip = $self->tx->remote_address;
|
|
|
|
if ( my $forward = $self->req->headers->header( 'x-forwarded-for' ) ) {
|
|
|
|
$client_ip .= sprintf " (from: %s)", $forward;
|
|
|
|
}
|
|
|
|
|
2019-05-21 10:33:25 +02:00
|
|
|
my @mail_template_lines = ();
|
|
|
|
push @mail_template_lines, 'Message from: %s <%s>';
|
|
|
|
push @mail_template_lines, 'Posted from: %s with %s';
|
|
|
|
push @mail_template_lines, 'Prefix: %s';
|
|
|
|
push @mail_template_lines, '***********************************************';
|
|
|
|
push @mail_template_lines, '%s';
|
|
|
|
my $mail_template = "\n" . join( "\n", @mail_template_lines ) . "\n\n";
|
|
|
|
|
2011-03-10 15:19:34 +00:00
|
|
|
# send
|
2011-06-28 15:25:14 +02:00
|
|
|
$self->send_mail(
|
|
|
|
{
|
|
|
|
msg => sprintf(
|
2019-05-21 10:33:25 +02:00
|
|
|
$mail_template,
|
2011-06-28 15:25:14 +02:00
|
|
|
$self->req->param( 'name' ) || '',
|
|
|
|
$self->req->param( 'email' ),
|
2019-05-14 13:02:21 +02:00
|
|
|
$client_ip,
|
2011-06-28 15:25:14 +02:00
|
|
|
$self->req->headers->user_agent,
|
2019-05-21 10:33:25 +02:00
|
|
|
$prefix,
|
2011-06-28 15:25:14 +02:00
|
|
|
$self->req->param( 'message' )
|
|
|
|
)
|
|
|
|
}
|
|
|
|
);
|
2011-03-10 15:19:34 +00:00
|
|
|
|
|
|
|
# mail sent message
|
|
|
|
$self->flash( message => 'Mail sent' );
|
|
|
|
|
|
|
|
# get after post
|
|
|
|
$self->redirect_to( 'contact' );
|
|
|
|
}
|
|
|
|
|
2019-05-21 09:29:21 +02:00
|
|
|
sub contact {
|
|
|
|
my $self = shift;
|
|
|
|
|
|
|
|
my @chars = ( "a" .. "z", "A" .. "Z", "0" .. "9" );
|
|
|
|
my $level = 2;
|
|
|
|
my $nonce = $level . ':' . join( '', map { $chars[ rand @chars ] } 1 .. 20 );
|
|
|
|
$self->stash->{ 'nonce' } = $nonce;
|
|
|
|
$self->session->{ 'nonce' } = $nonce;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-10-31 22:03:11 +01:00
|
|
|
sub info {
|
|
|
|
my $self = shift;
|
|
|
|
$self->redirect_to( 'new-explain' ) unless $self->session->{ 'user' };
|
|
|
|
$self->redirect_to( 'new-explain' ) unless $self->session->{ 'admin' };
|
|
|
|
|
|
|
|
my @versions = ();
|
|
|
|
for my $module ( sort keys %INC ) {
|
|
|
|
next if $module =~ m{^\.?/};
|
|
|
|
$module =~ s/\.pm$//;
|
|
|
|
$module =~ s#/#::#g;
|
2019-05-14 13:05:34 +02:00
|
|
|
push @versions,
|
|
|
|
{
|
|
|
|
'module' => $module,
|
2014-10-31 22:03:11 +01:00
|
|
|
'version' => $module->VERSION,
|
2019-05-14 13:05:34 +02:00
|
|
|
};
|
2014-10-31 22:03:11 +01:00
|
|
|
}
|
|
|
|
$self->stash( 'modules' => \@versions );
|
2019-05-14 13:05:34 +02:00
|
|
|
$self->stash(
|
|
|
|
'perl' => {
|
2014-10-31 22:03:11 +01:00
|
|
|
'version' => $PERL_VERSION,
|
2019-05-14 13:05:34 +02:00
|
|
|
'binary' => $Config{ 'perlpath' } . $Config{ '_exe' },
|
2014-10-31 22:03:11 +01:00
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-06-11 20:56:49 +02:00
|
|
|
sub status {
|
|
|
|
my $self = shift;
|
2019-05-14 13:05:34 +02:00
|
|
|
if ( $self->database->ping() ) {
|
|
|
|
$self->render( 'text' => 'OK', status => 200 );
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
$self->render( 'text' => 'DB FAILED', status => 500 );
|
2018-06-11 20:56:49 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-03-10 15:19:34 +00:00
|
|
|
sub help {
|
2011-06-28 15:25:14 +02:00
|
|
|
|
2011-03-10 15:19:34 +00:00
|
|
|
# direct to template
|
|
|
|
return ( shift )->render;
|
|
|
|
}
|
|
|
|
|
|
|
|
1;
|