App-Netdisco

 view release on metacpan or  search on metacpan

lib/App/Netdisco/Web.pm  view on Meta::CPAN

  *Dancer::send_error = sub {
      my ($body, $status) = @_;
      if (request_is_api) {
        status $status || 400;
        $body = '' unless defined $body;
        Dancer::Continuation::Route::ErrorSent->new(
            return_value => to_json { error => $body, return_url => param('return_url') }
        )->throw;
      }
      Dancer::Continuation::Route::ErrorSent->new(
          return_value => Dancer::Error->new(
              message => $body,
              code => $status || 500)->render()
      )->throw;
  };

  # to insert /t/$tenant if set
  # which is fine for building links, but not fine for
  # comparison to request->path, because when is_forward() the
  # request->path is changed...
  *Dancer::Request::uri_for = sub {
    my ($self, $part, $params, $dont_escape) = @_;
    my $uri = $self->base;

    if (vars->{'tenant'}) {
        $part = '/t/'. vars->{'tenant'} . $part;
    }

    # Make sure there's exactly one slash between the base and the new part
    my $base = $uri->path;
    $base =~ s|/$||;
    $part =~ s|^/||;
    $uri->path("$base/$part");

    $uri->query_form($params) if $params;

    return $dont_escape ? uri_unescape($uri->canonical) : $uri->canonical;
  };

  # ...so here we are monkeypatching request->path as well
  *Dancer::Request::path = sub {
    die "path is accessor not mutator" if scalar @_ > 1;
    my $self = shift;
    $self->_build_path() unless $self->{path};

    if (vars->{'tenant'} and $self->{path} !~ m{/t/}) {
        my $path = $self->{path};
        my $base = setting('path');
        my $tenant = '/t/' . vars->{'tenant'};

        $tenant = ($base . $tenant) if $base ne '/';
        $tenant .= '/' if $base eq '/';
        $path =~ s/^$base/$tenant/;

        return $path;
    }
    return $self->{path};
  };

  # implement same_site
  # from https://github.com/PerlDancer/Dancer-Session-Cookie/issues/20
  *Dancer::Session::Cookie::_cookie_params = sub {
      my $self     = shift;
      my $name     = $self->session_name;
      my $duration = $self->_session_expires_as_duration;
      my %cookie   = (
          name      => $name,
          value     => $self->_cookie_value,
          path      => setting('session_cookie_path') || '/',
          domain    => setting('session_domain'),
          secure    => setting('session_secure'),
          http_only => setting("session_is_http_only") // 1,
          same_site => setting("session_same_site"),
      );
      if ( defined $duration ) {
          $cookie{expires} = time + $duration;
      }
      return %cookie;
  };
}

use App::Netdisco::Web::AuthN;
use App::Netdisco::Web::Static;
use App::Netdisco::Web::Search;
use App::Netdisco::Web::Device;
use App::Netdisco::Web::Report;
use App::Netdisco::Web::API::Objects;
use App::Netdisco::Web::API::Queue;
use App::Netdisco::Web::API::Statistics;
use App::Netdisco::Web::API::User;
use App::Netdisco::Web::Health;
use App::Netdisco::Web::Metrics;
use App::Netdisco::Web::AdminTask;
use App::Netdisco::Web::TypeAhead;
use App::Netdisco::Web::PortControl;
use App::Netdisco::Web::Statistics;
use App::Netdisco::Web::Password;
use App::Netdisco::Web::CustomFields;
use App::Netdisco::Web::GenericReport;

sub _load_web_plugins {
  my $plugin_list = shift;

  foreach my $plugin (@$plugin_list) {
      $plugin =~ s/^X::/+App::NetdiscoX::Web::Plugin::/;
      $plugin = 'App::Netdisco::Web::Plugin::'. $plugin
        if $plugin !~ m/^\+/;
      $plugin =~ s/^\+//;

      $ENV{ND2_LOG_PLUGINS} && debug "loading web plugin $plugin";
      Module::Load::load $plugin;
  }
}

if (setting('web_plugins') and ref [] eq ref setting('web_plugins')) {
    _load_web_plugins( setting('web_plugins') );
}

if (setting('extra_web_plugins') and ref [] eq ref setting('extra_web_plugins')) {
    unshift @INC, dir(($ENV{NETDISCO_HOME} || $ENV{HOME}), 'site_plugins')->stringify;
    _load_web_plugins( setting('extra_web_plugins') );
}

foreach my $tag (keys %{ setting('_admin_tasks') }) {
    my $code = sub {
        # trick the ajax into working as if this were a tabbed page
        params->{tab} = $tag;

        var(nav => 'admin');
        template 'admintask', {
          task => setting('_admin_tasks')->{ $tag },
        }, { layout => 'main' };
    };

    if (setting('_admin_tasks')->{ $tag }->{ 'roles' }) {
        get "/admin/$tag" => require_any_role setting('_admin_tasks')->{ $tag }->{ 'roles' } => $code;
    }
    else {
        get "/admin/$tag" => require_role admin => $code;
    }
}


