2 use Mojo::Base 'Mojolicious';
3 use Mojo::Util qw(dumper);
7 use constant CONFIG_FILE => '/etc/newslash/newslash.conf';
9 # This method will run once at server start
13 # add commands in Newslash::Command
14 push @{$app->commands->namespaces}, 'Newslash::Command';
17 # first, check existence of /etc/newslash.conf
18 if ($app->mode eq 'production' && -e CONFIG_FILE) {
19 $app->plugin('Newslash::Plugin::YAMLConfig', file => CONFIG_FILE);
22 if ($ARGV[0] && $ARGV[0] eq "configdump") {
23 $app->plugin('Newslash::Plugin::YAMLConfig', default => {});
26 $app->plugin('Newslash::Plugin::YAMLConfig');
30 # load default configuration values
31 $app->plugin('Newslash::Plugin::DefaultConfig');
33 # TODO: load/save configs with database
35 if ($app->config->{Log} && $app->config->{Log}->{backtrace}) {
41 if ($app->config->{Log} && $app->config->{Log}->{system_log}) {
42 # check log is writable
43 my $pathname = $app->config->{Log}->{system_log};
46 $app->log->debug("logs will be outputed to $pathname ...");
47 $app->log(Mojo::Log->new);
48 $app->log->path($pathname);
51 $app->log->error("cannot write system log to file: $pathname");
55 if (open(my $fh, ">", $pathname)) {
57 $app->log->debug("logs will be outputed to $pathname ...");
58 $app->log(Mojo::Log->new);
59 $app->log->path($pathname);
62 $app->log->error("cannot create system log file: $pathname");
68 if ($app->config->{Log} && $app->config->{Log}->{level}) {
69 my $loglv = $app->config->{Log}->{level};
70 if (grep { $loglv eq $_ } qw(debug info warn error fatal)) {
71 $app->log->level($loglv);
74 $app->log->warn('invalid log level given in config file');
78 # add static contents directories
79 if ($app->config->{System} && $app->config->{System}->{static_dir}) {
80 for my $dir (@{$app->config->{System}->{static_dir}}) {
81 push @{$app->static->paths}, $dir;
85 ############################################################
89 ############################################################
91 # when "test" mode, output debug logs.
92 $app->log->level('debug') if $app->mode eq 'test';
94 if ($app->mode eq 'development'
95 && $app->config->{Profiler}
96 && $app->config->{Profiler}->{enable}) {
97 if ($app->config->{Profiler}->{profiler} eq 'nytprof') {
98 $app->plugin('Mojolicious::Plugin::NYTProf', { nytprof => {} });
103 $app->plugin('Newslash::Plugin::AccessLog::Debug', $app->config->{Log} || {});
104 $app->plugin('Newslash::Plugin::AccessLog::LocalFile', $app->config->{Log} || {});
106 # secret key for hasing
107 $app->secrets([$app->config->{System}->{secret_key},]);
109 # Helpers for Newslash
110 $app->plugin('Newslash::Plugin::NewslashHelpers');
113 $app->plugin('Newslash::Plugin::Epoch');
116 if ($app->config->{BasicAuth} && $app->config->{BasicAuth}->{enable}) {
117 $app->plugin('Newslash::Plugin::BasicAuth');
120 # use TimeLimitedCache ($app->cache)
121 $app->plugin('Newslash::Plugin::TimeLimitedCache');
123 # use KeyValue Store ($app->kvs)
124 $app->plugin('Newslash::Plugin::KeyValueStore');
127 my $readonly = $app->config->{System} ? $app->config->{System}->{readonly} : 0;
128 $app->helper(model => Newslash::Model::loader($app->config,
130 readonly => $readonly,
132 $app->helper(db => sub { state $db = Newslash::Model::Base->new($app->config,
134 readonly => $readonly
138 && $ARGV[0] ne "databaseinit"
139 && $ARGV[0] ne "configdump") {
140 Newslash::Model::startup($app->config,
143 readonly => $readonly,
147 $app->log->info("bypassing model startup...");
150 # use Easy Cache ($app->ezcache)
151 $app->plugin('Newslash::Plugin::EasyCache');
153 # use Template::Toolkit 2 render
154 $app->plugin('Newslash::Plugin::TT2Renderer');
157 $app->plugin('Newslash::Plugin::CustomBoxes');
159 # user AntiCsrf ($app->anti_csrf)
160 if ($app->mode ne 'test') {
161 # when test mode, disable AntiCsrf.
162 $app->plugin('Newslash::Plugin::AntiCsrf');
165 # contents preprocessor
166 $app->plugin('Newslash::Plugin::Preprocessor');
168 && $ARGV[0] ne "databaseinit"
169 && $ARGV[0] ne "configdump") {
170 $app->preprocessor->generate_all;
173 $app->log->info("bypassing preprocessor...");
177 $app->plugin('Newslash::Plugin::JavaScriptLoader');
179 && $ARGV[0] ne "databaseinit"
180 && $ARGV[0] ne "configdump") {
181 $app->javascript_loader->load_all;
184 $app->log->info("bypassing javasciprt loading...");
188 $app->plugin('Newslash::Plugin::UserAuth');
191 $app->plugin('Newslash::Plugin::AccessControl');
194 $app->plugin('Newslash::Plugin::ReCaptcha');
196 # set canocal (for test.srad.jp)
197 $app->plugin('Newslash::Plugin::Canonical');
200 $app->plugin('Newslash::Plugin::DiscussionHelper');
203 $app->plugin('Newslash::Plugin::Hsts');
206 $app->plugin('Newslash::Plugin::EventQue');
209 $app->plugin('Newslash::Plugin::Statics');
211 # Request Body based routing condition
212 $app->plugin('Newslash::Plugin::RequestBodyCondition');
215 $app->plugin('Newslash::Plugin::NSRPC');
218 $app->plugin('Newslash::Plugin::ADRenderer');
221 ############################################################
225 ############################################################
227 my $r = $app->routes;
230 $r->get('/')->to('timeline#stories');
231 $r->get('/recent')->to('timeline#recent');
232 $r->get('/popular')->to('timeline#popular');
233 $r->get('/comments')->to('timeline#comments');
234 $r->get('/journals')->to('timeline#journals');
235 $r->get('/submissions')->to('timeline#submissions');
236 $r->get('/polls')->to('timeline#polls');
239 $r->get('/banned')->to('index#banned', noindex => 1);
242 $r->get('/login')->to('login#login');
243 $r->post('/login')->to('login#login');
244 $r->get('/logout')->to('login#logout');
247 $r->get('/my/newuser')->to('login#newuser');
248 $r->post('/my/newuser')->to('login#newuser', captcha_check => 1);
251 $r->get('/story/:sid/' => [sid => qr|\d\d/\d\d/\d\d/\d+|])
252 ->to('story#single');
255 $r->get('/comment/:cid/')->to('comment#single');
258 $r->get('/journal/new')->to('journal#create', seclev => 1);
259 $r->get('/journal/:id/')->to('journal#single');
262 $r->get('/submission/new')->to('submission#create');
263 $r->get('/submission/:id/')->to('submission#single');
264 #$r->post('/submission')->to('submission#create');
267 $r->get('/poll/:qid')->to('poll#single');
268 $r->get('/vote/:qid')->to('poll#vote');
269 $r->post('/vote/:qid')->to('poll#vote_post', csrf_check_id => 'vote');
272 $r->get('/story/:year/:month/:day/')->to('archive#story');
273 $r->get('/story/:year/:month/')->to('archive#story');
274 $r->get('/story/')->to('archive#story');
276 $r->get('/journal/:year/:month/:day/')->to('archive#journal');
277 $r->get('/journal/:year/:month/')->to('archive#journal');
278 $r->get('/journal/')->to('archive#journal');
280 $r->get('/submission/:year/:month/:day/')->to('archive#submission');
281 $r->get('/submission/:year/:month/')->to('archive#submission');
282 $r->get('/submission/')->to('archive#submission');
284 $r->get('/poll/:year/:month/')->to('archive#poll');
285 $r->get('/poll/')->to('archive#poll');
288 $r->get('/tag/:tagname/:type/')->to('tag#list_tagged_items');
289 $r->get('/tag/:tagname/')->to('tag#list_tagged_items');
290 $r->get('/tag/')->to('tag#list_tags');
293 $r->get('/my/settings')->to('my#settings', seclev => 1);
294 $r->get('/my/sidebar')->to('my#sidebar', seclev => 1);
295 $r->get('/my/messages')->to('my#messages', seclev => 1);
296 $r->get('/my/')->to('user#home', seclev => 1);
299 $r->get('/search')->to('search#search');
302 # pages under /admin needs seclev equal or greater than 10000;
303 my $admin = $r->under('/admin' => sub { my $c = shift; $c->stash(seclev => 10000); return 1; });
305 $admin->get('/firehose/:id/')->to('admin-firehose#single');
306 $admin->get('/submissions')->to('admin-submissions#index');
308 $admin->get('/css')->to('admin-css#edit');
309 $admin->get('/story/edit')->to('admin-story#edit');
311 $admin->get('/users')->to('admin-users#index');
313 $admin->get('/default-sidebar')->to('admin-sidebar#defaults');
315 $admin->get('/sidebar')->to('admin-sidebar#index');
316 $admin->get('/feed')->to('admin-feed#index');
317 $admin->get('/blocking')->to('admin-blocking#index');
318 $admin->get('/ad')->to('admin-ads#index');
320 $admin->get('/repository')->to('admin-repository#index');
321 $admin->get('/cache')->to('admin-config#cache');
324 # pages under /api/v1/admin needs seclev equal or greater than 10000;
325 my $admin_api = $r->under('/api/v1/admin' => sub { my $c = shift; $c->stash(seclev => 10000); return 1; });
326 $admin_api->get('/feed')->to('API::Admin::Feed#get');
327 $admin_api->post('/feed')->to('API::Admin::Feed#post');
328 $admin_api->get('/blocking')->to('API::Admin::Blocking#get');
329 $admin_api->post('/blocking')->to('API::Admin::Blocking#post');
331 $admin_api->get('/repository/export')->to('API::Admin::Repository#export');
332 $admin_api->get('/repository/import')->to('API::Admin::Repository#import');
334 $admin_api->post('/sidebar')->to('API::Admin::Sidebar#post');
335 $admin_api->get('/sidebar')->to('API::Admin::Sidebar#get');
337 # ad codes management
338 $app->rpc->route_to_model($admin_api->get('/ad/code/'), 'ad_codes', 'select');
339 $app->rpc->route_to_model($admin_api->post('/ad/code/')->over(request_body => {action => "create"}), 'ad_codes', 'create');
340 $app->rpc->route_to_model($admin_api->post('/ad/code/')->over(request_body => {action => "update"}), 'ad_codes', 'update');
341 $app->rpc->route_to_model($admin_api->post('/ad/code/')->over(request_body => {action => "delete"}), 'ad_codes', 'delete');
344 my $api = $r->under('/api/v1');
345 $api->post('/login')->to('API::Login#login');
347 $api->get('/sidebar/item')->to('API::SidebarItem#get', seclev => 1);
349 $api->get('/comment')->to('API::Comment#get');
350 $api->post('/comment')->to('API::Comment#post', captcha_check => 1, csrf_check_id => 'comment');
352 $api->get('/user')->to('API::User#get');
353 $api->post('/user')->to('API::User#post', seclev => 1);
355 $api->get('/journal')->to('API::Journal#get');
356 $api->post('/journal')->to('API::Journal#post', seclev => 1, csrf_check_id => 'journal');
358 $api->get('/submission')->to('API::Submission#get');
359 $api->get('/submissions')->to('API::Submission#list');
360 $api->post('/submission')->to('API::Submission#post', captcha_check => 1, csrf_check_id => 'submission');
362 $api->get('/story')->to('API::Story#get');
363 $api->post('/story')->to('API::Story#post');
365 $api->get('/poll')->to('API::Poll#get');
366 $api->post('/poll')->to('API::Poll#post');
367 $api->post('/vote')->to('API::Poll#vote', csrf_check_id => 'vote');
369 $api->get('/moderation')->to('API::Moderation#get');
370 $api->post('/moderation')->to('API::Moderation#post', seclev => 1, csrf_check_id => 'moderation');
372 $api->get('/metamoderation')->to('API::Metamoderation#get');
373 $api->post('/metamoderation')->to('API::Metamoderation#post', seclev => 1, csrf_check_id => 'moderation');
375 $api->post('/relation')->to('API::Relation#post', seclev => 1, csrf_check_id => 'relation');
377 $api->get('/token')->to('API::Token#get');
380 # warning: these pathes uses regexp matching, so must write in tail of route definitions.
381 my $user = $r->under('/:nickname');
382 $user->get('/' => [nickname => qr/~.*/])->to('user#home');
383 $user->get('/journals' => [nickname => qr/~.*/])->to('user#journals');
384 $user->get('/journal' => [nickname => qr/~.*/])->to('user#journals'); # for compatibility
385 $user->get('/comments' => [nickname => qr/~.*/])->to('user#comments');
386 $user->get('/submissions' => [nickname => qr/~.*/])->to('user#submissions');
387 $user->get('/friends' => [nickname => qr/~.*/])->to('user#friends');
388 $user->get('/foes' => [nickname => qr/~.*/])->to('user#foes');
389 $user->get('/fans' => [nickname => qr/~.*/])->to('user#fans');
390 $user->get('/freaks' => [nickname => qr/~.*/])->to('user#freaks');
391 $user->get('/achievements' => [nickname => qr/~.*/])->to('user#achievements');
392 #$r->get('/:user_name/journal' => [user_name => qr/~.*/])->to('journal#user_journals');