OSDN Git Service

Plugin::Stories: add storiees->latest helper
[newslash/newslash.git] / src / newslash_web / lib / Newslash / Web.pm
index 80aafe3..a497974 100644 (file)
 package Newslash::Web;
 use Mojo::Base 'Mojolicious';
 use Mojo::Util qw(dumper);
-
-use Newslash::Model;
+use List::Util qw(any);
 
 use constant CONFIG_FILE => '/etc/newslash/newslash.conf';
+has subcommand => "";
 
 # This method will run once at server start
 sub startup {
     my $app = shift;
 
+    if ($ARGV[0]) {
+        $app->subcommand($ARGV[0]);
+    }
+
+    # add commands in Newslash::Command
+    push @{$app->commands->namespaces}, 'Newslash::Command';
+
     # load config file
     # first, check existence of /etc/newslash.conf
     if ($app->mode eq 'production' && -e CONFIG_FILE) {
         $app->plugin('Newslash::Plugin::YAMLConfig', file => CONFIG_FILE);
     }
     else {
-        #$app->plugin('JSONConfig');
-        $app->plugin('Newslash::Plugin::YAMLConfig');
+       if ($app->subcommand eq "configdump") {
+           $app->plugin('Newslash::Plugin::YAMLConfig', default => {});
+       }
+       else {
+           $app->plugin('Newslash::Plugin::YAMLConfig');
+       }
     }
+
+    # load default configuration values
+    $app->plugin('Newslash::Plugin::DefaultConfig');
+
     # TODO: load/save configs with database
 
-    # set log level
-    if ($app->config->{Log} && $app->config->{Log}->{level}) {
-        my $loglv = $app->config->{Log}->{level};
-        if (grep { $loglv eq $_ } qw(debug info warn error fatal)) {
-            $app->log->level($loglv);
+    if ($app->config->{Log} && $app->config->{Log}->{backtrace}) {
+        require Carp::Always;
+        Carp::Always->import;
+    }
+
+    # system log config
+    if ($app->config->{Log} && $app->config->{Log}->{system_log}) {
+       my $cnf = $app->config->{Log}->{system_log};
+
+       # set log level
+       if ($cnf->{level}) {
+           my $loglv = $cnf->{level};
+           if (grep { $loglv eq $_ } qw(debug info warn error fatal)) {
+               $app->log->level($loglv);
+           }
+           else {
+               $app->log->warn('invalid log level given in config file');
+           }
+       }
+
+       # file output settings
+       if ($cnf->{mode} eq "local_file") {
+           my $pathname = $cnf->{local_file};
+           # check log is writable
+           if (!$pathname) {
+               $app->log->error("cannot write system log to file: filename not given");
+           }
+           elsif (-e $pathname) {
+               if (-w $pathname) {
+                   $app->log->debug("logs will be outputed to $pathname ...");
+                   $app->log(Mojo::Log->new);
+                   $app->log->path($pathname);
+               }
+               else {
+                   $app->log->error("cannot write system log to file: $pathname");
+               }
+           }
+           else {
+               if (open(my $fh, ">", $pathname)) {
+                   close($fh);
+                   $app->log->debug("logs will be outputed to $pathname ...");
+                   $app->log(Mojo::Log->new);
+                   $app->log->path($pathname);
+               }
+               else {
+                   $app->log->error("cannot create system log file: $pathname");
+               }
+           }
         }
-        else {
-            $app->log->warn('invalid log level given in config file');
+    }
+
+    # add static contents directories
+    if ($app->config->{System} && $app->config->{System}->{static_dir}) {
+        for my $dir (@{$app->config->{System}->{static_dir}}) {
+            push @{$app->static->paths}, $dir;
         }
     }
 
+    # secret key for hasing
+    $app->secrets([$app->config->{System}->{secret_key},]);
+
+    # when "test" mode, output debug logs.
+    $app->log->level('debug') if $app->mode eq 'test';
+
+    # check if 'maintenance' mode
+    my $maintenance_mode = 0;
+    if ($app->subcommand
+        && any { $app->subcommand eq $_ } qw[configdump configimport databaseinit
+                                             testdatainsert useradd usermod
+                                           ]) {
+       $maintenance_mode = 1;
+    }
+
     ############################################################
     #
     # Plugin Settings
     #
     ############################################################
 
-    # when "test" mode, output debug logs.
-    $app->log->level('debug') if $app->mode eq 'test';
+    # profiler settings
+    if ($app->mode eq 'development'
+        && $app->config->{Profiler}
+        && $app->config->{Profiler}->{enable}) {
+        if ($app->config->{Profiler}->{profiler} eq 'nytprof') {
+            $app->plugin('Mojolicious::Plugin::NYTProf', { nytprof => {} });
+        }
+    }
 
-    # enable logging
-    $app->plugin('Newslash::Plugin::AccessLog::Debug', $app->config->{Log} || {});
-    $app->plugin('Newslash::Plugin::AccessLog::LocalFile', $app->config->{Log} || {});
+    # enable access logging
+    $app->plugin('Newslash::Plugin::AccessLog');
 
-    # secret key for hasing
-    $app->secrets([$app->config->{System}->{secret_key},]);
+    # Helpers for Newslash
+    $app->plugin('Newslash::Plugin::NewslashHelpers');
 
-    # stash for plugins
-    $app->config->{_Plugins} = {};
+    # use Epoch
+    $app->plugin('Newslash::Plugin::Epoch');
 
-    # use BasicAuth?
-    if ($app->config->{BasicAuth} && $app->config->{BasicAuth}->{enable}) {
-        $app->plugin('Newslash::Plugin::BasicAuth');
-    }
+    # support BasicAuth
+    $app->plugin('Newslash::Plugin::BasicAuth');
 
     # use TimeLimitedCache ($app->cache)
     $app->plugin('Newslash::Plugin::TimeLimitedCache');
@@ -63,25 +143,40 @@ sub startup {
     $app->plugin('Newslash::Plugin::KeyValueStore');
 
     # add Model Loader
-    my $model_opts = $app->config;
-    $model_opts->{Logger} = $app->log;
-    $app->helper(model => Newslash::Model::loader($model_opts));
-    Newslash::Model::startup($model_opts, $app);
+    $app->plugin('Newslash::Plugin::Model', { bypass_startup => $maintenance_mode });
 
-    # use Model Cache ($app->model_cache)
-    $app->plugin('Newslash::Plugin::ModelCache');
+    # use Easy Cache ($app->ezcache)
+    $app->plugin('Newslash::Plugin::EasyCache');
 
     # use Template::Toolkit 2 render
     $app->plugin('Newslash::Plugin::TT2Renderer');
 
-    # user AntiCsrf ($app->anti_csrf)
+    # use ResponseFilter (Faculities)
+    $app->plugin('Newslash::Plugin::ResponseFilter');
+
+    # use CustomBoxes
+    $app->plugin('Newslash::Plugin::CustomBoxes');
+
+    # use AntiCsrf
     $app->plugin('Newslash::Plugin::AntiCsrf');
 
-    # compile CSS
-    #$app->plugin('Newslash::Plugin::CSSCompile');
+    # contents preprocessor
+    $app->plugin('Newslash::Plugin::Preprocessor');
+    if ($maintenance_mode) {
+        $app->log->info("bypassing preprocessor...");
+    }
+    else {
+       $app->preprocessor->generate_all;
+    }
 
-    # quasi-static content
-    $app->plugin('Newslash::Plugin::QuasiStaticContent');
+    # javascript loader
+    $app->plugin('Newslash::Plugin::JavaScriptLoader');
+    if ($maintenance_mode) {
+        $app->log->info("bypassing javascriptloader...");
+    }
+    else {
+       $app->javascript_loader->load_all_bundles;
+    }
 
     # user authorization
     $app->plugin('Newslash::Plugin::UserAuth');
@@ -89,37 +184,44 @@ sub startup {
     # access control
     $app->plugin('Newslash::Plugin::AccessControl');
 
+    # ReCaptcha control
+    $app->plugin('Newslash::Plugin::ReCaptcha');
+
+    # set canocal (for test.srad.jp)
+    $app->plugin('Newslash::Plugin::Canonical');
+
+    # DiscussionHelper
+    $app->plugin('Newslash::Plugin::DiscussionHelper');
+
+    # use HSTS
+    $app->plugin('Newslash::Plugin::Hsts');
+
     # Event Que
     $app->plugin('Newslash::Plugin::EventQue');
 
+    # Statics Logger
+    $app->plugin('Newslash::Plugin::Statics');
 
-    ############################################################
-    #
-    # Generate site-global used javascript file
-    #
-    ############################################################
-    my $templ_name = "common/siteconfig.js";
-    my $mod_reasons = $app->model('moderations')->reasons();
-    my $topics = $app->model('tags')->get_topics;
-    my @acl2_types = $app->model('users')->acl2_types;
-    my $keywords = {};
-    for my $topic (@$topics) {
-        my $lc_keyword = lc($topic->{keyword});
-        my $lc_textname = lc($topic->{textname});
-        $keywords->{$lc_keyword} = {keyword => $topic->{keyword},
-                                    textname => $topic->{textname},
-                                    image => $topic->{image}};
-        if ($lc_keyword ne $lc_textname) {
-            $keywords->{$lc_textname} = $keywords->{$lc_keyword};
-        }
-    }
-    my $vars = {
-                moderate_reasons => $mod_reasons,
-                topics => $keywords,
-                acl2_types => \@acl2_types,
-               };
-    my $siteconfig = $app->tt2renderer->render($templ_name, $vars);
-    $app->static_content->add_content("js/siteconfig.js", $siteconfig, "text/javascript; charset=utf-8");
+    # Request Body based routing condition
+    $app->plugin('Newslash::Plugin::RequestBodyCondition');
+
+    # NS-RPC
+    $app->plugin('Newslash::Plugin::NSRPC');
+
+    # AD renderer
+    $app->plugin('Newslash::Plugin::ADRenderer');
+
+    # HTTP Compression
+    $app->plugin('Newslash::Plugin::HttpCompression');
+
+    # Users helper
+    $app->plugin('Newslash::Plugin::Users');
+
+    # Stories helper
+    $app->plugin('Newslash::Plugin::Stories');
+
+    # Sendmail helper
+    $app->plugin('Newslash::Plugin::Sendmail');
 
     ############################################################
     #
@@ -130,92 +232,163 @@ sub startup {
     my $r = $app->routes;
 
     # index page
-    $r->get('/')->to('index#root');
+    $r->get('/')->to('timeline#stories');
     $r->get('/recent')->to('timeline#recent');
-    $r->get('/journals')->to('index#journals');
-    $r->get('/comments')->to('index#comments');
-    $r->get('/submissions')->to('index#submissions');
+    $r->get('/popular')->to('timeline#popular');
+    $r->get('/comments')->to('timeline#comments');
+    $r->get('/journals')->to('timeline#journals');
+    $r->get('/submissions')->to('timeline#submissions');
+    $r->get('/polls')->to('timeline#polls');
+
+    # RSS
+    $r->get('/rss/sradjp' => [format => ['rss', 'xml']])->to('rss#sradjp');
 
     # Banned page
     $r->get('/banned')->to('index#banned', noindex => 1);
 
-    # archive page
-    $r->get('/story/:year/:month/:day/' => [year => qr/[0-9]{2}/,
-                                            month => qr/[0-9]{2}/,
-                                            day => qr/[0-9]{2}/])->to('index#story_archive');
-
-
     # Login / Logout
     $r->get('/login')->to('login#login');
     $r->post('/login')->to('login#login');
     $r->get('/logout')->to('login#logout');
 
-    # User Register
-    $r->get('/my/newuser')->to('login#newuser');
-    $r->post('/my/newuser')->to('login#newuser');
-
     # story page
     $r->get('/story/:sid/' => [sid => qr|\d\d/\d\d/\d\d/\d+|])
-      ->to('story#story');
+      ->to('story#single');
+
+    # comment page
+    $r->get('/comment/:cid/')->to('comment#single');
 
     # journal page
     $r->get('/journal/new')->to('journal#create', seclev => 1);
-    $r->get('/journal/:id/')->to('journal#journal');
+    $r->get('/journal/:id/')->to('journal#single');
 
     # submission page
     $r->get('/submission/new')->to('submission#create');
-    $r->get('/submission/:id/')->to('submission#submission');
+    $r->get('/submission/:id/')->to('submission#single');
     #$r->post('/submission')->to('submission#create');
 
+    # polls page
+    $r->get('/poll/:qid')->to('poll#single');
+    $r->get('/vote/:qid')->to('poll#vote');
+    $r->post('/vote/:qid')->to('poll#vote_post', csrf_check_id => 'vote');
+
+    # archive page
+    $r->get('/story/:year/:month/:day/')->to('archive#story');
+    $r->get('/story/:year/:month/')->to('archive#story');
+    $r->get('/story/')->to('archive#story');
+
+    $r->get('/journal/:year/:month/:day/')->to('archive#journal');
+    $r->get('/journal/:year/:month/')->to('archive#journal');
+    $r->get('/journal/')->to('archive#journal');
+
+    $r->get('/submission/:year/:month/:day/')->to('archive#submission');
+    $r->get('/submission/:year/:month/')->to('archive#submission');
+    $r->get('/submission/')->to('archive#submission');
+
+    $r->get('/poll/:year/:month/')->to('archive#poll');
+    $r->get('/poll/')->to('archive#poll');
+
+    # tag page
+    $r->get('/tag/:tagname/:type/')->to('tag#list_tagged_items');
+    $r->get('/tag/:tagname/')->to('tag#list_tagged_items');
+    $r->get('/tag/')->to('tag#list_tags');
+
     # my page
-    $r->get('/my/settings')->to('user#settings', seclev => 1);
-    $r->get('/my/sidebar')->to('user#sidebar', seclev => 1);
+    $r->get('/my/settings')->to('my#settings', seclev => 1);
+    $r->get('/my/sidebar')->to('my#sidebar', seclev => 1);
     $r->get('/my/messages')->to('my#messages', seclev => 1);
     $r->get('/my/')->to('user#home', seclev => 1);
 
+    # User Register
+    $r->get('/my/newuser')->to('login#newuser');
+    $r->post('/my/newuser')->to('login#newuser', captcha_check => 1);
+    $r->get('/my/activation')->to('login#activation');
+    $r->get('/my/resetpassword')->to('login#reset_password');
+    $r->post('/my/resetpassword')->to('login#reset_password', captcha_check => 1);
+
+    # Change Email
+    $r->get('/my/change_email')->to('my#change_email', seclev => 1);
+
+    # search page
+    $r->get('/search')->to('search#search');
+
     # Admin
     # pages under /admin needs seclev equal or greater than 10000;
     my $admin = $r->under('/admin' => sub { my $c = shift; $c->stash(seclev => 10000); return 1; });
 
+    $admin->get('/firehose/:id/')->to('admin-firehose#single');
     $admin->get('/submissions')->to('admin-submissions#index');
-    $admin->get('/submissions/list')->to('admin-submissions#list');
 
     $admin->get('/css')->to('admin-css#edit');
     $admin->get('/story/edit')->to('admin-story#edit');
 
     $admin->get('/users')->to('admin-users#index');
 
+    $admin->get('/default-sidebar')->to('admin-sidebar#defaults');
+
     $admin->get('/sidebar')->to('admin-sidebar#index');
-    $admin->post('/sidebar/update')->to('admin-sidebar#update');
-    $admin->post('/sidebar/delete')->to('admin-sidebar#delete');
-    $admin->any(['GET', 'POST'] => '/sidebar/list')->to('admin-sidebar#list');
-    $admin->post('/sidebar/get')->to('admin-sidebar#get');
+    $admin->get('/feed')->to('admin-feed#index');
+    $admin->get('/blocking')->to('admin-blocking#index');
+    $admin->get('/ad')->to('admin-ads#index');
+
+    $admin->get('/repository')->to('admin-repository#index');
+    $admin->get('/cache')->to('admin-config#cache');
+
+    # Admin API
+    # pages under /api/v1/admin needs seclev equal or greater than 10000;
+    my $admin_api = $r->under('/api/v1/admin' => sub { my $c = shift; $c->stash(seclev => 10000); return 1; });
+    $admin_api->get('/feed')->to('API::Admin::Feed#get');
+    $admin_api->post('/feed')->to('API::Admin::Feed#post');
+    $admin_api->get('/blocking')->to('API::Admin::Blocking#get');
+    $admin_api->post('/blocking')->to('API::Admin::Blocking#post');
+
+    $admin_api->get('/repository/export')->to('API::Admin::Repository#export');
+    $admin_api->get('/repository/import')->to('API::Admin::Repository#import');
+
+    $admin_api->post('/sidebar')->to('API::Admin::Sidebar#post');
+    $admin_api->get('/sidebar')->to('API::Admin::Sidebar#get');
+
+    # ad codes management
+    $app->rpc->route_to_model($admin_api->get('/ad/code/'), 'ad_codes', 'select');
+    $app->rpc->route_to_model($admin_api->post('/ad/code/')->over(request_body => {action => "create"}), 'ad_codes', 'create');
+    $app->rpc->route_to_model($admin_api->post('/ad/code/')->over(request_body => {action => "update"}), 'ad_codes', 'update');
+    $app->rpc->route_to_model($admin_api->post('/ad/code/')->over(request_body => {action => "delete"}), 'ad_codes', 'delete');
 
     # API
     my $api = $r->under('/api/v1');
-    $api->get('/sidebars')->to('admin-sidebar#list');
-
     $api->post('/login')->to('API::Login#login');
 
+    $api->post('/newuser/validate')->to('API::User#validate_new_user');
+    $api->post('/newuser/create')->to('API::User#create_new_user');
+    $api->post('/newuser/password')->to('API::User#newuser_password');
+
+    $api->get('/sidebar/item')->to('API::SidebarItem#get', seclev => 1);
+
     $api->get('/comment')->to('API::Comment#get');
-    $api->post('/comment')->to('API::Comment#post');
+    $api->post('/comment')->to('API::Comment#post', captcha_check => 1, csrf_check_id => 'comment');
 
     $api->get('/user')->to('API::User#get');
     $api->post('/user')->to('API::User#post', seclev => 1);
 
     $api->get('/journal')->to('API::Journal#get');
-    $api->post('/journal')->to('API::Journal#post', seclev => 1);
+    $api->post('/journal')->to('API::Journal#post', seclev => 1, csrf_check_id => 'journal');
 
     $api->get('/submission')->to('API::Submission#get');
-    $api->post('/submission')->to('API::Submission#post');
+    $api->get('/submissions')->to('API::Submission#list');
+    $api->post('/submission')->to('API::Submission#post', captcha_check => 1, csrf_check_id => 'submission');
+
     $api->get('/story')->to('API::Story#get');
     $api->post('/story')->to('API::Story#post');
 
+    $api->get('/poll')->to('API::Poll#get');
+    $api->post('/poll')->to('API::Poll#post');
+    $api->post('/vote')->to('API::Poll#vote', csrf_check_id => 'vote');
+
+    $api->get('/moderation')->to('API::Moderation#get');
     $api->post('/moderation')->to('API::Moderation#post', seclev => 1, csrf_check_id => 'moderation');
-    $api->get('/moderation')->to('API::Moderation#get',, csrf_check_id => 'moderation');
 
+    $api->get('/metamoderation')->to('API::Metamoderation#get');
     $api->post('/metamoderation')->to('API::Metamoderation#post', seclev => 1, csrf_check_id => 'moderation');
-    $api->get('/metamoderation')->to('API::Metamoderation#get', csrf_check_id => 'moderation');
 
     $api->post('/relation')->to('API::Relation#post', seclev => 1, csrf_check_id => 'relation');
 
@@ -226,6 +399,7 @@ sub startup {
     my $user = $r->under('/:nickname');
     $user->get('/'             => [nickname => qr/~.*/])->to('user#home');
     $user->get('/journals'     => [nickname => qr/~.*/])->to('user#journals');
+    $user->get('/journal'      => [nickname => qr/~.*/])->to('user#journals'); # for compatibility
     $user->get('/comments'     => [nickname => qr/~.*/])->to('user#comments');
     $user->get('/submissions'  => [nickname => qr/~.*/])->to('user#submissions');
     $user->get('/friends'      => [nickname => qr/~.*/])->to('user#friends');