2 use Mojo::Base 'Mojolicious';
3 use Mojo::Util qw(dumper);
4 use List::Util qw(any);
6 use constant CONFIG_FILE => '/etc/newslash/newslash.conf';
9 # This method will run once at server start
14 $app->subcommand($ARGV[0]);
17 # add commands in Newslash::Command
18 push @{$app->commands->namespaces}, 'Newslash::Command';
21 # first, check existence of /etc/newslash.conf
22 if ($app->mode eq 'production' && -e CONFIG_FILE) {
23 $app->plugin('Newslash::Plugin::YAMLConfig', file => CONFIG_FILE);
26 if ($app->subcommand eq "configdump") {
27 $app->plugin('Newslash::Plugin::YAMLConfig', default => {});
30 $app->plugin('Newslash::Plugin::YAMLConfig');
34 # load default configuration values
35 $app->plugin('Newslash::Plugin::DefaultConfig');
37 # TODO: load/save configs with database
39 if ($app->config->{Log} && $app->config->{Log}->{backtrace}) {
45 if ($app->config->{Log} && $app->config->{Log}->{system_log}) {
46 my $cnf = $app->config->{Log}->{system_log};
50 my $loglv = $cnf->{level};
51 if (grep { $loglv eq $_ } qw(debug info warn error fatal)) {
52 $app->log->level($loglv);
53 $app->log->info("set log level to $loglv");
56 $app->log->warn('invalid log level given in config file');
60 # file output settings
61 if ($cnf->{mode} eq "local_file") {
62 my $pathname = $cnf->{local_file};
63 # check log is writable
65 $app->log->error("cannot write system log to file: filename not given");
67 elsif (-e $pathname) {
69 $app->log->debug("logs will be outputed to $pathname ...");
70 $app->log(Mojo::Log->new);
71 $app->log->path($pathname);
74 $app->log->error("cannot write system log to file: $pathname");
78 if (open(my $fh, ">", $pathname)) {
80 $app->log->debug("logs will be outputed to $pathname ...");
81 $app->log(Mojo::Log->new);
82 $app->log->path($pathname);
85 $app->log->error("cannot create system log file: $pathname");
91 # add static contents directories
92 if ($app->config->{System} && $app->config->{System}->{static_dir}) {
93 for my $dir (@{$app->config->{System}->{static_dir}}) {
94 push @{$app->static->paths}, $dir;
98 # secret key for hasing
99 $app->secrets([$app->config->{System}->{secret_key},]);
101 # when "test" mode, output debug logs.
102 $app->log->level('debug') if $app->mode eq 'test';
104 # check if 'maintenance' mode
105 my $maintenance_mode = 0;
107 && any { $app->subcommand eq $_ } qw[configdump configimport databaseinit
108 testdatainsert useradd usermod
110 $maintenance_mode = 1;
113 ############################################################
117 ############################################################
120 if ($app->mode eq 'development'
121 && $app->config->{Profiler}
122 && $app->config->{Profiler}->{enable}) {
123 if ($app->config->{Profiler}->{profiler} eq 'nytprof') {
124 $app->plugin('Mojolicious::Plugin::NYTProf', { nytprof => {} });
128 # enable access logging
129 $app->plugin('Newslash::Plugin::AccessLog');
131 # Helpers for Newslash
132 $app->plugin('Newslash::Plugin::NewslashHelpers');
135 $app->plugin('Newslash::Plugin::Epoch');
138 $app->plugin('Newslash::Plugin::BasicAuth');
140 # use TimeLimitedCache ($app->cache)
141 $app->plugin('Newslash::Plugin::TimeLimitedCache');
143 # use KeyValue Store ($app->kvs)
144 $app->plugin('Newslash::Plugin::KeyValueStore');
147 $app->plugin('Newslash::Plugin::Model', { bypass_startup => $maintenance_mode });
149 # use Easy Cache ($app->ezcache)
150 #$app->plugin('Newslash::Plugin::EasyCache');
152 # use CompositeCache Cache ($app->ccache)
153 $app->plugin('Newslash::Plugin::CompositeCache');
155 # use Template::Toolkit 2 render
156 $app->plugin('Newslash::Plugin::TT2Renderer');
158 # use ResponseFilter (Faculities)
159 $app->plugin('Newslash::Plugin::ResponseFilter');
162 $app->plugin('Newslash::Plugin::CustomBoxes');
165 $app->plugin('Newslash::Plugin::AntiCsrf');
167 # contents preprocessor
168 $app->plugin('Newslash::Plugin::Preprocessor');
169 if ($maintenance_mode) {
170 $app->log->info("bypassing preprocessor...");
173 $app->preprocessor->generate_all;
177 $app->plugin('Newslash::Plugin::JavaScriptLoader');
178 if ($maintenance_mode) {
179 $app->log->info("bypassing javascriptloader...");
182 $app->javascript_loader->load_all_bundles;
186 $app->plugin('Newslash::Plugin::UserAuth');
189 $app->plugin('Newslash::Plugin::AccessControl');
192 $app->plugin('Newslash::Plugin::ReCaptcha');
195 $app->plugin('Newslash::Plugin::PostFilter');
197 # set canocal (for test.srad.jp)
198 $app->plugin('Newslash::Plugin::Canonical');
201 $app->plugin('Newslash::Plugin::DiscussionHelper');
204 $app->plugin('Newslash::Plugin::Hsts');
207 $app->plugin('Newslash::Plugin::EventQue');
210 $app->plugin('Newslash::Plugin::Stats');
212 # Request Body based routing condition
213 $app->plugin('Newslash::Plugin::RequestBodyCondition');
216 $app->plugin('Newslash::Plugin::NSRPC');
219 $app->plugin('Newslash::Plugin::ADRenderer');
222 $app->plugin('Newslash::Plugin::HttpCompression');
225 $app->plugin('Newslash::Plugin::Users');
228 $app->plugin('Newslash::Plugin::Stories');
231 $app->plugin('Newslash::Plugin::Sendmail');
233 # Wiki contents reader helper
234 $app->plugin('Newslash::Plugin::WikiContentsReader');
236 ############################################################
240 ############################################################
242 my $r = $app->routes;
245 $r->get('/')->to('timeline#stories');
246 $r->get('/recent')->to('timeline#recent');
247 $r->get('/popular')->to('timeline#popular');
248 $r->get('/comments')->to('timeline#comments');
249 $r->get('/journals')->to('timeline#journals');
250 $r->get('/submissions')->to('timeline#submissions');
251 $r->get('/polls')->to('timeline#polls');
254 $r->get('/:rss_type' => [format => ['rss', 'xml']])->to('rss#stories');
255 $r->get('/journals/rss')->to('rss#journals');
258 $r->get('/banned')->to('index#banned', noindex => 1);
261 $r->get('/login')->to('login#login');
262 $r->post('/login')->to('login#login');
263 $r->get('/logout')->to('login#logout');
266 $r->get('/story/:sid/' => [sid => qr|\d\d/\d\d/\d\d/\d+|])
267 ->to('story#single');
270 $r->get('/comment/:cid/')->to('comment#single');
273 # also see user page settings for /~username/journal/
274 $r->get('/journal/new')->to('journal#create', seclev => 1);
275 $r->get('/journal/:id/')->to('journal#single');
278 $r->get('/submission/new')->to('submission#create');
279 $r->get('/submission/:id/')->to('submission#single');
280 #$r->post('/submission')->to('submission#create');
283 $r->get('/poll/:qid')->to('poll#single');
284 $r->get('/vote/:qid')->to('poll#vote');
285 $r->post('/vote/:qid')->to('poll#vote_post', csrf_check_id => 'vote');
288 $r->get('/story/:year/:month/:day/')->to('archive#story');
289 $r->get('/story/:year/:month/')->to('archive#story');
290 $r->get('/story/')->to('archive#story');
292 $r->get('/journal/:year/:month/:day/')->to('archive#journal');
293 $r->get('/journal/:year/:month/')->to('archive#journal');
294 $r->get('/journal/')->to('archive#journal');
296 $r->get('/submission/:year/:month/:day/')->to('archive#submission');
297 $r->get('/submission/:year/:month/')->to('archive#submission');
298 $r->get('/submission/')->to('archive#submission');
300 $r->get('/comment/:year/:month/:day/')->to('archive#comment');
301 $r->get('/comment/:year/:month/')->to('archive#comment');
302 $r->get('/comment/')->to('archive#comment');
304 $r->get('/poll/:year/:month/')->to('archive#poll');
305 $r->get('/poll/')->to('archive#poll');
308 $r->get('/tag/:tagname/:type/')->to('tag#list_tagged_items');
309 $r->get('/tag/:tagname/')->to('tag#list_tagged_items');
310 $r->get('/tag/')->to('tag#list_tags');
313 $r->get('/my/settings')->to('my#settings', seclev => 1);
314 $r->get('/my/sidebar')->to('my#sidebar', seclev => 1);
315 $r->get('/my/messages')->to('my#messages', seclev => 1);
316 $r->get('/my/')->to('user#home', seclev => 1);
319 $r->get('/my/newuser')->to('login#newuser');
320 $r->post('/my/newuser')->to('login#newuser', captcha_check => 1);
321 $r->get('/my/activation')->to('login#activation');
322 $r->get('/my/resetpassword')->to('login#reset_password');
323 $r->post('/my/resetpassword')->to('login#reset_password', captcha_check => 1);
326 $r->get('/my/change_email')->to('my#change_email', seclev => 1);
329 $r->get('/search')->to('search#search');
332 $r->get('/about')->to('about#about');
335 # pages under /admin needs seclev equal or greater than 10000;
336 my $admin = $r->under('/admin' => sub { my $c = shift; $c->stash(seclev => 10000); return 1; });
338 $admin->get('/firehose/:id/')->to('admin-firehose#single');
339 $admin->get('/submissions')->to('admin-submissions#index');
341 $admin->get('/css')->to('admin-css#edit');
342 $admin->get('/story/edit')->to('admin-story#edit');
344 $admin->get('/users')->to('admin-users#index');
346 $admin->get('/default-sidebar')->to('admin-sidebar#defaults');
348 $admin->get('/sidebar')->to('admin-sidebar#index');
349 $admin->get('/feed')->to('admin-feed#index');
350 $admin->get('/filter')->to('admin-filter#index');
351 $admin->get('/blocking')->to('admin-blocking#index');
352 $admin->get('/ad')->to('admin-ads#index');
354 $admin->get('/repository')->to('admin-repository#index');
355 $admin->get('/cache')->to('admin-config#cache');
358 # pages under /api/v1/admin needs seclev equal or greater than 10000;
359 my $admin_api = $r->under('/api/v1/admin' => sub { my $c = shift; $c->stash(seclev => 10000); return 1; });
361 $admin_api->get('/feed')->to('API::Admin::Feed#get');
362 $admin_api->post('/feed')->to('API::Admin::Feed#post');
363 $admin_api->get('/filter')->to('API::Admin::Filter#get');
364 $admin_api->post('/filter')->to('API::Admin::Filter#post');
365 $admin_api->get('/blocking')->to('API::Admin::Blocking#get');
366 $admin_api->post('/blocking')->to('API::Admin::Blocking#post');
368 $admin_api->get('/repository/export')->to('API::Admin::Repository#export');
369 $admin_api->get('/repository/import')->to('API::Admin::Repository#import');
371 $admin_api->post('/sidebar')->to('API::Admin::Sidebar#post');
372 $admin_api->get('/sidebar')->to('API::Admin::Sidebar#get');
374 # ad codes management
375 $app->rpc->route_to_model($admin_api->get('/ad/code/'), 'ad_codes', 'select');
376 $app->rpc->route_to_model($admin_api->post('/ad/code/')->over(request_body => {action => "create"}), 'ad_codes', 'create');
377 $app->rpc->route_to_model($admin_api->post('/ad/code/')->over(request_body => {action => "update"}), 'ad_codes', 'update');
378 $app->rpc->route_to_model($admin_api->post('/ad/code/')->over(request_body => {action => "delete"}), 'ad_codes', 'delete');
381 my $api = $r->under('/api/v1');
382 $api->post('/login')->to('API::Login#login');
384 $api->post('/newuser/validate')->to('API::User#validate_new_user');
385 $api->post('/newuser/create')->to('API::User#create_new_user');
386 $api->post('/newuser/password')->to('API::User#newuser_password');
388 $api->get('/sidebar/item')->to('API::SidebarItem#get', seclev => 1);
390 $api->get('/comment')->to('API::Comment#get');
391 $api->post('/comment')->to('API::Comment#post', captcha_check => 1, csrf_check_id => 'comment');
393 $api->get('/user')->to('API::User#get');
394 $api->post('/user')->to('API::User#post', seclev => 1);
396 $api->get('/journal')->to('API::Journal#get');
397 $api->post('/journal')->to('API::Journal#post', seclev => 1, csrf_check_id => 'journal');
399 $api->get('/submission')->to('API::Submission#get');
400 $api->get('/submissions')->to('API::Submission#list');
401 $api->post('/submission')->to('API::Submission#post', captcha_check => 1, csrf_check_id => 'submission');
403 $api->get('/story')->to('API::Story#get');
404 $api->post('/story')->to('API::Story#post');
406 $api->get('/poll')->to('API::Poll#get');
407 $api->post('/poll')->to('API::Poll#post');
408 $api->post('/vote')->to('API::Poll#vote', csrf_check_id => 'vote');
410 $api->get('/moderation')->to('API::Moderation#get');
411 $api->post('/moderation')->to('API::Moderation#post', seclev => 1, csrf_check_id => 'moderation');
413 $api->get('/metamoderation')->to('API::Metamoderation#get');
414 $api->post('/metamoderation')->to('API::Metamoderation#post', seclev => 1, csrf_check_id => 'moderation');
416 $api->post('/relation')->to('API::Relation#post', seclev => 1, csrf_check_id => 'relation');
418 $api->get('/token')->to('API::Token#get');
421 # warning: these pathes uses regexp matching, so must write in tail of route definitions.
422 my $user = $r->under('/~:nickname');
423 $user->get('/' => [nickname => qr/.*/])->to('user#home');
424 $user->get('/journal/:id' => [nickname => qr/.*/])->to('journal#single');
425 $user->get('/journals' => [nickname => qr/.*/])->to('user#journals');
426 $user->get('/journal' => [nickname => qr/.*/])->to('user#journals'); # for compatibility
427 $user->get('/comments' => [nickname => qr/.*/])->to('user#comments');
428 $user->get('/submissions' => [nickname => qr/.*/])->to('user#submissions');
429 $user->get('/friends' => [nickname => qr/.*/])->to('user#friends');
430 $user->get('/foes' => [nickname => qr/.*/])->to('user#foes');
431 $user->get('/fans' => [nickname => qr/.*/])->to('user#fans');
432 $user->get('/freaks' => [nickname => qr/.*/])->to('user#freaks');
433 $user->get('/achievements' => [nickname => qr/.*/])->to('user#achievements');