website/lib/unix_dog/Controller/Main.pm

242 lines
6.2 KiB
Perl

package unix_dog::Controller::Main;
use strict;
use warnings FATAL => 'all';
use experimental 'signatures';
use Mojo::Base 'Mojolicious::Controller', -signatures;
use Net::LDAPS;
use File::ChangeNotify;
use File::stat;
use File::Basename;
use Time::Piece;
use Scalar::Util qw(looks_like_number);
sub index ($self) {
$self->render();
}
sub rules ($self) {
$self->render();
}
sub services ($self) {
$self->render();
}
my $cache = Mojo::Cache->new(max_keys => 1);
$cache->set(users => []);
Mojo::IOLoop->recurring(60 => sub {
$cache->set(users => []);
});
my $acache = Mojo::Cache->new(max_keys => 1);
my $filewatcher;
sub user_pages ($self) {
my $config = $self->config;
my $connStr = $config->{'ldap'}->{'uri'};
my $users = $cache->get('users');
if (scalar @{$users} != 0) {
$self->stash(users => $users);
return $self->render('main/usersList');
}
return Mojo::IOLoop->subprocess->run_p(sub {
my $ldap = Net::LDAPS->new($connStr, verify=>'none', version=>3) or die "$@";
my $mesg = $ldap->bind();
$mesg->code and die $mesg->error;
$mesg = $ldap->search(
base => 'OU=Dogs,DC=unix,DC=dog',
filter => '(objectClass=posixAccount)',
attrs => ['uid', 'createTimestamp'],
);
$mesg->code and die $mesg->error;
my @sorted = sort {$a->get_value('createTimestamp') cmp $b->get_value('createTimestamp')} $mesg->entries;
for (@sorted) {
push @{$users}, $_->get_value('uid');
}
return $users;
})->then(sub (@results) {
$self->stash(users => (@results));
$cache->set(users => (@results));
$self->render('main/usersList');
});
}
sub announcements ($self) {
if (!defined $acache->get('announcements')) {
setup_filewatch($self->config->{'announce'}->{'path'});
} else {
check_files();
}
my %announcements = %{$acache->get('announcements')};
my $page = $self->param('page');
if (!defined $page) {
$page = 0;
}
if (!looks_like_number($page) || $page < 0) {
return $self->reply->not_found;
}
my $total_items = scalar(keys %announcements);
my $items_per_page = 5;
my $start_index = $page * $items_per_page;
my $end_index = $start_index + $items_per_page-1;
if ($start_index >= $total_items) {
return $self->reply->not_found;
}
my $next_page = -1;
if ($end_index >= $total_items) {
$end_index = $total_items-1;
} elsif ($end_index != $total_items-1){
$next_page = $page + 1;
}
my @chosen = (sort { $b cmp $a } (keys %announcements))[$start_index..$end_index];
my @items;
for my $key (@chosen) {
push @items, $announcements{$key};
}
$self->stash(
next_page => $next_page,
items => \@items,
);
$self->render();
}
sub get_announcement ($self) {
if (!defined $acache->get('announcements')) {
setup_filewatch($self->config->{'announce'}->{'path'});
} else {
check_files();
}
my $announcements = $acache->get('announcements');
my $filename = $self->param('filename');
if (!defined $announcements->{$filename}) {
return $self->reply->not_found;
}
my $entry = $announcements->{$filename};
$self->render(text => $entry->{'raw'}, format => 'txt');
}
sub announcement_feed ($self) {
if (!defined $acache->get('announcements')) {
setup_filewatch($self->config->{'announce'}->{'path'});
} else {
check_files();
}
my $announcements = $acache->get('announcements');
my @chosen = sort (keys %$announcements);
if (scalar(@chosen) > 5) {
@chosen = @chosen[0..4];
}
my @elements;
for my $key (@chosen) {
push @elements, $announcements->{$key};
}
$self->stash(items => \@elements);
$self->render('main/announcements', format => 'rss');
}
sub setup_filewatch($path) {
opendir my $dir, $path or die "Could not open directory!";
my @files = readdir $dir;
closedir $dir;
my %announcements;
for my $filename (@files) {
if ($filename eq "." || $filename eq "..") {
next;
}
my $entry = get_entry($path.$filename);
if (!defined $entry) {
say "Invalid entry at $filename";
next;
}
$announcements{$filename} = $entry;
}
$acache->set(announcements => \%announcements);
$filewatcher = File::ChangeNotify->instantiate_watcher(
directories => [$path],
);
}
sub check_files {
for my $event ($filewatcher->new_events) {
if ($event->path =~ /\.txt$/) {
my $filename = basename($event->path);
if ($event->type eq 'create' || $event->type eq 'modify') {
my $entry = get_entry($event->path);
if (!defined $entry) {
say "Invalid entry at $filename";
delete $acache->get('announcements')->{$filename};
next;
}
$acache->get('announcements')->{$filename} = get_entry($event->path);
} elsif ($event->type eq 'delete') {
delete $acache->get('announcements')->{$filename};
}
}
}
}
sub get_entry($fullpath) {
open FH, '<', $fullpath or die "Could not open file! $!";
read FH, my $announce_content, -s FH;
close FH;
my $filestat = stat($fullpath);
my $announce_text;
my $announce_title;
if ($announce_content =~ /-----BEGIN PGP SIGNED MESSAGE-----\nHash: \w+\n\n(.+?)\n\n(.*)\n-----BEGIN PGP SIGNATURE-----/s) {
$announce_title = $1;
$announce_text = $2;
}
if (!defined $announce_title || !defined $announce_content || length $announce_title == 0 || length $announce_content == 0) {
return undef;
}
my $url = Mojo::URL->new("https://unix.dog/announcement/");
$url->path(basename($fullpath));
return {
raw => $announce_content,
text => $announce_text,
date => $filestat->mtime,
filename => basename($fullpath),
title => $announce_title,
url => $url,
};
}
sub get_stats($self) {
return $self->render('main/statsPage');
}
1;