✨ — Add support for Swift object storage
- Swift support - script to upload the existing files to Swift - procedure to handle breaking changes
This commit is contained in:
parent
5e7a752700
commit
08316d4c1d
|
@ -58,6 +58,8 @@ variables:
|
|||
services:
|
||||
- name: rroemhild/test-openldap
|
||||
alias: rroemhild-test-openldap
|
||||
- name: swiftstack/picoswiftstack
|
||||
alias: swiftstack-picoswiftstack
|
||||
.pg_template: &pg_definition
|
||||
<<: *tests_template
|
||||
needs:
|
||||
|
@ -67,6 +69,8 @@ variables:
|
|||
alias: postgres
|
||||
- name: rroemhild/test-openldap
|
||||
alias: rroemhild-test-openldap
|
||||
- name: swiftstack/picoswiftstack
|
||||
alias: swiftstack-picoswiftstack
|
||||
.mysql_template: &mysql_definition
|
||||
<<: *tests_template
|
||||
needs:
|
||||
|
@ -76,6 +80,8 @@ variables:
|
|||
alias: mariadb
|
||||
- name: rroemhild/test-openldap
|
||||
alias: rroemhild-test-openldap
|
||||
- name: swiftstack/picoswiftstack
|
||||
alias: swiftstack-picoswiftstack
|
||||
|
||||
### Publish tag changelog and create a toot
|
||||
##
|
||||
|
|
|
@ -4,6 +4,7 @@ Revision history for Lufi
|
|||
- Notifications when uploading and downloading files (#181)
|
||||
- Use Weblate instead of Zanata for translations (https://weblate.framasoft.org/projects/lufi/development/)
|
||||
- Add config API endpoint (#183)
|
||||
- Add support for Swift object storage
|
||||
|
||||
0.04.6 2019-11-07
|
||||
- Now can send large files (>2Gio) while using a DB other than SQLite (#165)
|
||||
|
|
8
Makefile
8
Makefile
|
@ -30,6 +30,14 @@ ldap:
|
|||
|
||||
ldapdev: ldap dev
|
||||
|
||||
swift:
|
||||
sudo docker run -d --rm -p 8080:8080 --hostname="picoswiftstack" --name="picoswiftstack" swiftstack/picoswiftstack; exit 0
|
||||
@echo "Sleeping 20 seconds to let picoswiftstack start"
|
||||
@sleep 20
|
||||
sudo docker exec picoswiftstack get_auth
|
||||
|
||||
swiftdev: swift dev
|
||||
|
||||
devlog:
|
||||
multitail log/development.log
|
||||
|
||||
|
|
12
cpanfile
12
cpanfile
|
@ -6,9 +6,9 @@ requires 'Mojolicious::Plugin::Mail';
|
|||
requires 'Mojolicious::Plugin::GzipStatic';
|
||||
requires 'Mojolicious::Plugin::StaticCache';
|
||||
requires 'Mojolicious::Plugin::CSPHeader', '>= 0.06';
|
||||
requires 'Mojolicious::Plugin::FiatTux::Helpers', '== 0.10', url => 'https://framagit.org/fiat-tux/mojolicious/mojolicious-plugin-fiattux-helpers/-/archive/0.10/mojolicious-plugin-fiattux-helpers-0.10.tar.gz';
|
||||
requires 'Mojolicious::Plugin::FiatTux::GrantAccess', '== 0.06', url => 'https://framagit.org/fiat-tux/mojolicious/mojolicious-plugin-fiattux-grantaccess/-/archive/0.06/mojolicious-plugin-fiattux-grantaccess-0.06.tar.gz';
|
||||
requires 'Mojolicious::Plugin::FiatTux::Themes', '== 0.02', url => 'https://framagit.org/fiat-tux/mojolicious/mojolicious-plugin-fiattux-themes/-/archive/0.02/mojolicious-plugin-fiattux-themes-0.02.tar.gz';
|
||||
requires 'Mojolicious::Plugin::FiatTux::Helpers', '== 0.12', url => 'https://framagit.org/fiat-tux/mojolicious/fiat-tux/mojolicious-plugin-fiattux-helpers/-/archive/0.12/mojolicious-plugin-fiattux-helpers-0.12.tar.gz';
|
||||
requires 'Mojolicious::Plugin::FiatTux::GrantAccess', '== 0.07', url => 'https://framagit.org/fiat-tux/mojolicious/fiat-tux/mojolicious-plugin-fiattux-grantaccess/-/archive/0.07/mojolicious-plugin-fiattux-grantaccess-0.07.tar.gz';
|
||||
requires 'Mojolicious::Plugin::FiatTux::Themes', '== 0.02', url => 'https://framagit.org/fiat-tux/mojolicious/fiat-tux/mojolicious-plugin-fiattux-themes/-/archive/0.02/mojolicious-plugin-fiattux-themes-0.02.tar.gz';
|
||||
requires 'Filesys::DiskUsage';
|
||||
requires 'Switch';
|
||||
requires 'Locale::Maketext';
|
||||
|
@ -55,3 +55,9 @@ feature 'mysql', 'MySQL support' => sub {
|
|||
requires 'Mojo::mysql';
|
||||
requires 'Mojolicious::Plugin::PgURLHelper';
|
||||
};
|
||||
feature 'swift-storage', 'Openstack Swift object storage support' => sub {
|
||||
requires 'Net::OpenStack::Swift';
|
||||
};
|
||||
feature 'test' => sub {
|
||||
requires 'Devel::Cover';
|
||||
};
|
||||
|
|
File diff suppressed because it is too large
Load Diff
32
lib/Lufi.pm
32
lib/Lufi.pm
|
@ -5,6 +5,7 @@ use Mojolicious::Sessions;
|
|||
use Email::Valid;
|
||||
use Data::Validate::URI qw(is_web_uri);
|
||||
use Lufi::DefaultConfig qw($default_config);
|
||||
use Lufi::DB::BreakingChange;
|
||||
|
||||
$ENV{MOJO_MAX_WEBSOCKET_SIZE} = 100485760; # 10 * 1024 * 1024 = 10MiB
|
||||
|
||||
|
@ -66,6 +67,11 @@ sub startup {
|
|||
# Helpers
|
||||
$self->plugin('Lufi::Plugin::Helpers');
|
||||
|
||||
# Now helpers has been loaded, time to check Swift container
|
||||
if ($config->{swift}) {
|
||||
$self->check_swift_container();
|
||||
}
|
||||
|
||||
# Recurrent task
|
||||
Mojo::IOLoop->recurring(2 => sub {
|
||||
my $loop = shift;
|
||||
|
@ -74,8 +80,30 @@ sub startup {
|
|||
});
|
||||
|
||||
# Create directory if needed
|
||||
mkdir($self->config('upload_dir'), 0700) unless (-d $self->config('upload_dir'));
|
||||
die ('The upload directory ('.$self->config('upload_dir').') is not writable') unless (-w $self->config('upload_dir'));
|
||||
if (!defined($config->{swift})) {
|
||||
mkdir($self->config('upload_dir'), 0700) unless (-d $self->config('upload_dir'));
|
||||
die ('The upload directory ('.$self->config('upload_dir').') is not writable') unless (-w $self->config('upload_dir'));
|
||||
}
|
||||
|
||||
## Handle breaking changes… but not when using the breakingchanges command line 😉
|
||||
if (scalar(@ARGV) == 0 || $ARGV[0] ne 'breakingchanges') {
|
||||
# We need to update existing files’ paths in DB to change them to paths relative to storage system (filesystem or Swift)
|
||||
my $bc = Lufi::DB::BreakingChange->new(app => $self, change => 'files_paths');
|
||||
if (!$bc->ack) {
|
||||
print <<EOF;
|
||||
=======================================================================
|
||||
== WARNING! BREAKING CHANGE ==
|
||||
=======================================================================
|
||||
== ==
|
||||
== You need to execute this command before being able to start Lufi: ==
|
||||
== ==
|
||||
== carton exec ./script/lufi breakingchanges files_paths ==
|
||||
== ==
|
||||
=======================================================================
|
||||
EOF
|
||||
exit 1;
|
||||
}
|
||||
}
|
||||
|
||||
# Configure sessions
|
||||
my $sessions = Mojolicious::Sessions->new;
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
# vim:set sw=4 ts=4 sts=4 ft=perl expandtab:
|
||||
package Lufi::Command::breakingchanges;
|
||||
use Mojo::Base 'Mojolicious::Commands';
|
||||
|
||||
has description => 'Execute tasks.';
|
||||
has hint => <<EOF;
|
||||
|
||||
See 'script/lufi breakingchanges help TASK' for more information on a specific task.
|
||||
EOF
|
||||
has message => sub { shift->extract_usage . "\nCron tasks:\n" };
|
||||
has namespaces => sub { ['Lufi::Command::breakingchanges'] };
|
||||
|
||||
sub help { shift->run(@_) }
|
||||
|
||||
1;
|
||||
|
||||
=encoding utf8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Lufi::Command::breakingchanges - Cron commands
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
Usage: script/lufi breakingchanges TASK [OPTIONS]
|
||||
|
||||
=cut
|
|
@ -0,0 +1,58 @@
|
|||
# vim:set sw=4 ts=4 sts=4 ft=perl expandtab:
|
||||
package Lufi::Command::breakingchanges::files_paths;
|
||||
use Mojo::Base 'Mojolicious::Command';
|
||||
use FindBin qw($Bin);
|
||||
use Lufi::DB::Slice;
|
||||
use Lufi::DB::BreakingChange;
|
||||
use Lufi::DefaultConfig qw($default_config);
|
||||
use Term::ProgressBar;
|
||||
|
||||
has description => 'Update existing files’ paths in DB to change them to paths relative to storage system (filesystem or Swift).';
|
||||
has usage => sub { shift->extract_usage };
|
||||
|
||||
sub run {
|
||||
my $c = shift;
|
||||
|
||||
my $bc = Lufi::DB::BreakingChange->new(app => $c->app, change => 'files_paths');
|
||||
if ($bc->ack) {
|
||||
say 'Change "files_paths" already applied. Exiting.';
|
||||
exit;
|
||||
}
|
||||
|
||||
say 'Getting number of database records to update, it can take some time.';
|
||||
my $count = Lufi::DB::Slice->new(app => $c->app)->count();
|
||||
if ($count) {
|
||||
say sprintf('There is %d database records to update, please be patient.', $count);
|
||||
print 'Do you want to continue? [Y/n] ';
|
||||
my $confirm = <STDIN>;
|
||||
|
||||
if ($confirm =~ m/yes|y/i) {
|
||||
my $progress = Term::ProgressBar->new({ count => $count, ETA => 'linear', name => 'Updating paths' });
|
||||
|
||||
Lufi::DB::Slice->new(app => $c->app)->convert_paths($progress);
|
||||
|
||||
$bc->acknowledge;
|
||||
say 'Change "files_paths" successfully applied. You can now start Lufi.';
|
||||
} else {
|
||||
say 'Change "files_paths" not applied. You won’t be able to start Lufi';
|
||||
}
|
||||
} else {
|
||||
say 'No records in database. Setting "files_paths" change as applied.';
|
||||
$bc->acknowledge;
|
||||
say 'Change "files_paths" successfully applied. You can now start Lufi.';
|
||||
}
|
||||
}
|
||||
|
||||
=encoding utf8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Lufi::Command::breakingchanges::files_paths - Update existing files’ paths in DB to change them to paths relative to storage system (filesystem or Swift).
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
Usage: script/lufi breakingchanges files_paths
|
||||
|
||||
=cut
|
||||
|
||||
1;
|
|
@ -0,0 +1,68 @@
|
|||
package Lufi::Command::copyFilesToSwift;
|
||||
use Mojo::Base 'Mojolicious::Command';
|
||||
use File::Spec;
|
||||
use Term::ProgressBar;
|
||||
|
||||
has description => 'Copy files from filesystem to Swift object storage';
|
||||
has usage => sub { shift->extract_usage };
|
||||
|
||||
sub run {
|
||||
my $c = shift;
|
||||
|
||||
if ($c->app->config('swift')) {
|
||||
$c->app->check_swift_container();
|
||||
my @dirs = glob(File::Spec->catdir($c->app->config('upload_dir'), '*'));
|
||||
|
||||
say sprintf('%d folders to upload to Swift (can\'t say how many files, or the total size, sorry). This can take some time.', scalar(@dirs));
|
||||
print 'Do you want to continue? [Y/n] ';
|
||||
my $confirm = <STDIN>;
|
||||
|
||||
if ($confirm =~ m/yes|y/i) {
|
||||
my $progress = Term::ProgressBar->new({ count => scalar(@dirs), ETA => 'linear', name => 'Copying to Swift'});
|
||||
for my $dir (@dirs) {
|
||||
my @files = glob(File::Spec->catfile($dir, '*'));
|
||||
for my $file (@files) {
|
||||
my ($volume, $directories, $filename) = File::Spec->splitpath($file);
|
||||
my @file_dirs = File::Spec->splitdir($directories);
|
||||
my $short = ($file_dirs[-1] ne '') ? $file_dirs[-1] : $file_dirs[-2];
|
||||
my $path = File::Spec->catfile($short, $filename);
|
||||
|
||||
open my $fh, '<', $file or die sprintf('Unable to open file %s: %s', $file, $!);
|
||||
|
||||
$c->app->swift->put_object(
|
||||
container_name => $c->app->config('swift')->{container},
|
||||
object_name => $path,
|
||||
content_length => -s $file,
|
||||
content => $fh
|
||||
);
|
||||
close $fh;
|
||||
}
|
||||
$progress->update();
|
||||
}
|
||||
say sprintf('The copy to Swift object storage has ended. You can test Lufi, then delete `%s` directory', $c->app->config('upload_dir'));
|
||||
} else {
|
||||
say 'You want to stop. No problem.';
|
||||
}
|
||||
} else {
|
||||
say 'You didn\'t configure `swift` in your config file. Exiting.';
|
||||
exit 1;
|
||||
}
|
||||
}
|
||||
|
||||
=encoding utf8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Lufi::Command::copyFilesToSwift Copy files from filesystem to Swift object storage
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
Usage: script/lufi copyFilesToSwift
|
||||
|
||||
This command needs you to:
|
||||
- set `upload_dir` in your config file (otherwise, it will use the default path, `files` to copy files from)
|
||||
- configure `swift` with correct informations in your config file
|
||||
|
||||
=cut
|
||||
|
||||
1;
|
|
@ -1,5 +1,6 @@
|
|||
package Lufi::Command::sqliteToOtherDB;
|
||||
use Mojo::Base 'Mojolicious::Command';
|
||||
use Lufi::DB::BreakingChange;
|
||||
use Lufi::DB::File;
|
||||
use Lufi::DB::Slice;
|
||||
use Lufi::DB::Invitation;
|
||||
|
@ -9,7 +10,7 @@ use Term::ProgressBar;
|
|||
use Lufi::DefaultConfig qw($default_config);
|
||||
|
||||
has description => 'Migrate the records from a SQLite db to the currently configured database';
|
||||
has usage => sub { shift->extract_usage };
|
||||
has usage => sub { shift->extract_usage };
|
||||
|
||||
sub run {
|
||||
my $c = shift;
|
||||
|
@ -36,8 +37,9 @@ sub run {
|
|||
my $files = $sqlite->db->select('files', undef)->hashes;
|
||||
my $slices = $sqlite->db->select('slices', undef)->hashes;
|
||||
my $invitations = $sqlite->db->select('invitations', undef)->hashes;
|
||||
my $changes = $sqlite->db->select('breakingchanges', undef)->hashes;
|
||||
|
||||
my $progress = Term::ProgressBar->new({count => $files->size + $slices->size + $invitations->size});
|
||||
my $progress = Term::ProgressBar->new({count => $files->size + $slices->size + $invitations->size + $changes->size});
|
||||
|
||||
$files->each(sub {
|
||||
my ($file, $num) = @_;
|
||||
|
@ -92,13 +94,22 @@ sub run {
|
|||
->write();
|
||||
$progress->update();
|
||||
});
|
||||
$changes->each(sub {
|
||||
my ($change, $num) = @_;
|
||||
|
||||
Lufi::DB::BreakingChange->new(app => $c->app)
|
||||
->change($change->{change})
|
||||
->ack($change->{ack})
|
||||
->write();
|
||||
$progress->update();
|
||||
});
|
||||
}
|
||||
|
||||
=encoding utf8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Lufi::Command::cron::sqliteToOtherDB Migrate the records from a SQLite db to the currently configured database
|
||||
Lufi::Command::sqliteToOtherDB Migrate the records from a SQLite db to the currently configured database
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
package Lufi::Command::theme;
|
||||
use Mojo::Base 'Mojolicious::Commands';
|
||||
use FindBin qw($Bin);
|
||||
use File::Spec qw(catfile cat dir);
|
||||
use File::Spec qw(catfile catdir);
|
||||
use File::Path qw(make_path);
|
||||
|
||||
has description => 'Create new theme skeleton.';
|
||||
|
|
|
@ -100,7 +100,8 @@ sub upload {
|
|||
}
|
||||
}
|
||||
# Check that we have enough space (multiplying by 2 since it's encrypted, it takes more place that the original file)
|
||||
if ($json->{part} == 0 && ($json->{size} * 2) >= dfportable($c->config('upload_dir'))->{bavail}) {
|
||||
# Only check if using filesystem, not Swift storage
|
||||
if (!defined($c->config('swift')) && $json->{part} == 0 && ($json->{size} * 2) >= dfportable($c->config('upload_dir'))->{bavail}) {
|
||||
$stop = 1;
|
||||
return $ws->send(decode('UTF-8', encode_json(
|
||||
{
|
||||
|
@ -190,19 +191,13 @@ sub upload {
|
|||
# If we already have a part, it's a resend because the websocket has been broken
|
||||
# In this case, we don't need to rewrite the file
|
||||
unless ($f->slices->grep(sub { $_->j == $json->{part} })->size) {
|
||||
# Create directory
|
||||
my $dir = catdir($c->config('upload_dir'), $f->short);
|
||||
mkdir($dir, 0700) unless (-d $dir);
|
||||
|
||||
# Create slice file
|
||||
my $file = catfile($dir, $json->{part}.'.part');
|
||||
my $s = Lufi::DB::Slice->new(
|
||||
app => $c->app,
|
||||
short => $f->short,
|
||||
j => $json->{part},
|
||||
path => $file
|
||||
);
|
||||
Mojo::File->new($file)->spurt($text);
|
||||
path => catfile($f->short, $json->{part}.'.part')
|
||||
)->store($text);
|
||||
push @{$f->slices}, $s;
|
||||
$s->write;
|
||||
|
||||
|
@ -327,7 +322,7 @@ sub download {
|
|||
|
||||
# Get the slice
|
||||
my $e = $f->slices->[$num];
|
||||
my $text = Mojo::File->new($e->path)->slurp;
|
||||
my $text = $e->retrieve();
|
||||
|
||||
my ($json2) = split('XXMOJOXX', $text, 2);
|
||||
$json2 = decode 'UTF-8', $json2;
|
||||
|
|
|
@ -0,0 +1,211 @@
|
|||
# vim:set sw=4 ts=4 sts=4 ft=perl expandtab:
|
||||
package Lufi::DB::BreakingChange;
|
||||
use Mojo::Base -base;
|
||||
use Mojo::File;
|
||||
use Mojo::Collection 'c';
|
||||
|
||||
has 'change';
|
||||
has 'ack' => 0;
|
||||
has 'record' => 0;
|
||||
has 'app';
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Lufi::DB::BreakingChange - DB abstraction layer for Lufi breaking changes
|
||||
|
||||
=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 Lufi::DB::BreakingChange::SQLite's code: it's simple and may be more understandable that this doc.
|
||||
|
||||
=head1 Attributes
|
||||
|
||||
=over 1
|
||||
|
||||
=item B<change> : string, name of the change
|
||||
|
||||
=item B<ack> : boolean, if the admin has acknowledged the change
|
||||
|
||||
=item B<app> : a Mojolicious object
|
||||
|
||||
=back
|
||||
|
||||
=head1 Sub routines
|
||||
|
||||
=head2 new
|
||||
|
||||
=over 1
|
||||
|
||||
=item B<Usage> : C<$c = Lufi::DB::BreakingChange-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<change> 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 Lufi::DB::BreakingChange 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 'Lufi::DB::BreakingChange') {
|
||||
my $dbtype = $c->app->config('dbtype');
|
||||
if ($dbtype eq 'sqlite') {
|
||||
use Lufi::DB::BreakingChange::SQLite;
|
||||
$c = Lufi::DB::BreakingChange::SQLite->new(@_);
|
||||
} elsif ($dbtype eq 'postgresql') {
|
||||
use Lufi::DB::BreakingChange::Pg;
|
||||
$c = Lufi::DB::BreakingChange::Pg->new(@_);
|
||||
} elsif ($dbtype eq 'mysql') {
|
||||
use Lufi::DB::BreakingChange::Mysql;
|
||||
$c = Lufi::DB::BreakingChange::Mysql->new(@_);
|
||||
}
|
||||
}
|
||||
|
||||
return $c;
|
||||
}
|
||||
|
||||
sub to_hash {
|
||||
my $c = shift;
|
||||
|
||||
return {
|
||||
change => $c->change,
|
||||
ack => $c->ack,
|
||||
};
|
||||
}
|
||||
|
||||
=head2 ack
|
||||
|
||||
=over 1
|
||||
|
||||
=item B<Usage> : C<$c-E<gt>acknowledge>
|
||||
|
||||
=item B<Arguments> : none
|
||||
|
||||
=item B<Purpose> : set the C<ack> flag to true
|
||||
|
||||
=item B<Returns> : the db accessor object
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
||||
sub acknowledge {
|
||||
my $c = shift;
|
||||
|
||||
$c->ack(1);
|
||||
|
||||
$c->write;
|
||||
|
||||
return $c;
|
||||
}
|
||||
|
||||
=head2 write
|
||||
|
||||
=over 1
|
||||
|
||||
=item B<Usage> : C<$c-E<gt>write>
|
||||
|
||||
=item B<Arguments> : none
|
||||
|
||||
=item B<Purpose> : create or update a record in the database, with the values of the object's attributes
|
||||
|
||||
=item B<Returns> : the db accessor object
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
||||
sub write {
|
||||
my $c = shift;
|
||||
|
||||
if ($c->record) {
|
||||
$c->app->dbi->db->update('breakingchanges', $c->to_hash, { change => $c->change });
|
||||
} else {
|
||||
$c->app->dbi->db->insert('breakingchanges', $c->to_hash);
|
||||
$c->record(1);
|
||||
}
|
||||
|
||||
return $c;
|
||||
}
|
||||
|
||||
=head2 from_change
|
||||
|
||||
=over 1
|
||||
|
||||
=item B<Usage> : C<$c-E<gt>from_change($change)>
|
||||
|
||||
=item B<Arguments> : string
|
||||
|
||||
=item B<Purpose> : find an invitation in the database from its C<change> attribute
|
||||
|
||||
=item B<Returns> : a db accessor object
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
||||
sub from_change {
|
||||
my $c = shift;
|
||||
my $change = shift;
|
||||
|
||||
my $r = $c->app->dbi->db->select('breakingchanges', undef, { change => $change })->hashes;
|
||||
|
||||
if ($r->size) {
|
||||
return $c->_slurp($r->first);
|
||||
} else {
|
||||
return undef;
|
||||
}
|
||||
}
|
||||
|
||||
=head2 _slurp
|
||||
|
||||
=over 1
|
||||
|
||||
=item B<Usage> : C<$c-E<gt>_slurp>
|
||||
|
||||
=item B<Arguments> : none
|
||||
|
||||
=item B<Purpose> : put a database record's columns into the Lufi::DB::BreakingChange object's attributes
|
||||
|
||||
=item B<Returns> : the Lufi::DB::BreakingChange object
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
||||
sub _slurp {
|
||||
my $c = shift;
|
||||
my $r = shift;
|
||||
|
||||
my $change;
|
||||
if (defined $r) {
|
||||
$change = $r;
|
||||
} else {
|
||||
my $changes = $c->app->dbi->db->select('breakingchanges', undef, { change => $c->change })->hashes;
|
||||
|
||||
if ($changes->size) {
|
||||
$change = $changes->first;
|
||||
}
|
||||
}
|
||||
|
||||
if ($change) {
|
||||
$c->change($change->{change});
|
||||
$c->ack( $change->{ack} );
|
||||
|
||||
$c->record(1) unless $c->record;
|
||||
}
|
||||
|
||||
return $c;
|
||||
}
|
||||
|
||||
1;
|
|
@ -0,0 +1,15 @@
|
|||
# vim:set sw=4 ts=4 sts=4 ft=perl expandtab:
|
||||
package Lufi::DB::BreakingChange::Mysql;
|
||||
use Mojo::Base 'Lufi::DB::BreakingChange';
|
||||
|
||||
sub new {
|
||||
my $c = shift;
|
||||
|
||||
$c = $c->SUPER::new(@_);
|
||||
|
||||
$c = $c->_slurp if defined $c->change;
|
||||
|
||||
return $c;
|
||||
}
|
||||
|
||||
1;
|
|
@ -0,0 +1,15 @@
|
|||
# vim:set sw=4 ts=4 sts=4 ft=perl expandtab:
|
||||
package Lufi::DB::BreakingChange::Pg;
|
||||
use Mojo::Base 'Lufi::DB::BreakingChange';
|
||||
|
||||
sub new {
|
||||
my $c = shift;
|
||||
|
||||
$c = $c->SUPER::new(@_);
|
||||
|
||||
$c = $c->_slurp if defined $c->change;
|
||||
|
||||
return $c;
|
||||
}
|
||||
|
||||
1;
|
|
@ -0,0 +1,15 @@
|
|||
# vim:set sw=4 ts=4 sts=4 ft=perl expandtab:
|
||||
package Lufi::DB::BreakingChange::SQLite;
|
||||
use Mojo::Base 'Lufi::DB::BreakingChange';
|
||||
|
||||
sub new {
|
||||
my $c = shift;
|
||||
|
||||
$c = $c->SUPER::new(@_);
|
||||
|
||||
$c = $c->_slurp if defined $c->change;
|
||||
|
||||
return $c;
|
||||
}
|
||||
|
||||
1;
|
|
@ -148,13 +148,37 @@ sub delete {
|
|||
|
||||
$c->slices->each(sub {
|
||||
my ($e, $num) = @_;
|
||||
unlink $e->path;
|
||||
$e->delete_file();
|
||||
});
|
||||
rmdir Mojo::File->new($c->app->config('upload_dir'), $c->short);
|
||||
$c->deleted(1);
|
||||
$c->delete_path
|
||||
->deleted(1)
|
||||
->write;
|
||||
|
||||
$c->write;
|
||||
return $c;
|
||||
}
|
||||
|
||||
=head2 delete_path
|
||||
|
||||
=over 1
|
||||
|
||||
=item B<Usage> : C<$c-E<gt>delete_path()>
|
||||
|
||||
=item B<Arguments> : none
|
||||
|
||||
=item B<Purpose> : delete the directory of the slices on filesystem or Swift object storage
|
||||
|
||||
=item B<Returns> : the db accessor object
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
||||
sub delete_path {
|
||||
my $c = shift;
|
||||
|
||||
if (!defined($c->app->config('swift'))) {
|
||||
rmdir Mojo::File->new($c->app->config('upload_dir'), $c->short);
|
||||
}
|
||||
return $c;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
# vim:set sw=4 ts=4 sts=4 ft=perl expandtab:
|
||||
package Lufi::DB::Slice;
|
||||
use Mojo::Base -base;
|
||||
use Encode 'encode';
|
||||
use File::Spec::Functions;
|
||||
use Mojo::Collection 'c';
|
||||
|
||||
has 'short';
|
||||
|
@ -105,6 +107,112 @@ sub write {
|
|||
return $c;
|
||||
}
|
||||
|
||||
=head2 store
|
||||
|
||||
=over 1
|
||||
|
||||
=item B<Usage> : C<$c-E<gt>store($text)>
|
||||
|
||||
=item B<Arguments> : a scalar value
|
||||
|
||||
=item B<Purpose> : will store the content to the object's path, either on filesystem or on Swift object storage
|
||||
|
||||
=item B<Returns> : the db accessor object
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
||||
sub store {
|
||||
my $c = shift;
|
||||
my $text = shift;
|
||||
|
||||
if ($c->app->config('swift')) {
|
||||
$c->app->swift->put_object(
|
||||
container_name => $c->app->config('swift')->{container},
|
||||
object_name => $c->path,
|
||||
content_length => length(Encode::encode_utf8($text)),
|
||||
content => $text
|
||||
);
|
||||
} else {
|
||||
# Create directory
|
||||
my $dir = catfile($c->app->config('upload_dir'), $c->short);
|
||||
mkdir($dir, 0700) unless (-d $dir);
|
||||
|
||||
# Write file
|
||||
my $file = catfile($c->app->config('upload_dir'), $c->path);
|
||||
Mojo::File->new($file)->spurt($text);
|
||||
}
|
||||
|
||||
return $c;
|
||||
}
|
||||
|
||||
=head2 retrieve
|
||||
|
||||
=over 1
|
||||
|
||||
=item B<Usage> : C<$c-E<gt>retrieve>
|
||||
|
||||
=item B<Arguments> : none
|
||||
|
||||
=item B<Purpose> : get file from storage, either filesystem or Swift object storage
|
||||
|
||||
=item B<Returns> : the data from the file
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
||||
sub retrieve {
|
||||
my $c = shift;
|
||||
my $upload = shift;
|
||||
|
||||
if ($c->app->config('swift')) {
|
||||
my $file;
|
||||
$c->app->swift->get_object(
|
||||
container_name => $c->app->config('swift')->{container},
|
||||
object_name => $c->path,
|
||||
write_code => sub {
|
||||
my ($status, $message, $headers, $chunk) = @_;
|
||||
$file .= $chunk;
|
||||
}
|
||||
);
|
||||
return $file;
|
||||
} else {
|
||||
my $file = catfile($c->app->config('upload_dir'), $c->path);
|
||||
return Mojo::File->new($file)->slurp;
|
||||
}
|
||||
}
|
||||
=head2 delete_file
|
||||
|
||||
=over 1
|
||||
|
||||
=item B<Usage> : C<$c-E<gt>delete_file()>
|
||||
|
||||
=item B<Arguments> : none
|
||||
|
||||
=item B<Purpose> : delete the file on filesystem or Swift object storage
|
||||
|
||||
=item B<Returns> : the db accessor object
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
||||
sub delete_file {
|
||||
my $c = shift;
|
||||
|
||||
if ($c->app->config('swift')) {
|
||||
$c->app->swift->delete_object({
|
||||
container_name => $c->app->config('swift')->{container},
|
||||
object_name => $c->path
|
||||
});
|
||||
} else {
|
||||
my $file = catfile($c->app->config('upload_dir'), $c->path);
|
||||
unlink $file or warn sprintf('Could not unlink %s: %s', $file, $!);
|
||||
}
|
||||
return $c;
|
||||
}
|
||||
=head2 get_slices_of_file
|
||||
|
||||
=over 1
|
||||
|
@ -147,7 +255,7 @@ sub get_slices_of_file {
|
|||
|
||||
=item B<Arguments> : none
|
||||
|
||||
=item B<Purpose> : delete all file records from database unconditionnally
|
||||
=item B<Purpose> : delete all slices records from database unconditionnally
|
||||
|
||||
=item B<Returns> : nothing
|
||||
|
||||
|
@ -161,6 +269,59 @@ sub delete_all {
|
|||
$c->app->dbi->db->delete('slices');
|
||||
}
|
||||
|
||||
=head2 convert_paths
|
||||
|
||||
=over 1
|
||||
|
||||
=item B<Usage> : C<$c-E<gt>convert_paths($progress)>
|
||||
|
||||
=item B<Arguments> : a Term::ProgressBar object
|
||||
|
||||
=item B<Purpose> : update existing files’ paths in DB to change them to paths relative to storage system (filesystem or Swift).
|
||||
|
||||
=item B<Returns> : nothing
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
||||
sub convert_paths {
|
||||
my $c = shift;
|
||||
my $progress = shift;
|
||||
|
||||
my $shorts = $c->app->dbi->db->select('files', 'short');
|
||||
while (my $short = $shorts->array) {
|
||||
my $slices = $c->app->dbi->db->select('slices', undef, { short => $short->[0] });
|
||||
while (my $slice = $slices->hash) {
|
||||
$slice->{path} = catfile($slice->{short}, $slice->{j}.'.part');
|
||||
Lufi::DB::Slice->new(app => $c->app)->_slurp($slice)->write;
|
||||
$progress->update() if defined($progress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
=head2 count
|
||||
|
||||
=over 1
|
||||
|
||||
=item B<Usage> : C<$c-E<gt>count()>
|
||||
|
||||
=item B<Arguments> : none
|
||||
|
||||
=item B<Purpose> : get count of slices records from database
|
||||
|
||||
=item B<Returns> : integer
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
||||
sub count {
|
||||
my $c = shift;
|
||||
|
||||
return $c->app->dbi->db->query('SELECT count(*) AS count FROM slices')->hashes->first->{count};
|
||||
}
|
||||
|
||||
=head2 _slurp
|
||||
|
||||
=over 1
|
||||
|
|
|
@ -27,6 +27,9 @@ sub startup {
|
|||
# Compress static assets
|
||||
$self->plugin('GzipStatic');
|
||||
|
||||
# Fiat Tux helpers
|
||||
$self->plugin('FiatTux::Helpers');
|
||||
|
||||
# Headers
|
||||
$self->plugin('Lufi::Plugin::Headers');
|
||||
|
||||
|
|
|
@ -99,14 +99,25 @@
|
|||
# Array of authorized domains for API calls.
|
||||
# If you want to authorize everyone to use the API: ['*']
|
||||
# optional, no domains allowed by default
|
||||
#allowed_domains => ['http://1.example.com', 'http://2.example.com'],
|
||||
#allowed_domains => ['http://1.example.com', 'http://2.example.com'],
|
||||
|
||||
# Define a path to the upload directory, where the uploaded files will be stored
|
||||
# You can define it relative to lufi directory or set an absolute path
|
||||
# Remember that it has to be in a directory writable by Lufi user
|
||||
# DO NOT CHANGE THIS IF FILES HAVE BEEN ALREADY UPLOADED: THEY WILL NOT BE DOWNLOADABLE ANYMORE
|
||||
# optional, default is 'files'
|
||||
#upload_dir => 'files',
|
||||
#upload_dir => 'files',
|
||||
|
||||
# You can store files on Swift object storage (https://en.wikipedia.org/wiki/OpenStack#Swift) instead of filesystem
|
||||
# Please read https://metacpan.org/pod/Net::OpenStack::Swift#SYNOPSIS to know how to configure this setting
|
||||
# IMPORTANT: add a `container` key in it, to let Lufi know which container to use. This is not a regular Net::OpenStack::Swift setting, but Lufi need it.
|
||||
# optional, no default
|
||||
#swift => {
|
||||
# auth_url => 'https://auth-endpoint-url/v2.0',
|
||||
# user => 'userid',
|
||||
# password => 'password',
|
||||
# tenant_name => 'project_id',
|
||||
# container => 'lufi'
|
||||
#},
|
||||
|
||||
# Allow to add a password on files, asked before allowing to download files
|
||||
# optional, default is 0
|
||||
|
|
50
t/mysql.conf
50
t/mysql.conf
|
@ -32,6 +32,10 @@
|
|||
# optional, default is ['fdjsofjoihrei'], PLEASE, CHANGE IT
|
||||
#secrets => ['fdjsofjoihrei'],
|
||||
|
||||
# Name of the instance, displayed next to the logo
|
||||
# optional, default is Lufi
|
||||
#instance_name => 'Lufi',
|
||||
|
||||
# Choose a theme. See the available themes in `themes` directory
|
||||
# Optional, default is 'default'
|
||||
#theme => 'default',
|
||||
|
@ -97,10 +101,6 @@
|
|||
# optional, no domains allowed by default
|
||||
#allowed_domains => ['http://1.example.com', 'http://2.example.com'],
|
||||
|
||||
# If set, the shortened URLs will use this domain
|
||||
# optional
|
||||
#fixed_domain => 'example.org',
|
||||
|
||||
# Define a path to the upload directory, where the uploaded files will be stored
|
||||
# You can define it relative to lufi directory or set an absolute path
|
||||
# Remember that it has to be in a directory writable by Lufi user
|
||||
|
@ -108,6 +108,12 @@
|
|||
# optional, default is 'files'
|
||||
#upload_dir => 'files',
|
||||
|
||||
# You can store files on Swift object storage (https://en.wikipedia.org/wiki/OpenStack#Swift) instead of filesystem
|
||||
# Please read https://metacpan.org/pod/Net::OpenStack::Swift#SYNOPSIS to know how to configure this setting
|
||||
# IMPORTANT: add a `container` key in it, to let Lufi know which container to use. This is not a regular Net::OpenStack::Swift setting, but Lufi need it.
|
||||
# optional, no default
|
||||
#swift => { auth_url => 'http://swiftstack-picoswiftstack:8080/auth/v1.0', user => 'test', password => 'test', container => 'lufi', auth_version => '1.0' },
|
||||
|
||||
# Allow to add a password on files, asked before allowing to download files
|
||||
# optional, default is 0
|
||||
allow_pwd_on_files => 1,
|
||||
|
@ -228,11 +234,47 @@
|
|||
#
|
||||
# Define the attributes like this: `lufi_attribute_name => 'LDAP_attribute_name'`
|
||||
# Note that you can’t use `username` as a Lufi attribute name: this name is reserved and will contain the login of the user
|
||||
# optional, no default
|
||||
#ldap_map_attr => {
|
||||
# displayname => 'cn',
|
||||
# mail => 'mail'
|
||||
#},
|
||||
|
||||
# When using LDAP authentication, LDAP users can invite people (by mail) to use Lufi to send them files without
|
||||
# being authenticated.
|
||||
# This is where you configure the behavior of the invitations.
|
||||
# You may need to fetch some attributes from LDAP to use some invitations settings. See `ldap_map_attr` above.
|
||||
# optional, no default
|
||||
#invitations => {
|
||||
# # The name of the key set in `ldap_map_attr` (above) that corresponds to the mail of the LDAP user
|
||||
# # optional, default is `mail`
|
||||
# mail_attr => 'mail',
|
||||
# # The `From` header of invitation mail can be the mail of the LDAP user
|
||||
# # Be sure to have a mail system that will correctly send the mail from your users! (DKIM, SPF…)
|
||||
# # To enable this feature, set it to 1
|
||||
# # optional, disabled by default
|
||||
# send_invitation_with_ldap_user_mail => 1,
|
||||
# # The user is able to set an expiration delay for the invitation.
|
||||
# # This expiration delay can’t be more than this setting (in days).
|
||||
# # optional, default is 30 days
|
||||
# max_invitation_expiration_delay => 30,
|
||||
# # Once the guest has submitted his files, he has an additional period of time to submit forgotten files.
|
||||
# # You can set that additional period of time in minutes here.
|
||||
# # To disable that feature, set it to 0 or less
|
||||
# # optional, default is 10 minutes
|
||||
# max_additional_period => 10,
|
||||
# # Lufi follows privacy-by-design, so, by default, no files URLs (with the decode secret) are stored in database.
|
||||
# # However, the concern is different for this case. Storing files URLs makes users able to retrieve the guests’ sent files
|
||||
# # from their `invitations` page.
|
||||
# # Set to 1 to store guests’ files URLs in database
|
||||
# # optional, default is 0 (disabled)
|
||||
# save_files_url_in_db => 0,
|
||||
# # Users can resend the invitation to their guest. This does not extend the invitation’s expiration delay unless you
|
||||
# # set this option to 1.
|
||||
# # optional, default is 0 (disabled)
|
||||
# extend_invitation_expiration_on_resend => 0,
|
||||
#},
|
||||
|
||||
#########################
|
||||
# Htpasswd authentication
|
||||
#########################
|
||||
|
|
|
@ -32,6 +32,10 @@
|
|||
# optional, default is ['fdjsofjoihrei'], PLEASE, CHANGE IT
|
||||
#secrets => ['fdjsofjoihrei'],
|
||||
|
||||
# Name of the instance, displayed next to the logo
|
||||
# optional, default is Lufi
|
||||
#instance_name => 'Lufi',
|
||||
|
||||
# Choose a theme. See the available themes in `themes` directory
|
||||
# Optional, default is 'default'
|
||||
#theme => 'default',
|
||||
|
@ -97,10 +101,6 @@
|
|||
# optional, no domains allowed by default
|
||||
#allowed_domains => ['http://1.example.com', 'http://2.example.com'],
|
||||
|
||||
# If set, the shortened URLs will use this domain
|
||||
# optional
|
||||
#fixed_domain => 'example.org',
|
||||
|
||||
# Define a path to the upload directory, where the uploaded files will be stored
|
||||
# You can define it relative to lufi directory or set an absolute path
|
||||
# Remember that it has to be in a directory writable by Lufi user
|
||||
|
@ -108,6 +108,12 @@
|
|||
# optional, default is 'files'
|
||||
#upload_dir => 'files',
|
||||
|
||||
# You can store files on Swift object storage (https://en.wikipedia.org/wiki/OpenStack#Swift) instead of filesystem
|
||||
# Please read https://metacpan.org/pod/Net::OpenStack::Swift#SYNOPSIS to know how to configure this setting
|
||||
# IMPORTANT: add a `container` key in it, to let Lufi know which container to use. This is not a regular Net::OpenStack::Swift setting, but Lufi need it.
|
||||
# optional, no default
|
||||
#swift => { auth_url => 'http://swiftstack-picoswiftstack:8080/auth/v1.0', user => 'test', password => 'test', container => 'lufi', auth_version => '1.0' },
|
||||
|
||||
# Allow to add a password on files, asked before allowing to download files
|
||||
# optional, default is 0
|
||||
allow_pwd_on_files => 1,
|
||||
|
@ -172,6 +178,7 @@
|
|||
#port => 5432,
|
||||
user => 'lufi',
|
||||
pwd => 'lufi_pwd'
|
||||
# # https://mojolicious.org/perldoc/Mojo/Pg#max_connections
|
||||
# # optional, default is 1
|
||||
# #max_connections => 1,
|
||||
},
|
||||
|
@ -212,11 +219,47 @@
|
|||
#
|
||||
# Define the attributes like this: `lufi_attribute_name => 'LDAP_attribute_name'`
|
||||
# Note that you can’t use `username` as a Lufi attribute name: this name is reserved and will contain the login of the user
|
||||
# optional, no default
|
||||
#ldap_map_attr => {
|
||||
# displayname => 'cn',
|
||||
# mail => 'mail'
|
||||
#},
|
||||
|
||||
# When using LDAP authentication, LDAP users can invite people (by mail) to use Lufi to send them files without
|
||||
# being authenticated.
|
||||
# This is where you configure the behavior of the invitations.
|
||||
# You may need to fetch some attributes from LDAP to use some invitations settings. See `ldap_map_attr` above.
|
||||
# optional, no default
|
||||
#invitations => {
|
||||
# # The name of the key set in `ldap_map_attr` (above) that corresponds to the mail of the LDAP user
|
||||
# # optional, default is `mail`
|
||||
# mail_attr => 'mail',
|
||||
# # The `From` header of invitation mail can be the mail of the LDAP user
|
||||
# # Be sure to have a mail system that will correctly send the mail from your users! (DKIM, SPF…)
|
||||
# # To enable this feature, set it to 1
|
||||
# # optional, disabled by default
|
||||
# send_invitation_with_ldap_user_mail => 1,
|
||||
# # The user is able to set an expiration delay for the invitation.
|
||||
# # This expiration delay can’t be more than this setting (in days).
|
||||
# # optional, default is 30 days
|
||||
# max_invitation_expiration_delay => 30,
|
||||
# # Once the guest has submitted his files, he has an additional period of time to submit forgotten files.
|
||||
# # You can set that additional period of time in minutes here.
|
||||
# # To disable that feature, set it to 0 or less
|
||||
# # optional, default is 10 minutes
|
||||
# max_additional_period => 10,
|
||||
# # Lufi follows privacy-by-design, so, by default, no files URLs (with the decode secret) are stored in database.
|
||||
# # However, the concern is different for this case. Storing files URLs makes users able to retrieve the guests’ sent files
|
||||
# # from their `invitations` page.
|
||||
# # Set to 1 to store guests’ files URLs in database
|
||||
# # optional, default is 0 (disabled)
|
||||
# save_files_url_in_db => 0,
|
||||
# # Users can resend the invitation to their guest. This does not extend the invitation’s expiration delay unless you
|
||||
# # set this option to 1.
|
||||
# # optional, default is 0 (disabled)
|
||||
# extend_invitation_expiration_on_resend => 0,
|
||||
#},
|
||||
|
||||
#########################
|
||||
# Htpasswd authentication
|
||||
#########################
|
||||
|
|
|
@ -32,6 +32,10 @@
|
|||
# optional, default is ['fdjsofjoihrei'], PLEASE, CHANGE IT
|
||||
#secrets => ['fdjsofjoihrei'],
|
||||
|
||||
# Name of the instance, displayed next to the logo
|
||||
# optional, default is Lufi
|
||||
#instance_name => 'Lufi',
|
||||
|
||||
# Choose a theme. See the available themes in `themes` directory
|
||||
# Optional, default is 'default'
|
||||
#theme => 'default',
|
||||
|
@ -97,10 +101,6 @@
|
|||
# optional, no domains allowed by default
|
||||
#allowed_domains => ['http://1.example.com', 'http://2.example.com'],
|
||||
|
||||
# If set, the shortened URLs will use this domain
|
||||
# optional
|
||||
#fixed_domain => 'example.org',
|
||||
|
||||
# Define a path to the upload directory, where the uploaded files will be stored
|
||||
# You can define it relative to lufi directory or set an absolute path
|
||||
# Remember that it has to be in a directory writable by Lufi user
|
||||
|
@ -108,6 +108,12 @@
|
|||
# optional, default is 'files'
|
||||
#upload_dir => 'files',
|
||||
|
||||
# You can store files on Swift object storage (https://en.wikipedia.org/wiki/OpenStack#Swift) instead of filesystem
|
||||
# Please read https://metacpan.org/pod/Net::OpenStack::Swift#SYNOPSIS to know how to configure this setting
|
||||
# IMPORTANT: add a `container` key in it, to let Lufi know which container to use. This is not a regular Net::OpenStack::Swift setting, but Lufi need it.
|
||||
# optional, no default
|
||||
#swift => { auth_url => 'http://swiftstack-picoswiftstack:8080/auth/v1.0', user => 'test', password => 'test', container => 'lufi', auth_version => '1.0' },
|
||||
|
||||
# Allow to add a password on files, asked before allowing to download files
|
||||
# optional, default is 0
|
||||
allow_pwd_on_files => 1,
|
||||
|
@ -217,11 +223,47 @@
|
|||
#
|
||||
# Define the attributes like this: `lufi_attribute_name => 'LDAP_attribute_name'`
|
||||
# Note that you can’t use `username` as a Lufi attribute name: this name is reserved and will contain the login of the user
|
||||
# optional, no default
|
||||
#ldap_map_attr => {
|
||||
# displayname => 'cn',
|
||||
# mail => 'mail'
|
||||
#},
|
||||
|
||||
# When using LDAP authentication, LDAP users can invite people (by mail) to use Lufi to send them files without
|
||||
# being authenticated.
|
||||
# This is where you configure the behavior of the invitations.
|
||||
# You may need to fetch some attributes from LDAP to use some invitations settings. See `ldap_map_attr` above.
|
||||
# optional, no default
|
||||
#invitations => {
|
||||
# # The name of the key set in `ldap_map_attr` (above) that corresponds to the mail of the LDAP user
|
||||
# # optional, default is `mail`
|
||||
# mail_attr => 'mail',
|
||||
# # The `From` header of invitation mail can be the mail of the LDAP user
|
||||
# # Be sure to have a mail system that will correctly send the mail from your users! (DKIM, SPF…)
|
||||
# # To enable this feature, set it to 1
|
||||
# # optional, disabled by default
|
||||
# send_invitation_with_ldap_user_mail => 1,
|
||||
# # The user is able to set an expiration delay for the invitation.
|
||||
# # This expiration delay can’t be more than this setting (in days).
|
||||
# # optional, default is 30 days
|
||||
# max_invitation_expiration_delay => 30,
|
||||
# # Once the guest has submitted his files, he has an additional period of time to submit forgotten files.
|
||||
# # You can set that additional period of time in minutes here.
|
||||
# # To disable that feature, set it to 0 or less
|
||||
# # optional, default is 10 minutes
|
||||
# max_additional_period => 10,
|
||||
# # Lufi follows privacy-by-design, so, by default, no files URLs (with the decode secret) are stored in database.
|
||||
# # However, the concern is different for this case. Storing files URLs makes users able to retrieve the guests’ sent files
|
||||
# # from their `invitations` page.
|
||||
# # Set to 1 to store guests’ files URLs in database
|
||||
# # optional, default is 0 (disabled)
|
||||
# save_files_url_in_db => 0,
|
||||
# # Users can resend the invitation to their guest. This does not extend the invitation’s expiration delay unless you
|
||||
# # set this option to 1.
|
||||
# # optional, default is 0 (disabled)
|
||||
# extend_invitation_expiration_on_resend => 0,
|
||||
#},
|
||||
|
||||
#########################
|
||||
# Htpasswd authentication
|
||||
#########################
|
||||
|
|
29
t/test.t
29
t/test.t
|
@ -7,6 +7,7 @@ use Mojolicious;
|
|||
use Test::More;
|
||||
use Test::Mojo;
|
||||
|
||||
use Lufi::DB::BreakingChange;
|
||||
use Lufi::DB::File;
|
||||
use Lufi::DB::Slice;
|
||||
use FindBin qw($Bin);
|
||||
|
@ -74,9 +75,13 @@ BEGIN {
|
|||
$m->plugin('DebugDumperHelper');
|
||||
} ## end BEGIN
|
||||
|
||||
Lufi::DB::BreakingChange->new(app => $m)->from_change('files_paths')->acknowledge;
|
||||
Lufi::DB::Slice->new(app => $m)->delete_all;
|
||||
Lufi::DB::File->new(app => $m)->delete_all;
|
||||
|
||||
$config_file = Mojo::File->new($cfile->to_abs->to_string);
|
||||
$config_orig = $config_file->slurp;
|
||||
|
||||
my $t = Test::Mojo->new('Lufi');
|
||||
|
||||
## Wait for short generation
|
||||
|
@ -103,6 +108,12 @@ test_infos_api(true);
|
|||
auth_test_suite('zoidberg', 'zoidberg');
|
||||
restore_config();
|
||||
|
||||
## Test Swift object storage
|
||||
switch_to_swift();
|
||||
test_upload_file();
|
||||
test_download_file();
|
||||
restore_config();
|
||||
|
||||
done_testing();
|
||||
|
||||
######
|
||||
|
@ -263,9 +274,7 @@ sub restore_config {
|
|||
}
|
||||
|
||||
sub switch_to_htpasswd {
|
||||
$config_file = Mojo::File->new($cfile->to_abs->to_string);
|
||||
$config_content = $config_file->slurp;
|
||||
$config_orig = $config_content;
|
||||
$config_content = $config_orig;
|
||||
$config_content =~ s/#?htpasswd.*/htpasswd => 't\/lufi.passwd',/gm;
|
||||
$config_file->spurt($config_content);
|
||||
|
||||
|
@ -291,3 +300,17 @@ sub switch_to_ldap {
|
|||
## Wait for short generation
|
||||
sleep 5;
|
||||
}
|
||||
|
||||
sub switch_to_swift {
|
||||
$config_content = $config_orig;
|
||||
$config_content =~ s/^( +)#?swift => \{ auth_url/$1swift => { auth_url/gm;
|
||||
$config_file->spurt($config_content);
|
||||
|
||||
Lufi::DB::Slice->new(app => $m)->delete_all;
|
||||
Lufi::DB::File->new(app => $m)->delete_all;
|
||||
|
||||
$t = Test::Mojo->new('Lufi');
|
||||
|
||||
## Wait for short generation
|
||||
sleep 5;
|
||||
}
|
||||
|
|
|
@ -138,15 +138,15 @@ msgstr "Copy all links to clipboard"
|
|||
msgid "Copy to clipboard"
|
||||
msgstr "Copy to clipboard"
|
||||
|
||||
#: lib/Lufi/Controller/Files.pm:507
|
||||
#: lib/Lufi/Controller/Files.pm:502
|
||||
msgid "Could not delete the file. You are not authenticated."
|
||||
msgstr "Could not delete the file. You are not authenticated."
|
||||
|
||||
#: lib/Lufi/Controller/Files.pm:489
|
||||
#: lib/Lufi/Controller/Files.pm:484
|
||||
msgid "Could not find the file. Are you sure of the URL and the token?"
|
||||
msgstr "Could not find the file. Are you sure of the URL and the token?"
|
||||
|
||||
#: lib/Lufi/Controller/Files.pm:400
|
||||
#: lib/Lufi/Controller/Files.pm:395
|
||||
msgid "Could not find the file. Are you sure of the URL?"
|
||||
msgstr "Could not find the file. Are you sure of the URL?"
|
||||
|
||||
|
@ -222,15 +222,15 @@ msgstr "Emails"
|
|||
msgid "Encrypting part XX1 of XX2"
|
||||
msgstr "Encrypting part XX1 of XX2"
|
||||
|
||||
#: lib/Lufi/Controller/Files.pm:289
|
||||
#: lib/Lufi/Controller/Files.pm:284
|
||||
msgid "Error: the file existed but was deleted."
|
||||
msgstr "Error: the file existed but was deleted."
|
||||
|
||||
#: lib/Lufi/Controller/Files.pm:369
|
||||
#: lib/Lufi/Controller/Files.pm:364
|
||||
msgid "Error: the file has not been sent entirely."
|
||||
msgstr "Error: the file has not been sent entirely."
|
||||
|
||||
#: lib/Lufi/Controller/Files.pm:379
|
||||
#: lib/Lufi/Controller/Files.pm:374
|
||||
msgid "Error: unable to find the file. Are you sure of your URL?"
|
||||
msgstr "Error: unable to find the file. Are you sure of your URL?"
|
||||
|
||||
|
@ -250,7 +250,7 @@ msgstr "Expires at"
|
|||
msgid "Export localStorage data"
|
||||
msgstr "Export localStorage data"
|
||||
|
||||
#: lib/Lufi/Controller/Files.pm:471
|
||||
#: lib/Lufi/Controller/Files.pm:466
|
||||
msgid "File deleted"
|
||||
msgstr "File deleted"
|
||||
|
||||
|
@ -426,7 +426,7 @@ msgid "Name of the zip file"
|
|||
msgstr "Name of the zip file"
|
||||
|
||||
#. (format_bytes($json->{size})
|
||||
#: lib/Lufi/Controller/Files.pm:108
|
||||
#: lib/Lufi/Controller/Files.pm:109
|
||||
msgid "No enough space available on the server for this file (size: %1)."
|
||||
msgstr "No enough space available on the server for this file (size: %1)."
|
||||
|
||||
|
@ -541,7 +541,7 @@ msgid "Sorry, your invitation has expired or has been deleted."
|
|||
msgstr "Sorry, your invitation has expired or has been deleted."
|
||||
|
||||
#. ($invit->ldap_user_mail)
|
||||
#: lib/Lufi/Controller/Files.pm:122
|
||||
#: lib/Lufi/Controller/Files.pm:123
|
||||
msgid "Sorry, your invitation has expired or has been deleted. Please contact %1 to have another invitation."
|
||||
msgstr "Sorry, your invitation has expired or has been deleted. Please contact %1 to have another invitation."
|
||||
|
||||
|
@ -575,7 +575,7 @@ msgstr "The email subject can't be empty."
|
|||
msgid "The expiration delay (%1) is not between 1 and %2 days."
|
||||
msgstr "The expiration delay (%1) is not between 1 and %2 days."
|
||||
|
||||
#: lib/Lufi/Controller/Files.pm:468
|
||||
#: lib/Lufi/Controller/Files.pm:463
|
||||
msgid "The file has already been deleted"
|
||||
msgstr "The file has already been deleted"
|
||||
|
||||
|
@ -630,11 +630,11 @@ msgstr "The mail has been sent."
|
|||
msgid "The original (and only for now) author is <a href=\"https://fiat-tux.fr\" class=\"classic\">Luc Didry</a>."
|
||||
msgstr "The original (and only for now) author is <a href=\"https://fiat-tux.fr\" class=\"classic\">Luc Didry</a>."
|
||||
|
||||
#: lib/Lufi/Controller/Files.pm:236
|
||||
#: lib/Lufi/Controller/Files.pm:231
|
||||
msgid "The server was unable to find the file record to add your file part to. Please, contact the administrator."
|
||||
msgstr "The server was unable to find the file record to add your file part to. Please, contact the administrator."
|
||||
|
||||
#: lib/Lufi/Controller/Files.pm:295
|
||||
#: lib/Lufi/Controller/Files.pm:290
|
||||
msgid "This file has been deactivated by the admins. Contact them to know why."
|
||||
msgstr "This file has been deactivated by the admins. Contact them to know why."
|
||||
|
||||
|
@ -664,17 +664,17 @@ msgid "Unable to copy the link(s) to your clipboard"
|
|||
msgstr "Unable to copy the link(s) to your clipboard"
|
||||
|
||||
#. ($short)
|
||||
#: lib/Lufi/Controller/Files.pm:439
|
||||
#: lib/Lufi/Controller/Files.pm:434
|
||||
msgid "Unable to get counter for %1. The file does not exists. It will be removed from your localStorage."
|
||||
msgstr "Unable to get counter for %1. The file does not exists. It will be removed from your localStorage."
|
||||
|
||||
#. ($short)
|
||||
#: lib/Lufi/Controller/Files.pm:429
|
||||
#: lib/Lufi/Controller/Files.pm:424
|
||||
msgid "Unable to get counter for %1. The token is invalid."
|
||||
msgstr "Unable to get counter for %1. The token is invalid."
|
||||
|
||||
#. ($short)
|
||||
#: lib/Lufi/Controller/Files.pm:449
|
||||
#: lib/Lufi/Controller/Files.pm:444
|
||||
msgid "Unable to get counter for %1. You are not authenticated."
|
||||
msgstr "Unable to get counter for %1. You are not authenticated."
|
||||
|
||||
|
@ -755,7 +755,7 @@ msgstr "Your browser does not have enough entropy to generate a strong encryptio
|
|||
msgid "Your file is too big: %1 (maximum size allowed: %2)"
|
||||
msgstr "Your file is too big: %1 (maximum size allowed: %2)"
|
||||
|
||||
#: lib/Lufi/Controller/Files.pm:351
|
||||
#: lib/Lufi/Controller/Files.pm:346
|
||||
msgid "Your password is not valid. Please refresh the page to retry."
|
||||
msgstr "Your password is not valid. Please refresh the page to retry."
|
||||
|
||||
|
|
|
@ -138,15 +138,15 @@ msgstr ""
|
|||
msgid "Copy to clipboard"
|
||||
msgstr ""
|
||||
|
||||
#: lib/Lufi/Controller/Files.pm:507
|
||||
#: lib/Lufi/Controller/Files.pm:502
|
||||
msgid "Could not delete the file. You are not authenticated."
|
||||
msgstr ""
|
||||
|
||||
#: lib/Lufi/Controller/Files.pm:489
|
||||
#: lib/Lufi/Controller/Files.pm:484
|
||||
msgid "Could not find the file. Are you sure of the URL and the token?"
|
||||
msgstr ""
|
||||
|
||||
#: lib/Lufi/Controller/Files.pm:400
|
||||
#: lib/Lufi/Controller/Files.pm:395
|
||||
msgid "Could not find the file. Are you sure of the URL?"
|
||||
msgstr ""
|
||||
|
||||
|
@ -222,15 +222,15 @@ msgstr ""
|
|||
msgid "Encrypting part XX1 of XX2"
|
||||
msgstr ""
|
||||
|
||||
#: lib/Lufi/Controller/Files.pm:289
|
||||
#: lib/Lufi/Controller/Files.pm:284
|
||||
msgid "Error: the file existed but was deleted."
|
||||
msgstr ""
|
||||
|
||||
#: lib/Lufi/Controller/Files.pm:369
|
||||
#: lib/Lufi/Controller/Files.pm:364
|
||||
msgid "Error: the file has not been sent entirely."
|
||||
msgstr ""
|
||||
|
||||
#: lib/Lufi/Controller/Files.pm:379
|
||||
#: lib/Lufi/Controller/Files.pm:374
|
||||
msgid "Error: unable to find the file. Are you sure of your URL?"
|
||||
msgstr ""
|
||||
|
||||
|
@ -250,7 +250,7 @@ msgstr ""
|
|||
msgid "Export localStorage data"
|
||||
msgstr ""
|
||||
|
||||
#: lib/Lufi/Controller/Files.pm:471
|
||||
#: lib/Lufi/Controller/Files.pm:466
|
||||
msgid "File deleted"
|
||||
msgstr ""
|
||||
|
||||
|
@ -426,7 +426,7 @@ msgid "Name of the zip file"
|
|||
msgstr ""
|
||||
|
||||
#. (format_bytes($json->{size})
|
||||
#: lib/Lufi/Controller/Files.pm:108
|
||||
#: lib/Lufi/Controller/Files.pm:109
|
||||
msgid "No enough space available on the server for this file (size: %1)."
|
||||
msgstr ""
|
||||
|
||||
|
@ -541,7 +541,7 @@ msgid "Sorry, your invitation has expired or has been deleted."
|
|||
msgstr ""
|
||||
|
||||
#. ($invit->ldap_user_mail)
|
||||
#: lib/Lufi/Controller/Files.pm:122
|
||||
#: lib/Lufi/Controller/Files.pm:123
|
||||
msgid "Sorry, your invitation has expired or has been deleted. Please contact %1 to have another invitation."
|
||||
msgstr ""
|
||||
|
||||
|
@ -575,7 +575,7 @@ msgstr ""
|
|||
msgid "The expiration delay (%1) is not between 1 and %2 days."
|
||||
msgstr ""
|
||||
|
||||
#: lib/Lufi/Controller/Files.pm:468
|
||||
#: lib/Lufi/Controller/Files.pm:463
|
||||
msgid "The file has already been deleted"
|
||||
msgstr ""
|
||||
|
||||
|
@ -630,11 +630,11 @@ msgstr ""
|
|||
msgid "The original (and only for now) author is <a href=\"https://fiat-tux.fr\" class=\"classic\">Luc Didry</a>."
|
||||
msgstr ""
|
||||
|
||||
#: lib/Lufi/Controller/Files.pm:236
|
||||
#: lib/Lufi/Controller/Files.pm:231
|
||||
msgid "The server was unable to find the file record to add your file part to. Please, contact the administrator."
|
||||
msgstr ""
|
||||
|
||||
#: lib/Lufi/Controller/Files.pm:295
|
||||
#: lib/Lufi/Controller/Files.pm:290
|
||||
msgid "This file has been deactivated by the admins. Contact them to know why."
|
||||
msgstr ""
|
||||
|
||||
|
@ -664,17 +664,17 @@ msgid "Unable to copy the link(s) to your clipboard"
|
|||
msgstr ""
|
||||
|
||||
#. ($short)
|
||||
#: lib/Lufi/Controller/Files.pm:439
|
||||
#: lib/Lufi/Controller/Files.pm:434
|
||||
msgid "Unable to get counter for %1. The file does not exists. It will be removed from your localStorage."
|
||||
msgstr ""
|
||||
|
||||
#. ($short)
|
||||
#: lib/Lufi/Controller/Files.pm:429
|
||||
#: lib/Lufi/Controller/Files.pm:424
|
||||
msgid "Unable to get counter for %1. The token is invalid."
|
||||
msgstr ""
|
||||
|
||||
#. ($short)
|
||||
#: lib/Lufi/Controller/Files.pm:449
|
||||
#: lib/Lufi/Controller/Files.pm:444
|
||||
msgid "Unable to get counter for %1. You are not authenticated."
|
||||
msgstr ""
|
||||
|
||||
|
@ -755,7 +755,7 @@ msgstr ""
|
|||
msgid "Your file is too big: %1 (maximum size allowed: %2)"
|
||||
msgstr ""
|
||||
|
||||
#: lib/Lufi/Controller/Files.pm:351
|
||||
#: lib/Lufi/Controller/Files.pm:346
|
||||
msgid "Your password is not valid. Please refresh the page to retry."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -51,3 +51,11 @@ DROP TABLE invitations;
|
|||
ALTER TABLE files MODIFY filesize bigint;
|
||||
-- 4 down
|
||||
ALTER TABLE files MODIFY filesize integer;
|
||||
-- 5 up
|
||||
CREATE TABLE IF NOT EXISTS breakingchanges (
|
||||
change varchar(255) PRIMARY KEY,
|
||||
ack boolean
|
||||
);
|
||||
INSERT INTO breakingchanges (change, ack) VALUES ('files_paths', false);
|
||||
-- 5 down
|
||||
DROP TABLE breakingchanges;
|
||||
|
|
|
@ -54,3 +54,11 @@ DROP TABLE invitations;
|
|||
ALTER TABLE files ALTER COLUMN filesize TYPE bigint;
|
||||
-- 5 down
|
||||
ALTER TABLE files ALTER COLUMN filesize TYPE integer;
|
||||
-- 6 up
|
||||
CREATE TABLE IF NOT EXISTS breakingchanges (
|
||||
change text PRIMARY KEY,
|
||||
ack boolean
|
||||
);
|
||||
INSERT INTO breakingchanges (change, ack) VALUES ('files_paths', false);
|
||||
-- 6 down
|
||||
DROP TABLE breakingchanges;
|
||||
|
|
|
@ -94,3 +94,11 @@ CREATE TABLE IF NOT EXISTS invitations (
|
|||
);
|
||||
-- 4 down
|
||||
DROP TABLE invitations;
|
||||
-- 5 up
|
||||
CREATE TABLE IF NOT EXISTS breakingchanges (
|
||||
change TEXT PRIMARY KEY,
|
||||
ack INTEGER
|
||||
);
|
||||
INSERT INTO breakingchanges (change, ack) VALUES ('files_paths', 0);
|
||||
-- 5 down
|
||||
DROP TABLE breakingchanges;
|
||||
|
|
Loading…
Reference in New Issue