Commit 0de43b74 by Luc Didry

First work on #42

- Start creating a DB abstraction layer
- Use this abstraction layer in watch and cleanfiles commands

This commit is dedicated to guilhemB, who is supporting me on Tipeee.
Many thanks :-)
parent 5dffc402
*.swp
lutim.conf
lutim.db
*.db-shm
*.db-wal
script/hypnotoad.pid
local/*
files/*
......
package Lutim;
use Mojo::Base 'Mojolicious';
use Mojo::Util qw(quote);
use LutimModel;
use Lutim::DB::Image;
use Crypt::CBC;
use Data::Entropy qw(entropy_source);
......@@ -42,6 +42,7 @@ sub startup {
crypto_key_length => 8,
thumbnail_size => 100,
theme => 'default',
dbtype => 'sqlite',
}
});
......@@ -285,10 +286,10 @@ sub startup {
$self->helper(
delete_image => sub {
my $c = shift;
my $image = shift;
unlink $image->path();
$image->update(enabled => 0);
my $c = shift;
my $img = shift;
unlink $img->path or warn "Could not unlink ".$img->path.": $!";
$img->disable();
}
);
......
package Lutim::Command::cron::cleanfiles;
use Mojo::Base 'Mojolicious::Command';
use LutimModel;
use Lutim;
use Mojo::Util qw(slurp decode);
use Lutim::DB::Image;
use Lutim;
use FindBin qw($Bin);
use File::Spec qw(catfile);
......@@ -11,26 +11,33 @@ has usage => sub { shift->extract_usage };
sub run {
my $c = shift;
my $l = Lutim->new;
my $time = time();
my @images = LutimModel::Lutim->select('WHERE enabled = 1 AND (delete_at_day * 86400) < (? - created_at) AND delete_at_day != 0', $time);
for my $image (@images) {
$l->app->delete_image($image);
}
my $config = $c->app->plugin('Config', {
file => File::Spec->catfile($Bin, '..' ,'lutim.conf'),
file => File::Spec->catfile($Bin, '..' ,'lutim.conf'),
default => {
dbtype => 'sqlite',
}
});
if (defined($config->{delete_no_longer_viewed_files}) && $config->{delete_no_longer_viewed_files} > 0) {
$time = time() - $config->{delete_no_longer_viewed_files} * 86400;
@images = LutimModel::Lutim->select('WHERE enabled = 1 AND last_access_at < ?', $time);
my $l = Lutim->new;
for my $image (@images) {
$l->app->delete_image($image);
my $dbi = Lutim::DB::Image->new(app => $c->app);
$dbi->get_images_to_clean()->each(
sub {
my ($img, $num) = @_;
$l->app->delete_image($img);
}
);
if (defined($config->{delete_no_longer_viewed_files}) && $config->{delete_no_longer_viewed_files} > 0) {
my $time = time() - $config->{delete_no_longer_viewed_files} * 86400;
$dbi->get_no_longer_viewed_files($time)->each(
sub {
my ($img, $num) = @_;
$l->app->delete_image($img);
}
);
}
}
......
# vim:set sw=4 ts=4 sts=4 ft=perl expandtab:
package Lutim::Command::cron::watch;
use Mojo::Base 'Mojolicious::Command';
use Mojo::Util qw(slurp decode);
use Filesys::DiskUsage qw/du/;
use LutimModel;
use Lutim::DB::Image;
use Lutim;
use Switch;
use FindBin qw($Bin);
use File::Spec qw(catfile);
......@@ -16,7 +18,8 @@ sub run {
my $config = $c->app->plugin('Config', {
file => File::Spec->catfile($Bin, '..' ,'lutim.conf'),
default => {
policy_when_full => 'warn'
policy_when_full => 'warn',
dbtype => 'sqlite',
}
});
......@@ -36,11 +39,15 @@ sub run {
}
case 'delete' {
say '[Lutim cron job watch] Older files are being deleted';
my $dbi = Lutim::DB::Image->new(app => $c->app);
my $l = Lutim->new;
do {
for my $img (LutimModel::Lutim->select('WHERE path IS NOT NULL AND enabled = 1 ORDER BY created_at ASC LIMIT 50')) {
unlink $img->path() or warn "Could not unlink ".$img->path.": $!";
$img->update(enabled => 0);
}
$dbi->get_50_oldest()->each(
sub {
my ($img, $num) = @_;
$l->app->delete_image($img);
}
);
} while (du(qw/files/) > $config->{max_total_size});
}
else {
......
# vim:set sw=4 ts=4 sts=4 ft=perl expandtab:
package Lutim::DB::Image;
use Mojo::Base -base;
has 'short';
has 'path';
has 'footprint';
has 'enabled';
has 'mediatype';
has 'filename';
has 'counter' => 0;
has 'delete_at_first_view';
has 'delete_at_day';
has 'created_at';
has 'created_by';
has 'last_access_at';
has 'mod_token';
has 'width';
has 'height';
has 'app';
=head1 NAME
Lutim::DB::Image - DB abstraction layer for Lutim images
=head1 Contributing
When creating a new database accessor, make sure that it provides the following subroutines.
After that, modify this file and modify the C<new> subroutine to allow to use your accessor.
Have a look at Lutim::DB::Image::SQLite's code: it's simple and may be more understandable that this doc.
=head1 Attributes
=over 1
=item B<short> : random string
=item B<path> : string, path to the image, relative to lutim's installation directory
=item B<footprint> : string, sha512 checksum of the image
=item B<enabled> : boolean, is the image accessible?
=item B<mediatype> : string, mimetype of the image
=item B<filename> : string
=item B<counter> : integer
=item B<delete_at_first_view> : boolean
=item B<delete_at_day> : integer, number of days from image upload to deletion
=item B<created_at> : unix timestamp
=item B<created_by> : unix timestamp
=item B<last_access_at> : unix timestamp
=item B<mod_token> : random string
=item B<width> : integer
=item B<height> : integer
=item B<app> : a mojolicious object
=back
=head1 Sub routines
=head2 new
=over 1
=item B<Usage> : C<$c = Lutim::DB::Image-E<gt>new(app =E<gt> $self);>
=item B<Arguments> : any of the attribute above
=item B<Purpose> : construct a new db accessor object. If the C<short> attribute is provided, it have to load the informations from the database.
=item B<Returns> : the db accessor object
=item B<Info> : the app argument is used by Lutim::DB::Image to choose which db accessor will be used, you don't need to use it in new(), but you can use it to access helpers or configuration settings in the other subroutines
=back
=cut
sub new {
my $c = shift;
$c = $c->SUPER::new(@_);
if (ref($c) eq 'Lutim::DB::Image') {
my $dbtype = $c->app->config('dbtype');
if ($dbtype eq 'sqlite') {
use Lutim::DB::Image::SQLite;
$c = Lutim::DB::Image::SQLite->new(@_);
} elsif ($dbtype eq 'postgresql') {
use Lutim::DB::Image::Pg;
$c = Lutim::DB::Image::Pg->new(@_);
}
}
return $c;
}
sub to_hash {
my $c = shift;
return {
short => $c->short,
path => $c->path,
footprint => $c->footprint,
enabled => $c->enabled,
mediatype => $c->mediatype,
filename => $c->filename,
counter => $c->counter,
delete_at_first_view => $c->delete_at_first_view,
delete_at_day => $c->delete_at_day,
created_at => $c->created_at,
created_by => $c->created_by,
last_access_at => $c->last_access_at,
mod_token => $c->mod_token,
width => $c->width,
height => $c->height
};
}
=head2 get_no_longer_viewed_files
=over 1
=item B<Usage> : C<$c-E<gt>get_no_longer_viewed_files($time)>
=item B<Arguments> : unix timestamp
=item B<Purpose> : get images no longer viewed after the given timestamp
=item B<Returns> : a Mojo::Collection object containing the no longer viewed images as Lutim::DB::Image objects
=back
=head2 get_images_to_clean
=over 1
=item B<Usage> : C<$c-E<gt>get_images_to_clean>
=item B<Arguments> : none
=item B<Purpose> : get images that are expired but not marked as it
=item B<Returns> : a Mojo::Collection object containing the images to clean as Lutim::DB::Image objects
=back
=head2 get_50_oldest
=over 1
=item B<Usage> : C<$c-E<gt>get_50_oldest>
=item B<Arguments> : none
=item B<Purpose> : get the 50 oldest enabled images
=item B<Returns> : a Mojo::Collection object containing the 50 oldest enabled images as Lutim::DB::Image objects
=back
=head2 disable
=over 1
=item B<Usage> : C<$c-E<gt>disable>
=item B<Arguments> : none
=item B<Purpose> : change the attribute C<enabled> to false and update the database record
=item B<Returns> : the db accessor object
=back
=cut
1;
# vim:set sw=4 ts=4 sts=4 ft=perl expandtab:
package Lutim::DB::Image::SQLite;
use Mojo::Base 'Lutim::DB::Image';
use Lutim::DB::SQLite;
use Mojo::Collection 'c';
has 'record';
sub new {
my $c = shift;
$c = $c->SUPER::new(@_);
$c = $c->_slurp if ($c->short);
return $c;
}
sub get_no_longer_viewed_files {
my $c = shift;
my $time = shift;
my @images;
my @records = c(LutimModel::Lutim->select('WHERE enabled = 1 AND last_access_at < ?', $time));
for my $e (@records) {
my $i = Lutim::DB::Image->new(app => $c->app);
$i->record($e);
$i->_slurp;
push @images, $i;
}
return c(@images);
}
sub get_images_to_clean {
my $c = shift;
my @images;
my @records = c(LutimModel::Lutim->select('WHERE enabled = 1 AND (delete_at_day * 86400) < (? - created_at) AND delete_at_day != 0', time()));
for my $e (@records) {
my $i = Lutim::DB::Image->new(app => $c->app);
$i->record($e);
$i->_slurp;
push @images, $i;
}
return c(@images);
}
sub get_50_oldest {
my $c = shift;
my @images;
my @records = c(Lutim::DB::SQLite::Lutim->select('WHERE path IS NOT NULL AND enabled = 1 ORDER BY created_at ASC LIMIT 50'));
for my $e (@records) {
my $i = Lutim::DB::Image->new(app => $c->app);
$i->record($e);
$i->_slurp;
push @images, $i;
}
return c(@images);
}
sub disable {
my $c = shift;
$c->record->update(enabled => 0);
$c->enabled(0);
return $c;
}
sub _slurp {
my $c = shift;
my @urls;
if ($c->record) {
@urls = ($c->record);
} elsif ($c->short) {
@urls = Lutim::DB::SQLite::Lutim->select('WHERE short = ?', $c->short);
}
if (scalar @urls) {
$c->short($urls[0]->short);
$c->path($urls[0]->path);
$c->footprint($urls[0]->footprint);
$c->enabled($urls[0]->enabled);
$c->mediatype($urls[0]->mediatype);
$c->filename($urls[0]->filename);
$c->counter($urls[0]->counter);
$c->delete_at_first_view($urls[0]->delete_at_first_view);
$c->delete_at_day($urls[0]->delete_at_day);
$c->created_at($urls[0]->created_at);
$c->created_by($urls[0]->created_by);
$c->last_access_at($urls[0]->last_access_at);
$c->mod_token($urls[0]->mod_token);
$c->width($urls[0]->width);
$c->height($urls[0]->height);
$c->record($urls[0]) unless $c->record;
}
return $c;
}
1;
package LutimModel;
# vim:set sw=4 ts=4 sts=4 ft=perl expandtab:
package Lutim::DB::SQLite;
use Mojolicious;
use Mojo::File;
use FindBin qw($Bin);
use File::Spec qw(catfile);
BEGIN {
my $m = Mojolicious->new;
our $config = $m->plugin('Config' =>
{
file => File::Spec->catfile($Bin, '..' ,'lutim.conf'),
file => Mojo::File->new($Bin, '..' ,'lutim.conf')->to_abs->to_string,
default => {
db_path => 'lutim.db'
}
......
......@@ -108,12 +108,28 @@
# optional, defaut is /
#prefix => '/',
# choose what database you want to use
# valid choices are sqlite and postgresql (all lowercase)
# optional, default is sqlite
#dbtype => 'sqlite',
# SQLite ONLY - only used if dbtype is set to sqlite
# define a path to the SQLite database
# you can define it relative to lutim directory or set an absolute path
# remember that it has to be in a directory writable by Lutim user
# optional, default is lutim.db
#db_path => 'lutim.db',
# PostgreSQL ONLY - only used if dbtype is set to postgresql
# these are the credentials to access the PostgreSQL database
# mandatory if you choosed postgresql as dbtype
#pgdb => {
# database => 'lutim',
# host => 'localhost',
# #user => 'DBUSER',
# #pwd => 'DBPASSWORD'
#},
# define the height of the thumbnails generated at users' will
# this is not the height of the thumbnails send after upload,
# we're talking about thumbnails generated when someone asked for
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment