OSDN Git Service

Plugin::Stories: add storiees->latest helper
[newslash/newslash.git] / src / newslash_web / lib / Newslash / Web.pm
1 package Newslash::Web;
2 use Mojo::Base 'Mojolicious';
3 use Mojo::Util qw(dumper);
4 use List::Util qw(any);
5
6 use constant CONFIG_FILE => '/etc/newslash/newslash.conf';
7 has subcommand => "";
8
9 # This method will run once at server start
10 sub startup {
11     my $app = shift;
12
13     if ($ARGV[0]) {
14         $app->subcommand($ARGV[0]);
15     }
16
17     # add commands in Newslash::Command
18     push @{$app->commands->namespaces}, 'Newslash::Command';
19
20     # load config file
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);
24     }
25     else {
26         if ($app->subcommand eq "configdump") {
27             $app->plugin('Newslash::Plugin::YAMLConfig', default => {});
28         }
29         else {
30             $app->plugin('Newslash::Plugin::YAMLConfig');
31         }
32     }
33
34     # load default configuration values
35     $app->plugin('Newslash::Plugin::DefaultConfig');
36
37     # TODO: load/save configs with database
38
39     if ($app->config->{Log} && $app->config->{Log}->{backtrace}) {
40         require Carp::Always;
41         Carp::Always->import;
42     }
43
44     # system log config
45     if ($app->config->{Log} && $app->config->{Log}->{system_log}) {
46         my $cnf = $app->config->{Log}->{system_log};
47
48         # set log level
49         if ($cnf->{level}) {
50             my $loglv = $cnf->{level};
51             if (grep { $loglv eq $_ } qw(debug info warn error fatal)) {
52                 $app->log->level($loglv);
53             }
54             else {
55                 $app->log->warn('invalid log level given in config file');
56             }
57         }
58
59         # file output settings
60         if ($cnf->{mode} eq "local_file") {
61             my $pathname = $cnf->{local_file};
62             # check log is writable
63             if (!$pathname) {
64                 $app->log->error("cannot write system log to file: filename not given");
65             }
66             elsif (-e $pathname) {
67                 if (-w $pathname) {
68                     $app->log->debug("logs will be outputed to $pathname ...");
69                     $app->log(Mojo::Log->new);
70                     $app->log->path($pathname);
71                 }
72                 else {
73                     $app->log->error("cannot write system log to file: $pathname");
74                 }
75             }
76             else {
77                 if (open(my $fh, ">", $pathname)) {
78                     close($fh);
79                     $app->log->debug("logs will be outputed to $pathname ...");
80                     $app->log(Mojo::Log->new);
81                     $app->log->path($pathname);
82                 }
83                 else {
84                     $app->log->error("cannot create system log file: $pathname");
85                 }
86             }
87         }
88     }
89
90     # add static contents directories
91     if ($app->config->{System} && $app->config->{System}->{static_dir}) {
92         for my $dir (@{$app->config->{System}->{static_dir}}) {
93             push @{$app->static->paths}, $dir;
94         }
95     }
96
97     # secret key for hasing
98     $app->secrets([$app->config->{System}->{secret_key},]);
99
100     # when "test" mode, output debug logs.
101     $app->log->level('debug') if $app->mode eq 'test';
102
103     # check if 'maintenance' mode
104     my $maintenance_mode = 0;
105     if ($app->subcommand
106         && any { $app->subcommand eq $_ } qw[configdump configimport databaseinit
107                                              testdatainsert useradd usermod
108                                            ]) {
109         $maintenance_mode = 1;
110     }
111
112     ############################################################
113     #
114     # Plugin Settings
115     #
116     ############################################################
117
118     # profiler settings
119     if ($app->mode eq 'development'
120         && $app->config->{Profiler}
121         && $app->config->{Profiler}->{enable}) {
122         if ($app->config->{Profiler}->{profiler} eq 'nytprof') {
123             $app->plugin('Mojolicious::Plugin::NYTProf', { nytprof => {} });
124         }
125     }
126
127     # enable access logging
128     $app->plugin('Newslash::Plugin::AccessLog');
129
130     # Helpers for Newslash
131     $app->plugin('Newslash::Plugin::NewslashHelpers');
132
133     # use Epoch
134     $app->plugin('Newslash::Plugin::Epoch');
135
136     # support BasicAuth
137     $app->plugin('Newslash::Plugin::BasicAuth');
138
139     # use TimeLimitedCache ($app->cache)
140     $app->plugin('Newslash::Plugin::TimeLimitedCache');
141
142     # use KeyValue Store ($app->kvs)
143     $app->plugin('Newslash::Plugin::KeyValueStore');
144
145     # add Model Loader
146     $app->plugin('Newslash::Plugin::Model', { bypass_startup => $maintenance_mode });
147
148     # use Easy Cache ($app->ezcache)
149     $app->plugin('Newslash::Plugin::EasyCache');
150
151     # use Template::Toolkit 2 render
152     $app->plugin('Newslash::Plugin::TT2Renderer');
153
154     # use ResponseFilter (Faculities)
155     $app->plugin('Newslash::Plugin::ResponseFilter');
156
157     # use CustomBoxes
158     $app->plugin('Newslash::Plugin::CustomBoxes');
159
160     # use AntiCsrf
161     $app->plugin('Newslash::Plugin::AntiCsrf');
162
163     # contents preprocessor
164     $app->plugin('Newslash::Plugin::Preprocessor');
165     if ($maintenance_mode) {
166         $app->log->info("bypassing preprocessor...");
167     }
168     else {
169         $app->preprocessor->generate_all;
170     }
171
172     # javascript loader
173     $app->plugin('Newslash::Plugin::JavaScriptLoader');
174     if ($maintenance_mode) {
175         $app->log->info("bypassing javascriptloader...");
176     }
177     else {
178         $app->javascript_loader->load_all_bundles;
179     }
180
181     # user authorization
182     $app->plugin('Newslash::Plugin::UserAuth');
183
184     # access control
185     $app->plugin('Newslash::Plugin::AccessControl');
186
187     # ReCaptcha control
188     $app->plugin('Newslash::Plugin::ReCaptcha');
189
190     # set canocal (for test.srad.jp)
191     $app->plugin('Newslash::Plugin::Canonical');
192
193     # DiscussionHelper
194     $app->plugin('Newslash::Plugin::DiscussionHelper');
195
196     # use HSTS
197     $app->plugin('Newslash::Plugin::Hsts');
198
199     # Event Que
200     $app->plugin('Newslash::Plugin::EventQue');
201
202     # Statics Logger
203     $app->plugin('Newslash::Plugin::Statics');
204
205     # Request Body based routing condition
206     $app->plugin('Newslash::Plugin::RequestBodyCondition');
207
208     # NS-RPC
209     $app->plugin('Newslash::Plugin::NSRPC');
210
211     # AD renderer
212     $app->plugin('Newslash::Plugin::ADRenderer');
213
214     # HTTP Compression
215     $app->plugin('Newslash::Plugin::HttpCompression');
216
217     # Users helper
218     $app->plugin('Newslash::Plugin::Users');
219
220     # Stories helper
221     $app->plugin('Newslash::Plugin::Stories');
222
223     # Sendmail helper
224     $app->plugin('Newslash::Plugin::Sendmail');
225
226     ############################################################
227     #
228     # Routing Settings
229     #
230     ############################################################
231
232     my $r = $app->routes;
233
234     # index page
235     $r->get('/')->to('timeline#stories');
236     $r->get('/recent')->to('timeline#recent');
237     $r->get('/popular')->to('timeline#popular');
238     $r->get('/comments')->to('timeline#comments');
239     $r->get('/journals')->to('timeline#journals');
240     $r->get('/submissions')->to('timeline#submissions');
241     $r->get('/polls')->to('timeline#polls');
242
243     # RSS
244     $r->get('/rss/sradjp' => [format => ['rss', 'xml']])->to('rss#sradjp');
245
246     # Banned page
247     $r->get('/banned')->to('index#banned', noindex => 1);
248
249     # Login / Logout
250     $r->get('/login')->to('login#login');
251     $r->post('/login')->to('login#login');
252     $r->get('/logout')->to('login#logout');
253
254     # story page
255     $r->get('/story/:sid/' => [sid => qr|\d\d/\d\d/\d\d/\d+|])
256       ->to('story#single');
257
258     # comment page
259     $r->get('/comment/:cid/')->to('comment#single');
260
261     # journal page
262     $r->get('/journal/new')->to('journal#create', seclev => 1);
263     $r->get('/journal/:id/')->to('journal#single');
264
265     # submission page
266     $r->get('/submission/new')->to('submission#create');
267     $r->get('/submission/:id/')->to('submission#single');
268     #$r->post('/submission')->to('submission#create');
269
270     # polls page
271     $r->get('/poll/:qid')->to('poll#single');
272     $r->get('/vote/:qid')->to('poll#vote');
273     $r->post('/vote/:qid')->to('poll#vote_post', csrf_check_id => 'vote');
274
275     # archive page
276     $r->get('/story/:year/:month/:day/')->to('archive#story');
277     $r->get('/story/:year/:month/')->to('archive#story');
278     $r->get('/story/')->to('archive#story');
279
280     $r->get('/journal/:year/:month/:day/')->to('archive#journal');
281     $r->get('/journal/:year/:month/')->to('archive#journal');
282     $r->get('/journal/')->to('archive#journal');
283
284     $r->get('/submission/:year/:month/:day/')->to('archive#submission');
285     $r->get('/submission/:year/:month/')->to('archive#submission');
286     $r->get('/submission/')->to('archive#submission');
287
288     $r->get('/poll/:year/:month/')->to('archive#poll');
289     $r->get('/poll/')->to('archive#poll');
290
291     # tag page
292     $r->get('/tag/:tagname/:type/')->to('tag#list_tagged_items');
293     $r->get('/tag/:tagname/')->to('tag#list_tagged_items');
294     $r->get('/tag/')->to('tag#list_tags');
295
296     # my page
297     $r->get('/my/settings')->to('my#settings', seclev => 1);
298     $r->get('/my/sidebar')->to('my#sidebar', seclev => 1);
299     $r->get('/my/messages')->to('my#messages', seclev => 1);
300     $r->get('/my/')->to('user#home', seclev => 1);
301
302     # User Register
303     $r->get('/my/newuser')->to('login#newuser');
304     $r->post('/my/newuser')->to('login#newuser', captcha_check => 1);
305     $r->get('/my/activation')->to('login#activation');
306     $r->get('/my/resetpassword')->to('login#reset_password');
307     $r->post('/my/resetpassword')->to('login#reset_password', captcha_check => 1);
308
309     # Change Email
310     $r->get('/my/change_email')->to('my#change_email', seclev => 1);
311
312     # search page
313     $r->get('/search')->to('search#search');
314
315     # Admin
316     # pages under /admin needs seclev equal or greater than 10000;
317     my $admin = $r->under('/admin' => sub { my $c = shift; $c->stash(seclev => 10000); return 1; });
318
319     $admin->get('/firehose/:id/')->to('admin-firehose#single');
320     $admin->get('/submissions')->to('admin-submissions#index');
321
322     $admin->get('/css')->to('admin-css#edit');
323     $admin->get('/story/edit')->to('admin-story#edit');
324
325     $admin->get('/users')->to('admin-users#index');
326
327     $admin->get('/default-sidebar')->to('admin-sidebar#defaults');
328
329     $admin->get('/sidebar')->to('admin-sidebar#index');
330     $admin->get('/feed')->to('admin-feed#index');
331     $admin->get('/blocking')->to('admin-blocking#index');
332     $admin->get('/ad')->to('admin-ads#index');
333
334     $admin->get('/repository')->to('admin-repository#index');
335     $admin->get('/cache')->to('admin-config#cache');
336
337     # Admin API
338     # pages under /api/v1/admin needs seclev equal or greater than 10000;
339     my $admin_api = $r->under('/api/v1/admin' => sub { my $c = shift; $c->stash(seclev => 10000); return 1; });
340     $admin_api->get('/feed')->to('API::Admin::Feed#get');
341     $admin_api->post('/feed')->to('API::Admin::Feed#post');
342     $admin_api->get('/blocking')->to('API::Admin::Blocking#get');
343     $admin_api->post('/blocking')->to('API::Admin::Blocking#post');
344
345     $admin_api->get('/repository/export')->to('API::Admin::Repository#export');
346     $admin_api->get('/repository/import')->to('API::Admin::Repository#import');
347
348     $admin_api->post('/sidebar')->to('API::Admin::Sidebar#post');
349     $admin_api->get('/sidebar')->to('API::Admin::Sidebar#get');
350
351     # ad codes management
352     $app->rpc->route_to_model($admin_api->get('/ad/code/'), 'ad_codes', 'select');
353     $app->rpc->route_to_model($admin_api->post('/ad/code/')->over(request_body => {action => "create"}), 'ad_codes', 'create');
354     $app->rpc->route_to_model($admin_api->post('/ad/code/')->over(request_body => {action => "update"}), 'ad_codes', 'update');
355     $app->rpc->route_to_model($admin_api->post('/ad/code/')->over(request_body => {action => "delete"}), 'ad_codes', 'delete');
356
357     # API
358     my $api = $r->under('/api/v1');
359     $api->post('/login')->to('API::Login#login');
360
361     $api->post('/newuser/validate')->to('API::User#validate_new_user');
362     $api->post('/newuser/create')->to('API::User#create_new_user');
363     $api->post('/newuser/password')->to('API::User#newuser_password');
364
365     $api->get('/sidebar/item')->to('API::SidebarItem#get', seclev => 1);
366
367     $api->get('/comment')->to('API::Comment#get');
368     $api->post('/comment')->to('API::Comment#post', captcha_check => 1, csrf_check_id => 'comment');
369
370     $api->get('/user')->to('API::User#get');
371     $api->post('/user')->to('API::User#post', seclev => 1);
372
373     $api->get('/journal')->to('API::Journal#get');
374     $api->post('/journal')->to('API::Journal#post', seclev => 1, csrf_check_id => 'journal');
375
376     $api->get('/submission')->to('API::Submission#get');
377     $api->get('/submissions')->to('API::Submission#list');
378     $api->post('/submission')->to('API::Submission#post', captcha_check => 1, csrf_check_id => 'submission');
379
380     $api->get('/story')->to('API::Story#get');
381     $api->post('/story')->to('API::Story#post');
382
383     $api->get('/poll')->to('API::Poll#get');
384     $api->post('/poll')->to('API::Poll#post');
385     $api->post('/vote')->to('API::Poll#vote', csrf_check_id => 'vote');
386
387     $api->get('/moderation')->to('API::Moderation#get');
388     $api->post('/moderation')->to('API::Moderation#post', seclev => 1, csrf_check_id => 'moderation');
389
390     $api->get('/metamoderation')->to('API::Metamoderation#get');
391     $api->post('/metamoderation')->to('API::Metamoderation#post', seclev => 1, csrf_check_id => 'moderation');
392
393     $api->post('/relation')->to('API::Relation#post', seclev => 1, csrf_check_id => 'relation');
394
395     $api->get('/token')->to('API::Token#get');
396
397     # user page
398     # warning: these pathes uses regexp matching, so must write in tail of route definitions.
399     my $user = $r->under('/:nickname');
400     $user->get('/'             => [nickname => qr/~.*/])->to('user#home');
401     $user->get('/journals'     => [nickname => qr/~.*/])->to('user#journals');
402     $user->get('/journal'      => [nickname => qr/~.*/])->to('user#journals'); # for compatibility
403     $user->get('/comments'     => [nickname => qr/~.*/])->to('user#comments');
404     $user->get('/submissions'  => [nickname => qr/~.*/])->to('user#submissions');
405     $user->get('/friends'      => [nickname => qr/~.*/])->to('user#friends');
406     $user->get('/foes'         => [nickname => qr/~.*/])->to('user#foes');
407     $user->get('/fans'         => [nickname => qr/~.*/])->to('user#fans');
408     $user->get('/freaks'       => [nickname => qr/~.*/])->to('user#freaks');
409     $user->get('/achievements' => [nickname => qr/~.*/])->to('user#achievements');
410     #$r->get('/:user_name/journal' => [user_name => qr/~.*/])->to('journal#user_journals');
411
412 }
413
414 1;