diff --git a/CHANGELOG b/CHANGELOG index 381bb22..8558dcc 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,7 @@ Revision history for Lufi 0.02 2017-? - Database abstraction layer + - PostgreSQL support - IE 11 and Edge support - Fix encoding error (#83) - Htpassword authentication support diff --git a/lib/Lufi/DB/File.pm b/lib/Lufi/DB/File.pm index 09d6589..d323b9e 100644 --- a/lib/Lufi/DB/File.pm +++ b/lib/Lufi/DB/File.pm @@ -106,9 +106,9 @@ sub new { if ($dbtype eq 'sqlite') { use Lufi::DB::File::SQLite; $c = Lufi::DB::File::SQLite->new(@_); - #} elsif ($dbtype eq 'postgresql') { - #use Lufi::DB::File::Pg; - #$c = Lufi::DB::File::Pg->new(@_); + } elsif ($dbtype eq 'postgresql') { + use Lufi::DB::File::Pg; + $c = Lufi::DB::File::Pg->new(@_); } } diff --git a/lib/Lufi/DB/File/Pg.pm b/lib/Lufi/DB/File/Pg.pm new file mode 100644 index 0000000..f28a099 --- /dev/null +++ b/lib/Lufi/DB/File/Pg.pm @@ -0,0 +1,177 @@ +# vim:set sw=4 ts=4 sts=4 ft=perl expandtab: +package Lufi::DB::File::Pg; +use Mojo::Base 'Lufi::DB::File'; +use Mojo::File; +use Mojo::Collection 'c'; +use Lufi::DB::Slice; + +has 'record' => 0; + +sub new { + my $c = shift; + + $c = $c->SUPER::new(@_); + + return $c; +} + +sub write { + my $c = shift; + + if ($c->record) { + $c->app->pg->db->query('UPDATE files SET short = ?, deleted = ?, mediatype = ?, filename = ?, filesize = ?, counter = ?, delete_at_first_view = ?, delete_at_day = ?, created_at = ?, created_by = ?, last_access_at = ?, mod_token = ?, nbslices = ?, complete = ?, passwd = ? WHERE short = ?', $c->short, $c->deleted, $c->mediatype, $c->filename, $c->filesize, $c->counter, $c->delete_at_first_view, $c->delete_at_day, $c->created_at, $c->created_by, $c->last_access_at, $c->mod_token, $c->nbslices, $c->complete, $c->passwd, $c->short); + } else { + $c->app->pg->db->query('INSERT INTO files (short, deleted, mediatype, filename, filesize, counter, delete_at_first_view, delete_at_day, created_at, created_by, last_access_at, mod_token, nbslices, complete, passwd) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', $c->short, $c->deleted, $c->mediatype, $c->filename, $c->filesize, $c->counter, $c->delete_at_first_view, $c->delete_at_day, $c->created_at, $c->created_by, $c->last_access_at, $c->mod_token, $c->nbslices, $c->complete, $c->passwd); + $c->record(1); + } + + return $c; +} + +sub count_empty { + my $c = shift; + + return $c->app->pg->db->query('SELECT count(short) FROM files WHERE created_at IS NULL')->hashes->first->{count}; +} + +sub already_exists { + my $c = shift; + my $short = shift; + + return $c->app->pg->db->query('SELECT count(short) FROM files WHERE short = ?', $short)->hashes->first->{count}; +} + +sub get_empty { + my $c = shift; + + my $r = $c->app->pg->db->query('SELECT * FROM files WHERE created_at IS NULL')->hashes->shuffle->first; + + return $c->_slurp($r)->created_at(time)->write; +} + +sub get_stats { + my $c = shift; + + my $files = $c->app->pg->db->query('SELECT count(short) FROM files WHERE created_at IS NOT null AND deleted = 0')->hashes->first->{count}; + my $deleted = $c->app->pg->db->query('SELECT count(short) FROM files WHERE created_at IS NOT null AND deleted = 1')->hashes->first->{count}; + my $empty = $c->app->pg->db->query('SELECT count(short) FROM files WHERE created_at IS null')->hashes->first->{count}; + + return { files => $files, deleted => $deleted, empty => $empty }; +} + +sub from_short { + my $c = shift; + my $short = shift; + + my $r = $c->app->pg->db->query('SELECT * FROM files WHERE short = ?', $short)->hashes; + + if ($r->size) { + return $c->_slurp($r->first)->record(1); + } else { + return undef; + } +} + +sub get_oldest_undeleted_files { + my $c = shift; + my $num = shift; + + my @files; + my $records = $c->app->pg->db->query('SELECT * FROM files WHERE deleted = 0 ORDER BY created_at ASC LIMIT ?', $num)->hashes; + $records->each( + sub { + my ($e, $num) = @_; + my $i = Lufi::DB::File->new(app => $c->app); + + push @files, $i->_slurp($e); + } + ); + + return c(@files); +} + +sub get_expired { + my $c = shift; + my $time = shift; + + my @files; + ## Select only files expired since two days, to be sure that nobody is still downloading it + my $records = $c->app->pg->db->query('SELECT * FROM files WHERE deleted = 0 AND ((delete_at_day + 2) * 86400) < (? - created_at) AND delete_at_day != 0', $time)->hashes; + $records->each( + sub { + my ($e, $num) = @_; + my $i = Lufi::DB::File->new(app => $c->app); + + push @files, $i->_slurp($e); + } + ); + + return c(@files); +} + +sub get_no_longer_viewed { + my $c = shift; + my $time = shift; + + my @files; + my $records = $c->app->pg->db->query('SELECT * FROM files WHERE deleted = 0 AND last_access_at < ?', $time)->hashes; + $records->each( + sub { + my ($e, $num) = @_; + my $i = Lufi::DB::File->new(app => $c->app); + + push @files, $i->_slurp($e); + } + ); + + return c(@files); +} + +sub delete_creator_before { + my $c = shift; + my $separation = shift; + + $c->app->pg->db->query('UPDATE files SET created_by = NULL WHERE created_by IS NOT NULL AND created_at < ?', $separation); +} + +sub _slurp { + my $c = shift; + my $r = shift; + + my $file; + if (defined $r) { + $file = $r; + } else { + my $files = $c->app->pg->db->query('SELECT * FROM files WHERE short = ?', $c->short)->hashes; + + if ($files->size) { + $file = $files->first; + } + } + + if ($file) { + $c->short($file->{short}); + $c->deleted($file->{deleted}); + $c->mediatype($file->{mediatype}); + $c->filename($file->{filename}); + $c->filesize($file->{filesize}); + $c->counter($file->{counter}); + $c->delete_at_first_view($file->{delete_at_first_view}); + $c->delete_at_day($file->{delete_at_day}); + $c->created_at($file->{created_at}); + $c->created_by($file->{created_by}); + $c->last_access_at($file->{last_access_at}); + $c->mod_token($file->{mod_token}); + $c->nbslices($file->{nbslices}); + $c->complete($file->{complete}); + $c->passwd($file->{passwd}); + + $c->record(1) unless $c->record; + } + + $c->slices(Lufi::DB::Slice->new(app => $c->app)->get_slices_of_file($c->short)); + + return $c; +} + +1; diff --git a/lib/Lufi/DB/File/SQLite.pm b/lib/Lufi/DB/File/SQLite.pm index bd6bd2c..a62e710 100644 --- a/lib/Lufi/DB/File/SQLite.pm +++ b/lib/Lufi/DB/File/SQLite.pm @@ -90,7 +90,7 @@ sub get_stats { my $deleted = Lufi::DB::SQLite::Files->count('WHERE created_at IS NOT null AND deleted = 1'); my $empty = Lufi::DB::SQLite::Files->count('WHERE created_at IS null'); - return {files => $files, deleted => $deleted, empty => $empty}; + return { files => $files, deleted => $deleted, empty => $empty }; } sub from_short { diff --git a/lib/Lufi/DB/Slice.pm b/lib/Lufi/DB/Slice.pm index 5d77b4b..b350071 100644 --- a/lib/Lufi/DB/Slice.pm +++ b/lib/Lufi/DB/Slice.pm @@ -62,9 +62,9 @@ sub new { if ($dbtype eq 'sqlite') { use Lufi::DB::Slice::SQLite; $c = Lufi::DB::Slice::SQLite->new(@_); - #} elsif ($dbtype eq 'postgresql') { - # use Lufi::DB::Slice::Pg; - # $c = Lufi::DB::Slice::Pg->new(@_); + } elsif ($dbtype eq 'postgresql') { + use Lufi::DB::Slice::Pg; + $c = Lufi::DB::Slice::Pg->new(@_); } } diff --git a/lib/Lufi/DB/Slice/Pg.pm b/lib/Lufi/DB/Slice/Pg.pm new file mode 100644 index 0000000..5744b49 --- /dev/null +++ b/lib/Lufi/DB/Slice/Pg.pm @@ -0,0 +1,73 @@ +# vim:set sw=4 ts=4 sts=4 ft=perl expandtab: +package Lufi::DB::Slice::Pg; +use Mojo::Base 'Lufi::DB::Slice'; +use Mojo::Collection 'c'; + +has 'record' => 0; + +sub new { + my $c = shift; + + $c = $c->SUPER::new(@_); + + return $c; +} + +sub write { + my $c = shift; + + if ($c->record) { + $c->app->pg->db->query('UPDATE slices SET short = ?, j = ?, path = ? WHERE short = ? AND j = ?', $c->short, $c->j, $c->path, $c->short, $c->j); + } else { + $c->app->pg->db->query('INSERT INTO slices (short, j, path) VALUES (?, ?, ?)', $c->short, $c->j, $c->path); + $c->record(1); + } + + return $c; +} + +sub get_slices_of_file { + my $c = shift; + my $short = shift; + + my @slices; + my $records = $c->app->pg->db->query('SELECT * FROM slices WHERE short = ? ORDER BY j ASC', $short)->hashes; + $records->each( + sub { + my ($e, $num) = @_; + my $i = Lufi::DB::Slice->new(app => $c->app); + + push @slices, $i->_slurp($e); + } + ); + + return c(@slices); +} + +sub _slurp { + my $c = shift; + my $r = shift; + + my $slice; + if (defined $r) { + $slice = $r; + } else { + my $slices = $c->app->pg->db->query('SELECT * FROM slices WHERE short = ? AND j = ?', $c->short, $c->j)->hashes; + + if ($slices->size) { + $slice = $slices->first; + } + } + + if ($slice) { + $c->short($slice->{short}); + $c->j($slice->{j}); + $c->path($slice->{path}); + + $c->record(1); + } + + return $c; +} + +1; diff --git a/lib/Lufi/Plugin/Helpers.pm b/lib/Lufi/Plugin/Helpers.pm index 1d5539f..518f3fe 100644 --- a/lib/Lufi/Plugin/Helpers.pm +++ b/lib/Lufi/Plugin/Helpers.pm @@ -8,20 +8,19 @@ sub register { my ($self, $app) = @_; $app->plugin('PgURLHelper'); - # - # if ($app->config('dbtype') eq 'postgresql') { - # use Mojo::Pg; - # $app->helper(pg => \&_pg); - # - # # Database migration - # my $migrations = Mojo::Pg::Migrations->new(pg => $app->pg); - # if ($app->mode eq 'development' && $ENV{LUFI_DEV} == 1) { - # $migrations->from_file('utilities/migrations.sql')->migrate(0)->migrate(1); - # } else { - # $migrations->from_file('utilities/migrations.sql')->migrate(1); - # } - # } elsif ($app->config('dbtype') eq 'sqlite') { - if ($app->config('dbtype') eq 'sqlite') { + + if ($app->config('dbtype') eq 'postgresql') { + use Mojo::Pg; + $app->helper(pg => \&_pg); + + # Database migration + my $migrations = Mojo::Pg::Migrations->new(pg => $app->pg); + if ($app->mode eq 'development' && $ENV{LUFI_DEV}) { + $migrations->from_file('utilities/migrations_pg.sql')->migrate(0)->migrate(1); + } else { + $migrations->from_file('utilities/migrations_pg.sql')->migrate(1); + } + } elsif ($app->config('dbtype') eq 'sqlite') { # SQLite database migration if needed use Lufi::DB::SQLite; my $columns = Lufi::DB::SQLite::Files->table_info; diff --git a/lufi.conf.template b/lufi.conf.template old mode 100755 new mode 100644 index 218f23b..ac0b7f2 --- a/lufi.conf.template +++ b/lufi.conf.template @@ -115,7 +115,7 @@ # SQLite ONLY - only used if dbtype is set to sqlite # define a path to the SQLite database # you can define it relative to lufi directory or set an absolute path - # remember that it has to be in a directory writable by Lutim user + # remember that it has to be in a directory writable by Lufi user # optional, default is lufi.db #db_path => 'lufi.db', diff --git a/utilities/migrations_pg.sql b/utilities/migrations_pg.sql index e2917b2..3ceeef9 100644 --- a/utilities/migrations_pg.sql +++ b/utilities/migrations_pg.sql @@ -18,9 +18,10 @@ CREATE TABLE IF NOT EXISTS files ( ); CREATE TABLE IF NOT EXISTS slices ( - short text REFERENCES files(short) ON DELETE CASCADE, + short text NOT NULL REFERENCES files(short) ON DELETE CASCADE, j integer NOT NULL, path text unique NOT NULL, + constraint slice_short_j UNIQUE (short, j) ); -- 1 down DROP TABLE slices;