package LDV::Comments; use strict; use warnings; use utf8; use Mojo::Base 'Mojolicious::Controller'; use File::Slurp qw(read_file write_file); use Mojo::URL; sub _gen_pageid { my ($self) = @_; my $url = $self->req->param('pageid') || $self->req->headers->referrer; my $maxlen = 64; return unless $url; my $pageid = Mojo::URL->new($url)->path; $pageid =~ s{^/+}{}o; $pageid =~ s{/+$}{}o; $pageid =~ y{/.}{-}s; $pageid =~ s<\.[a-z0-9]{2,4}$><>io; $pageid = substr($pageid, -$maxlen, $maxlen); $self->app->log->debug("comments id: $pageid -- $url"); return $pageid; } sub add { my ($self) = @_; eval { my $sectoken = $self->session('c_sectoken') or die("missing security token\n"); my ($ip, $upto) = ($sectoken =~ m{^([0-9a-f\.:]+)-(\d+)$}io) or die("malformed security token\n"); ($upto > time()) or die("expired security token\n"); ($ip eq $self->tx->remote_address) or die("remote address mismatch\n"); my $text = $self->req->param('text') or die("empty comment\n"); my $pageid = $self->_gen_pageid() or die("can't get pageid\n"); my %opts = (binmode => ':bytes'); my $comments = []; my $path = $self->app->home->rel_file("data/comments/$pageid.json"); if (-f $path) { my $json = read_file($path, %opts); $comments = $self->app->json->decode($json); } push @{ $comments }, { text => $text, time => time(), user => $self->session('username') || 'anonymous', }; write_file($path, \%opts, $self->app->json->encode($comments)); $path = $self->app->home->rel_file("data/comments/$pageid.html"); $self->stash({comments => $comments}); write_file($path, {binmode => ':utf8'}, $self->render_to_string(template => 'comments/list')); $self->render(text => 'OK'); } or do { chomp $@; $self->app->log->error($@); $@ = 'internal error' if $@ =~ m{line \d+}o; $self->res->code(400); $self->render(text => $@); }; $self->rendered(); } sub get { my ($self) = @_; eval { my $pageid = $self->_gen_pageid() or die("can't get id\n"); my $path = $self->app->home->rel_file("data/comments/$pageid.html"); if (-f $path) { my $comments = read_file($path, binmode => ':utf8'); $self->render(text => $comments); } else { $self->render(template => 'comments/none'); } 1; } or do { chomp $@; $self->app->log->error($@); $@ = 'internal error' if $@ =~ m{line \d+}o; $self->res->code(400); $self->render(text => $@); }; $self->rendered(); } sub create { my ($self) = @_; eval { die("request error\n") unless $self->req->is_xhr; my $ip = $self->tx->remote_address or die("can't find remote ip\n"); my $sectoken = sprintf "%s-%d", $ip, time() + 60 * 7; $self->session(c_sectoken => $sectoken); my $pageid = $self->_gen_pageid() or die("can't get pageid\n"); $self->stash({pageid => $pageid}); $self->render(template => 'comments/new'); } or do { chomp $@; $self->app->log->error($@); $@ = 'internal error' if $@ =~ m{line \d+}o; $self->res->code(400); $self->render(text => $@); }; } 1;