2022-10-14 19:24:16 -05:00
|
|
|
package unix_dog::Controller::Main;
|
2022-10-17 18:10:32 -05:00
|
|
|
use strict;
|
|
|
|
use warnings FATAL => 'all';
|
|
|
|
use experimental 'signatures';
|
|
|
|
|
2022-10-14 19:24:16 -05:00
|
|
|
use Mojo::Base 'Mojolicious::Controller', -signatures;
|
2022-10-15 21:10:55 -05:00
|
|
|
use Net::LDAPS;
|
2022-11-03 12:42:42 -05:00
|
|
|
use File::ChangeNotify;
|
|
|
|
use File::stat;
|
|
|
|
use File::Basename;
|
|
|
|
use Time::Piece;
|
|
|
|
use Scalar::Util qw(looks_like_number);
|
2022-10-14 19:24:16 -05:00
|
|
|
|
|
|
|
sub index ($self) {
|
|
|
|
$self->render();
|
|
|
|
}
|
|
|
|
|
|
|
|
sub rules ($self) {
|
|
|
|
$self->render();
|
|
|
|
}
|
|
|
|
|
2022-10-15 01:29:55 -05:00
|
|
|
sub services ($self) {
|
|
|
|
$self->render();
|
|
|
|
}
|
|
|
|
|
2022-10-15 21:10:55 -05:00
|
|
|
|
|
|
|
my $cache = Mojo::Cache->new(max_keys => 1);
|
|
|
|
$cache->set(users => []);
|
|
|
|
Mojo::IOLoop->recurring(60 => sub {
|
|
|
|
$cache->set(users => []);
|
|
|
|
});
|
|
|
|
|
2022-11-03 12:42:42 -05:00
|
|
|
my $acache = Mojo::Cache->new(max_keys => 1);
|
|
|
|
my $filewatcher;
|
|
|
|
|
2022-10-15 21:10:55 -05:00
|
|
|
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)',
|
2023-02-05 16:06:34 -06:00
|
|
|
attrs => ['uid', 'createTimestamp'],
|
2022-10-15 21:10:55 -05:00
|
|
|
);
|
|
|
|
$mesg->code and die $mesg->error;
|
|
|
|
|
2023-02-05 16:06:34 -06:00
|
|
|
my @sorted = sort {$a->get_value('createTimestamp') cmp $b->get_value('createTimestamp')} $mesg->entries;
|
|
|
|
|
|
|
|
for (@sorted) {
|
2022-10-15 21:10:55 -05:00
|
|
|
push @{$users}, $_->get_value('uid');
|
|
|
|
}
|
|
|
|
return $users;
|
|
|
|
|
|
|
|
})->then(sub (@results) {
|
|
|
|
$self->stash(users => (@results));
|
|
|
|
$cache->set(users => (@results));
|
|
|
|
$self->render('main/usersList');
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-11-03 12:42:42 -05:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2022-11-12 18:34:04 -06:00
|
|
|
my @chosen = (sort { $b cmp $a } (keys %announcements))[$start_index..$end_index];
|
2022-11-03 13:17:49 -05:00
|
|
|
my @items;
|
|
|
|
for my $key (@chosen) {
|
|
|
|
push @items, $announcements{$key};
|
|
|
|
}
|
2022-11-03 12:42:42 -05:00
|
|
|
|
|
|
|
$self->stash(
|
2022-11-03 13:17:49 -05:00
|
|
|
next_page => $next_page,
|
|
|
|
items => \@items,
|
2022-11-03 12:42:42 -05:00
|
|
|
);
|
|
|
|
$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;
|
|
|
|
}
|
|
|
|
|
2022-11-12 18:33:27 -06:00
|
|
|
my $url = Mojo::URL->new("https://unix.dog/announcement/");
|
|
|
|
$url->path(basename($fullpath));
|
2022-11-03 12:42:42 -05:00
|
|
|
|
|
|
|
return {
|
|
|
|
raw => $announce_content,
|
|
|
|
text => $announce_text,
|
|
|
|
date => $filestat->mtime,
|
|
|
|
filename => basename($fullpath),
|
|
|
|
title => $announce_title,
|
2022-11-12 18:33:27 -06:00
|
|
|
url => $url,
|
2022-11-03 12:42:42 -05:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2023-01-03 23:40:17 -06:00
|
|
|
sub get_stats($self) {
|
|
|
|
return $self->render('main/statsPage');
|
|
|
|
}
|
|
|
|
|
2022-10-14 19:24:16 -05:00
|
|
|
1;
|