# after plugins are loaded, add our own template path
push @{ config->{engines}->{netdisco_template_toolkit}->{INCLUDE_PATH} },
     setting('views');

# sort the reports which have been loaded, by their label
foreach my $cat (@{ setting('_report_order') }) {
    setting('_reports_menu')->{ $cat } ||= [];
    setting('_reports_menu')->{ $cat }
      = [ sort { setting('_reports')->{$a}->{'label'}
                 cmp
                 setting('_reports')->{$b}->{'label'} }
          @{ setting('_reports_menu')->{ $cat } } ];
}

# any template paths in deployment.yml (should override plugins)
if (setting('template_paths') and ref [] eq ref setting('template_paths')) {
    if (setting('site_local_files')) {
      push @{setting('template_paths')},
         dir(($ENV{NETDISCO_HOME} || $ENV{HOME}), 'nd-site-local', 'share')->stringify,
         dir(($ENV{NETDISCO_HOME} || $ENV{HOME}), 'nd-site-local', 'share', 'views')->stringify;
    }
    unshift @{ config->{engines}->{netdisco_template_toolkit}->{INCLUDE_PATH} },
      @{setting('template_paths')};
}

# load cookie key from database
setting('session_cookie_key' => undef);
setting('session_cookie_key' => 'this_is_for_testing_only')
  if $ENV{HARNESS_ACTIVE};
eval {
  my $sessions = schema('netdisco')->resultset('Session');
  my $skey = $sessions->find({id => 'dancer_session_cookie_key'});
  setting('session_cookie_key' => $skey->get_column('a_session')) if $skey;
};
Dancer::Session::Cookie::init(session);

# workaround for https://github.com/PerlDancer/Dancer/issues/935
hook after_error_render => sub { setting('layout' => 'main') };

# build list of port detail columns
{
  my @port_columns =
    sort { $a->{idx} <=> $b->{idx} }
    map  {{ name => $_, %{ setting('sidebar_defaults')->{'device_ports'}->{$_} } }}
    grep { $_ =~ m/^c_/ } keys %{ setting('sidebar_defaults')->{'device_ports'} };

  splice @port_columns, setting('device_port_col_idx_right') + 1, 0,
    grep {$_->{position} eq 'right'} @{ setting('_extra_device_port_cols') };
  splice @port_columns, setting('device_port_col_idx_mid') + 1, 0,
    grep {$_->{position} eq 'mid'}   @{ setting('_extra_device_port_cols') };
  splice @port_columns, setting('device_port_col_idx_left') + 1, 0,
    grep {$_->{position} eq 'left'}  @{ setting('_extra_device_port_cols') };

  set('port_columns' => \@port_columns);

  # update sidebar_defaults so hooks scanning params see new plugin cols
  setting('sidebar_defaults')->{'device_ports'}->{ $_->{name} } = $_
    for @port_columns;
}

# build lookup for tenancies
{
    set('tenant_data' => {
        map { ( $_->{tag} => { displayname => $_->{'displayname'},
                               tag => $_->{'tag'},
                               path => config->{'url_base'}->with("/t/$_->{tag}")->path } ) }
            @{ setting('tenant_databases') },
            { tag => 'netdisco', displayname => (setting('database')->{displayname} || 'Default') }
    });
    config->{'tenant_data'}->{'netdisco'}->{'path'}
      = URI::Based->new((config->{path} eq '/') ? '' : config->{path})->path;
    set('tenant_tags' => [  map { $_->{'tag'} }
                           sort { $a->{'displayname'} cmp $b->{'displayname'} }
                                values %{ config->{'tenant_data'} } ]);
}

hook 'before' => sub {
  my $key = request->path;
  if (param('tab') and ($key !~ m/ajax/)) {
      $key .= ('/' . param('tab'));
  }
  $key =~ s|.*/(\w+)/(\w+)$|${1}_${2}|;
  var(sidebar_key => $key);

  # trim whitespace
  params->{'q'} =~ s/^\s+|\s+$//g if param('q');

  # copy sidebar defaults into vars so we can mess about with it
  foreach my $sidebar (keys %{setting('sidebar_defaults')}) {
    vars->{'sidebar_defaults'}->{$sidebar} = { map {
      ($_ => setting('sidebar_defaults')->{$sidebar}->{$_}->{'default'})
    } keys %{setting('sidebar_defaults')->{$sidebar}} };
  }
};



( run in 0.679 second using v1.01-cache-2.11-cpan-e1769b4cff6 )