OSDN Git Service

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