OSDN Git Service

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