Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package MirrorCache for openSUSE:Factory checked in at 2022-06-15 00:33:09 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/MirrorCache (Old) and /work/SRC/openSUSE:Factory/.MirrorCache.new.1548 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "MirrorCache" Wed Jun 15 00:33:09 2022 rev:15 rq:982588 version:1.034 Changes: -------- --- /work/SRC/openSUSE:Factory/MirrorCache/MirrorCache.changes 2022-05-26 18:44:10.645186177 +0200 +++ /work/SRC/openSUSE:Factory/.MirrorCache.new.1548/MirrorCache.changes 2022-06-15 00:34:11.138692801 +0200 @@ -1,0 +2,8 @@ +Tue Jun 07 09:05:15 UTC 2022 - Andrii Nikitin <andrii.niki...@suse.com> + +- Update to version 1.034: + * Provide torrent (#278) + * Add report for projects on mirrors (#276) + * Fix render when metalink is optional (#277) + +------------------------------------------------------------------- Old: ---- MirrorCache-1.033.obscpio New: ---- MirrorCache-1.034.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ MirrorCache.spec ++++++ --- /var/tmp/diff_new_pack.KNLQIW/_old 2022-06-15 00:34:17.486702130 +0200 +++ /var/tmp/diff_new_pack.KNLQIW/_new 2022-06-15 00:34:17.490702136 +0200 @@ -22,7 +22,7 @@ %define main_requires %{assetpack_requires} perl(Carp) perl(DBD::Pg) >= 3.7.4 perl(DBI) >= 1.632 perl(DBIx::Class) >= 0.082801 perl(DBIx::Class::DynamicDefault) perl(DateTime) perl(Encode) perl(Time::Piece) perl(Time::Seconds) perl(Time::ParseDate) perl(DateTime::Format::Pg) perl(Exporter) perl(File::Basename) perl(LWP::UserAgent) perl(Mojo::Base) perl(Mojo::ByteStream) perl(Mojo::IOLoop) perl(Mojo::JSON) perl(Mojo::Pg) perl(Mojo::URL) perl(Mojo::Util) perl(Mojolicious::Commands) perl(Mojolicious::Plugin) perl(Mojolicious::Plugin::RenderFile) perl(Mojolicious::Static) perl(Net::OpenID::Consumer) perl(POSIX) perl(Sort::Versions) perl(URI::Escape) perl(XML::Writer) perl(base) perl(constant) perl(diagnostics) perl(strict) perl(warnings) shadow rubygem(sass) perl(Net::DNS) perl(LWP::Protocol::https) perl(Digest::SHA) %define build_requires %{assetpack_requires} rubygem(sass) tidy sysuser-shadow sysuser-tools Name: MirrorCache -Version: 1.033 +Version: 1.034 Release: 0 Summary: WebApp to redirect and manage mirrors License: GPL-2.0-or-later ++++++ MirrorCache-1.033.obscpio -> MirrorCache-1.034.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.033/assets/assetpack.def new/MirrorCache-1.034/assets/assetpack.def --- old/MirrorCache-1.033/assets/assetpack.def 2022-05-18 14:28:29.000000000 +0200 +++ new/MirrorCache-1.034/assets/assetpack.def 2022-06-07 10:53:05.000000000 +0200 @@ -115,6 +115,7 @@ < http://timeago.yarp.com/jquery.timeago.js < javascripts/mirrorcache.js < javascripts/mirrorlist.js +< javascripts/reporttable.js < javascripts/admintable.js < javascripts/admin_user.js < javascripts/audit_log.js diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.033/assets/javascripts/reporttable.js new/MirrorCache-1.034/assets/javascripts/reporttable.js --- old/MirrorCache-1.033/assets/javascripts/reporttable.js 1970-01-01 01:00:00.000000000 +0100 +++ new/MirrorCache-1.034/assets/javascripts/reporttable.js 2022-06-07 10:53:05.000000000 +0200 @@ -0,0 +1,33 @@ + +function setupReportTable() { + // read columns from empty HTML table rendered by the server + var columns = []; + var thElements = $('.reporttable thead th').each(function() { + var th = $(this); + + // add column + var columnName; + columnName = th.text().trim().replace(/ /g,"").toLowerCase().replace(/\./g,""); + if (columnName.match(/^\d/)) { + columnName = 'c' + columnName; + } + columns.push({ data: columnName, defaultContent: "" }); + }); + + var url = $("#reporttable_api_url").val(); + var table = $('.reporttable'); + var dataTable = table.DataTable({ + order: [ + [0, 'asc'] + ], + ajax: { + url: url, + dataSrc: 'report' + }, + columns: columns, + search: { + regex: true, + }, + }); + dataTable.rowData = []; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.033/lib/MirrorCache/Datamodule.pm new/MirrorCache-1.034/lib/MirrorCache/Datamodule.pm --- old/MirrorCache-1.033/lib/MirrorCache/Datamodule.pm 2022-05-18 14:28:29.000000000 +0200 +++ new/MirrorCache-1.034/lib/MirrorCache/Datamodule.pm 2022-06-07 10:53:05.000000000 +0200 @@ -38,6 +38,7 @@ has [ '_is_secure', '_is_ipv4', '_ipvstrict', '_is_head' ]; has 'mirrorlist'; has 'zsync'; +has [ 'torrent', 'magnet', 'btih' ]; has 'json'; has [ 'folder_id', 'file_id', 'file_age', 'folder_sync_last', 'folder_scan_last' ]; # shortcut to requested folder and file, if known @@ -105,7 +106,7 @@ } sub extra($self) { - return ($self->metalink || $self->mirrorlist || $self->zsync); + return ($self->metalink || $self->mirrorlist || $self->zsync || $self->magnet || $self->torrent || $self->btih ); } sub region($self) { @@ -258,10 +259,8 @@ sub redirect($self, $url) { my $xtra = ''; - if ($self->_original_path =~ m/.*\.metalink$/) { - $xtra = ".metalink"; - } elsif ($self->_original_path =~ m/.*\.mirrorlist$/) { - $xtra = ".mirrorlist"; + if ($self->_original_path =~ m/(\.metalink|\.mirrorlist|\.torrent|\.magnet|\.btih)$/) { + $xtra = $1; } return $self->c->redirect_to($url . $xtra . $self->query1); @@ -350,6 +349,9 @@ $self->_query1('?' . $query_string); $self->mirrorlist(1) if defined $query->param('mirrorlist'); $self->zsync(1) if defined $query->param('zsync'); + $self->torrent(1) if defined $query->param('torrent'); + $self->magnet(1) if defined $query->param('magnet'); + $self->btih(1) if defined $query->param('btih'); $self->json(1) if defined $query->param('json') || defined $query->param('JSON'); $pedantic = $query->param('PEDANTIC'); } else { @@ -395,6 +397,24 @@ $path = substr($path, 0, $pos); } } + if (!$trailing_slash && ((my $pos = length($path) - length('.torrent')) > 1)) { + if ('.torrent' eq substr($path, $pos)) { + $self->torrent(1); + $path = substr($path, 0, $pos); + } + } + if (!$trailing_slash && ((my $pos = length($path) - length('.magnet')) > 1)) { + if ('.magnet' eq substr($path, $pos)) { + $self->magnet(1); + $path = substr($path, 0, $pos); + } + } + if (!$trailing_slash && ((my $pos = length($path) - length('.btih')) > 1)) { + if ('.btih' eq substr($path, $pos)) { + $self->btih(1); + $path = substr($path, 0, $pos); + } + } $pedantic = $ENV{'MIRRORCACHE_PEDANTIC'} unless defined $pedantic; if (!defined $pedantic) { if ( $path =~ m/.*\/([^\/]*-Current[^\/]*)$/ ) { @@ -407,6 +427,7 @@ $self->_pedantic($pedantic) if defined $pedantic; + $self->agent; # parse headers $self->must_render_from_root(1) if !$self->mirrorlist && ( !$self->metalink || $self->metalink_accept ) @@ -419,7 +440,6 @@ $self->_mime($mime); $self->_path($path); $self->_trailing_slash($trailing_slash); - $self->agent; # parse headers } sub root_is_hit($self) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.033/lib/MirrorCache/Schema/ResultSet/Server.pm new/MirrorCache-1.034/lib/MirrorCache/Schema/ResultSet/Server.pm --- old/MirrorCache-1.033/lib/MirrorCache/Schema/ResultSet/Server.pm 2022-05-18 14:28:29.000000000 +0200 +++ new/MirrorCache-1.034/lib/MirrorCache/Schema/ResultSet/Server.pm 2022-06-07 10:53:05.000000000 +0200 @@ -247,4 +247,49 @@ $prep->execute($state, $extra, $server_id, $project_id); } +sub report_mirrors { + my ($self, $project, $region) = @_; + my $rsource = $self->result_source; + my $schema = $rsource->schema; + my $dbh = $schema->storage->dbh; + + my $where1 = ''; + my $where2 = ''; + $where1 = "WHERE REPLACE(prj.name,' ','') = ?" if $project; + $where2 = "WHERE s.region = ?" if $region; + + my $sql = <<"END_SQL"; +select s.region, s.country, x.name project, s.id, min(case when s1 = 0 then 1 else 0 end) as curr, round((sum(s2)*1.0)/(sum(s1)+sum(s2))*100) as score, concat(s.hostname, s.urldir) as url, max(example) victim +from ( +select prj.name, fds.server_id, + sum(case when fds.folder_diff_id != fds2.folder_diff_id then 1 else 0 end) s1, + sum(case when fds.folder_diff_id = fds2.folder_diff_id then 1 else 0 end) s2, + max(case when fds.folder_diff_id != fds2.folder_diff_id then f.path else '' end) example +from project prj +join folder f on f.path like concat(prj.path,'%') +join folder_diff fd on fd.folder_id = f.id +join folder_diff fd2 on fd2.folder_id = fd.folder_id +join folder_diff_server fds on fd.id = fds.folder_diff_id +join folder_diff_server fds2 on fd2.id = fds2.folder_diff_id and fds2.server_id = prj.etalon +$where1 +group by fds2.server_id, prj.name, fds.server_id ) x +join server s on x.server_id = s.id +$where2 +group by s.id, x.name +order by region, country, score, url; +END_SQL + my $prep = $dbh->prepare($sql); + if ($project && $region) { + $prep->execute($project, $region); + } elsif ($project) { + $prep->execute($project); + } elsif ($region) { + $prep->execute($region); + } else { + $prep->execute; + } + my $server_arrayref = $dbh->selectall_arrayref($prep, { Slice => {} }); + return $server_arrayref; +} + 1; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.033/lib/MirrorCache/Task/Report.pm new/MirrorCache-1.034/lib/MirrorCache/Task/Report.pm --- old/MirrorCache-1.033/lib/MirrorCache/Task/Report.pm 1970-01-01 01:00:00.000000000 +0100 +++ new/MirrorCache-1.034/lib/MirrorCache/Task/Report.pm 2022-06-07 10:53:05.000000000 +0200 @@ -0,0 +1,78 @@ +# Copyright (C) 2022 SUSE LLC +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, see <http://www.gnu.org/licenses/>. + +package MirrorCache::Task::Report; +use Mojo::Base 'Mojolicious::Plugin'; +use Mojo::JSON qw(decode_json encode_json); + +sub register { + my ($self, $app) = @_; + $app->minion->add_task(report => sub { _run($app, @_) }); +} + +my $DELAY = int($ENV{MIRRORCACHE_SCHEDULE_REPORT_RETRY_INTERVAL} // 15 * 60); + +sub _run { + my ($app, $job, $once) = @_; + my $minion = $app->minion; + return $job->finish('Previous report job is still active') + unless my $guard = $minion->guard('report', 15*60); + + my $schema = $app->schema; + + my $mirrors = $schema->resultset('Server')->report_mirrors; + # this is just tmp structure we use for aggregation + my %report; + for my $m (@$mirrors) { + $report{$m->{region}}{$m->{country}}{$m->{url}}{$m->{project}} = [$m->{score},$m->{victim}]; + } + # json expects array, so we collect array here + my @report; + for my $region (sort keys %report) { + my $by_region = $report{$region}; + for my $country (sort keys %$by_region) { + my $by_country = $by_region->{$country}; + for my $url (sort keys %$by_country) { + my %row = ( + region => $region, + country => $country, + url => $url, + ); + my $by_project = $by_country->{$url}; + for my $project (sort keys %$by_project) { + my $p = $by_project->{$project}; + my $score = $p->[0]; + my $victim = $p->[1]; + $project =~ tr/ //ds; + $project =~ tr/\.//ds; + $project = lc($project); + $project = "c$project" if $project =~ /^\d/; + $row{$project . 'score'} = $score; + $row{$project . 'victim'} = $victim; + } + push @report, \%row; + } + } + } + my $json = encode_json(\@report); + my $sql = 'insert into report_body select 1, now(), ?'; + + $schema->storage->dbh->prepare($sql)->execute($json); + + return $job->finish if $once; + return $job->retry({delay => $DELAY}); +} + +1; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.033/lib/MirrorCache/WebAPI/Controller/Report/Mirror.pm new/MirrorCache-1.034/lib/MirrorCache/WebAPI/Controller/Report/Mirror.pm --- old/MirrorCache-1.033/lib/MirrorCache/WebAPI/Controller/Report/Mirror.pm 1970-01-01 01:00:00.000000000 +0100 +++ new/MirrorCache-1.034/lib/MirrorCache/WebAPI/Controller/Report/Mirror.pm 2022-06-07 10:53:05.000000000 +0200 @@ -0,0 +1,30 @@ +# Copyright (C) 2022 SUSE LLC +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, see <http://www.gnu.org/licenses/>. + +package MirrorCache::WebAPI::Controller::Report::Mirror; +use Mojo::Base 'Mojolicious::Controller'; + +sub index { + my ($self, $template) = @_; + my $projects = $self->mcproject->list; + + $self->stash; + $self->render( + "report/mirror/index", + projects => $projects + ); +} + +1; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.033/lib/MirrorCache/WebAPI/Controller/Rest/ReportMirror.pm new/MirrorCache-1.034/lib/MirrorCache/WebAPI/Controller/Rest/ReportMirror.pm --- old/MirrorCache-1.033/lib/MirrorCache/WebAPI/Controller/Rest/ReportMirror.pm 1970-01-01 01:00:00.000000000 +0100 +++ new/MirrorCache-1.034/lib/MirrorCache/WebAPI/Controller/Rest/ReportMirror.pm 2022-06-07 10:53:05.000000000 +0200 @@ -0,0 +1,43 @@ +# Copyright (C) 2022 SUSE LLC +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, see <http://www.gnu.org/licenses/>. + +package MirrorCache::WebAPI::Controller::Rest::ReportMirror; +use Mojo::Base 'Mojolicious::Controller'; + +use Mojo::JSON qw(decode_json encode_json); +use Data::Dumper; + +sub list { + my ($self) = @_; + + my $sql = 'select dt, body from report_body where report_id = 1 order by dt desc limit 1'; + + eval { + my @res = $self->schema->storage->dbh->selectrow_array($sql); + my $body = $res[1]; + my $hash = decode_json($body); + + $self->render( + json => { report => $hash, dt => $res[0] } + ); + }; + my $error = $@; + if ($error) { + print STDERR "RESPMIRRORREPORT : " . $error . "\n"; + return $self->render(json => {error => $error}, status => 404); + } +} + +1; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.033/lib/MirrorCache/WebAPI/Plugin/Backstage.pm new/MirrorCache-1.034/lib/MirrorCache/WebAPI/Plugin/Backstage.pm --- old/MirrorCache-1.033/lib/MirrorCache/WebAPI/Plugin/Backstage.pm 2022-05-18 14:28:29.000000000 +0200 +++ new/MirrorCache-1.034/lib/MirrorCache/WebAPI/Plugin/Backstage.pm 2022-06-07 10:53:05.000000000 +0200 @@ -32,7 +32,7 @@ } my @permanent_jobs = - qw(folder_sync_schedule_from_misses folder_sync_schedule mirror_scan_schedule_from_misses mirror_scan_schedule_from_path_errors mirror_scan_schedule cleanup stat_agg_schedule mirror_check_from_stat); + qw(folder_sync_schedule_from_misses folder_sync_schedule mirror_scan_schedule_from_misses mirror_scan_schedule_from_path_errors mirror_scan_schedule cleanup stat_agg_schedule mirror_check_from_stat report); sub register_tasks { my $self = shift; @@ -54,6 +54,7 @@ qw(MirrorCache::Task::FolderSync), qw(MirrorCache::Task::FolderTree), qw(MirrorCache::Task::Cleanup), + qw(MirrorCache::Task::Report), qw(MirrorCache::Task::StatAggSchedule), ); if (defined $ENV{MIRRORCACHE_PERMANENT_JOBS}) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.033/lib/MirrorCache/WebAPI/Plugin/Dir.pm new/MirrorCache-1.034/lib/MirrorCache/WebAPI/Plugin/Dir.pm --- old/MirrorCache-1.033/lib/MirrorCache/WebAPI/Plugin/Dir.pm 2022-05-18 14:28:29.000000000 +0200 +++ new/MirrorCache-1.034/lib/MirrorCache/WebAPI/Plugin/Dir.pm 2022-06-07 10:53:05.000000000 +0200 @@ -71,8 +71,9 @@ || _redirect_geo($dm) || _redirect_normalized($dm) || _render_stats($dm) - || _local_render($dm) - || _render_from_db($dm); + || _local_render($dm, 0) # check if we should render local + || _render_from_db($dm) + || _local_render($dm, 1); # check if we should render local when metalink cannot be provided my $tx = $c->render_later->tx; my $rendered; @@ -183,7 +184,7 @@ my ($path, $trailing_slash, $original_path) = $dm->path; return undef if $path eq '/'; $path = $path . '.metalink' if $dm->metalink && !$dm->metalink_accept; - return $dm->c->redirect_to($dm->route . $path . $trailing_slash . $dm->query1) unless $original_path eq $path || $dm->mirrorlist || $dm->zsync; + return $dm->c->redirect_to($dm->route . $path . $trailing_slash . $dm->query1) unless $original_path eq $path || ($dm->extra && !$dm->metalink); return undef; } @@ -242,8 +243,9 @@ } sub _local_render { - my $dm = shift; - return undef if $dm->extra; + my $dm = shift; + my $accept = shift; + return undef if $dm->extra && (!$accept || !$dm->metalink_accept); my ($path, $trailing_slash) = $dm->path; return $root->render_file_if_nfs($dm, $path) if $root->is_remote; @@ -272,13 +274,7 @@ $dm->folder_id($parent_folder->id); } else { my $another_folder = $rsFolder->find({path => $dm->root_subtree . $f->dirname}); - if (!$another_folder) { - my $res = $c->render(status => 425, text => "The file is unknown, retry later"); - # log miss here even thoough we haven't rendered anything - $c->stat->redirect_to_root($dm, 0); - return $res; - } - $dm->folder_id($another_folder->id); + return undef unless $another_folder; # nothing found, proceed to _guess_what_to_render } my $file; $file = $schema->resultset('File')->find_with_hash($parent_folder->id, $f->basename) if $parent_folder && !$trailing_slash; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.033/lib/MirrorCache/WebAPI/Plugin/Project.pm new/MirrorCache-1.034/lib/MirrorCache/WebAPI/Plugin/Project.pm --- old/MirrorCache-1.033/lib/MirrorCache/WebAPI/Plugin/Project.pm 1970-01-01 01:00:00.000000000 +0100 +++ new/MirrorCache-1.034/lib/MirrorCache/WebAPI/Plugin/Project.pm 2022-06-07 10:53:05.000000000 +0200 @@ -0,0 +1,64 @@ +# Copyright (C) 2022 SUSE LLC +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +package MirrorCache::WebAPI::Plugin::Project; +use Mojo::Base 'Mojolicious::Plugin'; +use Data::Dumper; + +my $initialized = 0; +my @projects; +my %projects_path; +my %projects_alias; + +sub register { + my ($self, $app) = @_; + + $app->helper('mcproject.list' => \&_list); + return $self; +} + +sub _init_if_needed { + return 1 if $initialized; + my ($c) = @_; + $initialized = 1; + eval { #the table may be missing - no big deal (only reports might be inaccurate if some other error occurred). + @projects = $c->schema->resultset('Project')->all; + 1; + } or $c->log->error(Dumper("Cannot load projects", @_)); + + + + for my $p (@projects) { + my $name = $p->name; + my $alias = $name; + $alias =~ tr/ //ds; # remove spaces + $projects_path{$name} = $p->path; + $projects_alias{$name} = $alias; + } +} + +sub _list { + my ($c) = @_; + _init_if_needed($c); + + my @res; + for my $p (@projects) { + push @res, $p->name; + } + return \@res; +} + +1; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.033/lib/MirrorCache/WebAPI/Plugin/RenderFileFromMirror.pm new/MirrorCache-1.034/lib/MirrorCache/WebAPI/Plugin/RenderFileFromMirror.pm --- old/MirrorCache-1.033/lib/MirrorCache/WebAPI/Plugin/RenderFileFromMirror.pm 2022-05-18 14:28:29.000000000 +0200 +++ new/MirrorCache-1.034/lib/MirrorCache/WebAPI/Plugin/RenderFileFromMirror.pm 2022-06-07 10:53:05.000000000 +0200 @@ -25,6 +25,7 @@ use DateTime; use Mojo::File; use Mojo::Date; +use Mojo::Util; use Mojo::IOLoop::Subprocess; sub register { @@ -89,17 +90,33 @@ if (!$folder || !$file) { return $c->render(status => 404, text => "File not found"); } - if ($dm->zsync) { - my $url = $root->is_remote ? $root->location($dm, $folder->path) : $root->redirect($dm, $folder->path); - if ($url) { - $url = $url . "/" . $basename; - } else { - ($url = $c->req->url->to_abs->to_string) =~ s/\.zsync$//; + my $url; + if ($dm->torrent || $dm->zsync || $dm->metalink) { + $url = $root->is_remote ? $root->location($dm, $folder->path) : $root->redirect($dm, $folder->path); + if (!$dm->metalink) { + if ($url && ! $dm->metalink) { + $url = $url . "/" . $basename; + } else { + ($url = $c->req->url->to_abs->to_string) =~ s/\.(torrent|zsync)$//; + } } + } + + if ($dm->zsync) { _render_zsync($c, $url, $basename, $file->{mtime}, $file->{size}, $file->{sha1}, $file->{zblock_size}, $file->{zlengths}, $file->{zhashes}); $c->stat->redirect_to_root($dm, 1); return 1; } + if ($dm->btih) { + _render_btih($c, $basename, $file); + $c->stat->redirect_to_root($dm, 1); + return 1; + } + if ($dm->magnet) { + _render_magnet($c, $url, $basename, $file); + $c->stat->redirect_to_root($dm, 1); + return 1; + } my (@mirrors_country, @mirrors_region, @mirrors_rest); @@ -112,7 +129,7 @@ $mirror = $mirrors_region[0] if !$mirror && @mirrors_region; $mirror = $mirrors_rest[0] if !$mirror && @mirrors_rest; - if ($dm->metalink || $dm->mirrorlist) { + if ($dm->extra) { if ($mirror) { $c->stat->redirect_to_mirror($mirror->{mirror_id}, $dm); } else { @@ -120,29 +137,30 @@ } } - if ($dm->metalink && !($dm->metalink_accept && 'media.1/media' eq substr($filepath,length($filepath)-length('media.1/media')))) { - my $url = $c->req->url->to_abs; + return _render_torrent($dm, $file, \@mirrors_country, \@mirrors_region, \@mirrors_rest, $url) if $dm->torrent; + if ($dm->metalink && !($dm->metalink_accept && 'media.1/media' eq substr($filepath,length($filepath)-length('media.1/media')))) { my $origin; if (my $publisher_url = $ENV{MIRRORCACHE_METALINK_PUBLISHER_URL}) { $publisher_url =~ s/^https?:\/\///; $origin = $dm->scheme . '://' . $publisher_url; } else { - $origin = $dm->scheme . '://' . $url->host; - $origin = $origin . ":" . $url->port if $url->port && $url->port != "80"; + my $originurl = $c->req->url->to_abs; + $origin = $dm->scheme . '://' . $originurl->host; + $origin = $origin . ":" . $originurl->port if $originurl->port && $originurl->port != "80"; $origin = $origin . $dm->route; } $origin = $origin . $filepath; my $xml = _build_metalink( $dm, $folder->path, $file, $country, $region, \@mirrors_country, \@mirrors_region, - \@mirrors_rest, $origin, 'MirrorCache', $root->is_remote ? $root->location($dm) : $root->redirect($dm, $folder->path) ); + \@mirrors_rest, $origin, 'MirrorCache', $url); $c->res->headers->content_disposition('attachment; filename="' .$basename. '.metalink"'); $c->render(data => $xml, format => 'metalink'); return 1; } if ($dm->mirrorlist) { - my $url = $c->req->url->to_abs; + $url = $c->req->url->to_abs; my @mirrordata; if ($country and !$dm->avoid_countries || !(grep { $country eq $_ } $dm->avoid_countries)) { for my $m (@mirrors_country) { @@ -560,4 +578,106 @@ return 1; } +sub _render_btih() { + my ($c, $filename, $file) = @_; + + unless($file->{md5}) { + $c->render(status => 404, text => "File not found"); + return 1; + } + + $c->render(text => "$filename " . _calc_btih($filename, $file)); + return 1; +} + +sub _render_magnet() { + my ($c, $url, $filename, $file) = @_; + + unless($file->{piece_size}) { + $c->render(status => 404, text => "File not found"); + return 1; + } + + my $btih = _calc_btih($filename, $file); + my $md5 = $file->{md5}; + my $size = $file->{size}; + $filename = Mojo::Util::url_escape($filename); + $url = Mojo::Util::url_escape($url); + + $c->render(text => "magnet:?xt=urn:btih:$btih&xt=urn:md5:$md5&xl=$size&dn=$filename&as=$url"); + return 1; +} + +sub _render_torrent() { + my ($dm, $file, $mirrors_country, $mirrors_region, $mirrors_rest, $url) = @_; + + my $c = $dm->c; + + unless($file->{piece_size}) { + $c->render(status => 404, text => "File not found"); + return 1; + } + + my $tracker = $ENV{MIRRORCACHE_TRACKER} // 'http://tracker.opensuse.org:6969/announce'; + my $trackerlen = length($tracker); + my $filename = $file->{name}; + my $filenamelen = length($filename); + my $size = $file->{size}; + my $mtime = $file->{mtime}; + my $md5 = $file->{md5}; + my $piece_size = $file->{piece_size}; + my $sha1 = pack 'H*', $file->{sha1}; + my $sha256 = pack 'H*', $file->{sha256}; + my $pieces = pack 'H*', $file->{pieces}; + + my $header = "d8:announce$trackerlen:$tracker" . + "13:announce-listll$trackerlen:${tracker}ee" . + "7:comment$filenamelen:$filename" . + "10:created by11:MirrorCache13:creation datei${mtime}e" . + "4:infod6:lengthi${size}e" . + "6:md5sum32:$md5" . + "4:name$filenamelen:$filename" . + "12:piece lengthi${piece_size}e" . + "6:pieces" . length($pieces) . ":"; + + my $footer = "4:sha120:$sha1" . + "6:sha25632:${sha256}e" . + "7:sourcesl"; + if (scalar(@$mirrors_country) > 0 || scalar(@$mirrors_region) > 0 || scalar(@$mirrors_rest) > 0) { + for my $m (@$mirrors_country, @$mirrors_region, @$mirrors_rest) { + $footer = $footer . length($m->{url}) . ":" . $m->{url}; + } + } else { + $footer = $footer . length($url) . ":" . $url; + } + $footer = $footer . 'ee'; + + $c->res->headers->content_length(length($header) + length($pieces) + length($footer)); + $c->write($header => sub () { + $c->write($pieces => sub () { + $c->write($footer => sub () {$c->finish}); + }) + }); + + return 1; +} + +sub _calc_btih() { + my ($filename, $file) = @_; + + my $sha1 = Digest::SHA->new(1); + $sha1-> + add('d')-> + add('6:lengthi:' . $file->{size})-> + add('6:md5sum32:' . $file->{md5})-> + add('4:name' . length($filename) . ":$filename")-> + add('12:piece lengthi:' . $file->{piece_size})-> + add('6:pieces:' . length($file->{pieces}))->add($file->{pieces})-> + add('4:sha120:' . $file->{sha1})-> + add('6:sha25632:' . $file->{sha256})-> + add('e'); + + return $sha1->hexdigest; +} + 1; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.033/lib/MirrorCache/WebAPI/Plugin/RootLocal.pm new/MirrorCache-1.034/lib/MirrorCache/WebAPI/Plugin/RootLocal.pm --- old/MirrorCache-1.033/lib/MirrorCache/WebAPI/Plugin/RootLocal.pm 2022-05-18 14:28:29.000000000 +0200 +++ new/MirrorCache-1.034/lib/MirrorCache/WebAPI/Plugin/RootLocal.pm 2022-06-07 10:53:05.000000000 +0200 @@ -79,7 +79,9 @@ if ($redirect) { $res = !!$c->redirect_to($redirect . $root_subtree . $filepath); } else { - $res = !!$c->render_file(filepath => $self->rootpath($filepath) . $root_subtree . $filepath, content_type => $dm->mime); + my $rootpath = $self->rootpath($filepath); + return !!$c->render(status => 404, text => "File $filepath not found") unless $rootpath; + $res = !!$c->render_file(filepath => $rootpath . $root_subtree . $filepath, content_type => $dm->mime); } $c->stat->redirect_to_root($dm, $not_miss); return $res; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.033/lib/MirrorCache/WebAPI.pm new/MirrorCache-1.034/lib/MirrorCache/WebAPI.pm --- old/MirrorCache-1.033/lib/MirrorCache/WebAPI.pm 2022-05-18 14:28:29.000000000 +0200 +++ new/MirrorCache-1.034/lib/MirrorCache/WebAPI.pm 2022-06-07 10:53:05.000000000 +0200 @@ -116,6 +116,7 @@ $self->plugin('Helpers', root => $root, route => '/download'); $self->plugin('Subsidiary'); + $self->plugin('Project'); if ($root) { # check prefix if ('rsync://' eq substr($root, 0, 8)) { @@ -183,6 +184,7 @@ $rest_usr_r->put('/myserver/location/:id')->name('rest_put_myserver_location')->to('myserver_location#update_location'); $rest_r->get('/folder')->name('rest_folder')->to('table#list', table => 'Folder'); + $rest_r->get('/repmirror')->name('rest_repmirror')->to('report_mirror#list'); $rest_r->get('/folder_jobs/:id')->name('rest_folder_jobs')->to('folder_jobs#list'); $rest_r->get('/myip')->name('rest_myip')->to('my_ip#show') if $self->_geodb; @@ -190,6 +192,9 @@ $rest_r->get('/stat')->name('rest_stat')->to('stat#list'); $rest_r->get('/mystat')->name('rest_mystat')->to('stat#mylist'); + my $report_r = $r->any('/report')->to(namespace => 'MirrorCache::WebAPI::Controller::Report'); + $report_r->get('/mirror')->name('report_mirror')->to('mirror#index'); + my $app_r = $r->any('/app')->to(namespace => 'MirrorCache::WebAPI::Controller::App'); $app_r->get('/server')->name('server')->to('server#index'); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.033/lib/MirrorCache/resources/migrations/Pg.sql new/MirrorCache-1.034/lib/MirrorCache/resources/migrations/Pg.sql --- old/MirrorCache-1.033/lib/MirrorCache/resources/migrations/Pg.sql 2022-05-18 14:28:29.000000000 +0200 +++ new/MirrorCache-1.034/lib/MirrorCache/resources/migrations/Pg.sql 2022-06-07 10:53:05.000000000 +0200 @@ -280,3 +280,17 @@ username varchar(64) not null, primary key(server_id,username) ); +-- 24 up +create table report ( + id serial NOT NULL PRIMARY KEY, + title varchar(64), + description varchar(256), + interval_seconds int DEFAULT 3600 +); +insert into report select 1, 'Mirrors', NULL, 15*60; +create table report_body ( + report_id int references report on delete cascade, + dt timestamp, + body text +); +create index if not exists report_content_dt_inx on report_body(report_id, dt); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.033/lib/MirrorCache/resources/migrations/mysql.sql new/MirrorCache-1.034/lib/MirrorCache/resources/migrations/mysql.sql --- old/MirrorCache-1.033/lib/MirrorCache/resources/migrations/mysql.sql 2022-05-18 14:28:29.000000000 +0200 +++ new/MirrorCache-1.034/lib/MirrorCache/resources/migrations/mysql.sql 2022-06-07 10:53:05.000000000 +0200 @@ -276,3 +276,18 @@ primary key(server_id,username), constraint `fk_server_admin` FOREIGN KEY(server_id) references server(id) on delete cascade ); +-- 24 up +create table report ( + id int AUTO_INCREMENT NOT NULL PRIMARY KEY, + title varchar(64), + description varchar(256), + interval_seconds int DEFAULT 3600 +); +insert into report select 1, 'Mirrors', NULL, 15*60; +create table report_body ( + report_id int, + dt timestamp, + body text, + constraint `fk_report_body_report` FOREIGN KEY(report_id) references report(id) on delete cascade +); +create index if not exists report_content_dt_inx on report_body(report_id, dt); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.033/t/environ/02-files-hashes.sh new/MirrorCache-1.034/t/environ/02-files-hashes.sh --- old/MirrorCache-1.033/t/environ/02-files-hashes.sh 2022-05-18 14:28:29.000000000 +0200 +++ new/MirrorCache-1.034/t/environ/02-files-hashes.sh 2022-06-07 10:53:05.000000000 +0200 @@ -57,3 +57,12 @@ $mc/curl /download/folder1/file1.1.dat.metalink | grep -o '<hash type="md5">b2c5860a03d2c4f1f049a3b2409b39a8</hash>' $mc/curl /download/folder1/file1.1.dat.metalink | grep -o '<hash type="sha-256">63d19a99ef7db94ddbb1e4a5083062226551cd8197312e3aa0aa7c369ac3e458</hash>' $mc/curl /download/folder1/file1.1.dat.metalink | grep -o '<hash>5179db3d4263c9cb4ecf0edbc653ca460e3678b7</hash>' + +$mc/curl -I /download/folder1/file1.1.dat.btih | grep '200 OK' +$mc/curl /download/folder1/file1.1.dat.btih +$mc/curl -I /download/folder1/file1.1.dat.magnet | grep '200 OK' +$mc/curl /download/folder1/file1.1.dat.magnet +$mc/curl -I /download/folder1/file1.1.dat.torrent | grep '200 OK' +$mc/curl /download/folder1/file1.1.dat.torrent + +echo success diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.033/t/environ/02-files.sh new/MirrorCache-1.034/t/environ/02-files.sh --- old/MirrorCache-1.033/t/environ/02-files.sh 2022-05-18 14:28:29.000000000 +0200 +++ new/MirrorCache-1.034/t/environ/02-files.sh 2022-06-07 10:53:05.000000000 +0200 @@ -22,10 +22,11 @@ for x in $mc $ap7 $ap8; do mkdir -p $x/dt/{folder1,folder2,folder3} - mkdir -p $x/dt/Folder1 + mkdir -p $x/dt/Folder1/repodata echo $x/dt/{folder1,folder2,folder3}/{file1.1,file2.1}.dat | xargs -n 1 touch echo $x/dt/folder1/file1.dat | xargs -n 1 touch echo $x/dt/Folder1/file1.1.DAT | xargs -n 1 touch + echo $x/dt/Folder1/repodata/repomd.xml | xargs -n 1 touch mkdir -p $x/dt/folder1.11test/ for f in $unversionedfiles; do str=1 @@ -37,6 +38,8 @@ $ap7/start $ap8/start +$mc/curl -I -H "Accept: */*, application/metalink+xml" /download/Folder1/repodata/repomd.xml | grep '200 OK' + $mc/sql "insert into server(hostname,urldir,enabled,country,region) select '$($ap7/print_address)','','t','us','na'" $mc/sql "insert into server(hostname,urldir,enabled,country,region) select '$($ap8/print_address)','','t','ca','na'" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.033/t/environ/14-project.sh new/MirrorCache-1.034/t/environ/14-project.sh --- old/MirrorCache-1.033/t/environ/14-project.sh 2022-05-18 14:28:29.000000000 +0200 +++ new/MirrorCache-1.034/t/environ/14-project.sh 2022-06-07 10:53:05.000000000 +0200 @@ -12,8 +12,10 @@ for x in $mc $ap7 $ap8 $ap6; do mkdir -p $x/dt/{folder1,folder2,folder3} mkdir -p $x/dt/project1/{folder1,folder2,folder3} + mkdir -p $x/dt/project2/{folder1,folder2,folder3} echo $x/dt/{folder1,folder2,folder3}/{file1.1,file2.1}.dat | xargs -n 1 touch echo $x/dt/project1/{folder1,folder2,folder3}/{file1.1,file2.1}.dat | xargs -n 1 touch + echo $x/dt/project2/{folder1,folder2,folder3}/{file1.1,file2.1}.dat | xargs -n 1 touch done $ap6/start @@ -25,9 +27,10 @@ $mc/sql "insert into server(hostname,urldir,enabled,country,region) select '$($ap6/print_address)','','t','us','na'" $mc/sql "insert into server(hostname,urldir,enabled,country,region) select '$($ap7/print_address)','','t','us','na'" -$mc/sql "insert into server(hostname,urldir,enabled,country,region) select '$($ap8/print_address)','','t','us','na'" +$mc/sql "insert into server(hostname,urldir,enabled,country,region) select '$($ap8/print_address)','','t','de','eu'" -$mc/sql "insert into project(name,path,etalon) select 'proj1','/project1', 1" +$mc/sql "insert into project(name,path,etalon) select 'proj1','/project1', 3" +$mc/sql "insert into project(name,path,etalon) select 'proj 2','/project2', 3" $mc/backstage/job -e folder_sync -a '["/project1/folder1"]' $mc/backstage/job -e mirror_scan -a '["/project1/folder1","us"]' @@ -40,11 +43,20 @@ $mc/backstage/job -e folder_sync -a '["/project1/folder2"]' -$mc/backstage/job -e mirror_scan -a '["/project1/folder2","us"]' +$mc/backstage/job -e mirror_scan -a '["/project1/folder2"]' $mc/backstage/shoot + +$mc/backstage/job -e folder_sync -a '["/project2/folder1"]' +$mc/backstage/job -e mirror_scan -a '["/project2/folder1"]' +$mc/backstage/shoot + +$mc/backstage/job -e report -a '["once"]' +$mc/backstage/shoot + +$mc/curl -s /rest/repmirror | grep '[{"country":"de","proj1score":"100","proj1victim":"","proj2score":"100","proj2victim":"","region":"eu","url":"127.0.0.1:1314"},{"country":"us","proj1score":"100","proj1victim":"","proj2score":"100","proj2victim":"","region":"na","url":"127.0.0.1:1294"},{"country":"us","proj1score":"50","proj1victim":"\/project1\/folder2","proj2score":"100","proj2victim":"","region":"na","url":"127.0.0.1:1304"}]' + $mc/curl -s /rest/project/proj1/mirror_summary $mc/curl -s /rest/project/proj1/mirror_summary | grep -E '"current":"?2' | grep -E '"outdated":"?1' -$mc/curl -s /rest/project/proj1/mirror_list # $mc/curl -s /rest/project/proj1/mirror_list | grep -E '{"current":"?1"?,"server_id":"?1"?,"url":"127.0.0.1:1294"},{"current":"?1"?,"server_id":"?3"?,"url":"127.0.0.1:1314"},{"current":"?0"?,"server_id":"?2"?,"url":"127.0.0.1:1304"}' $mc/curl -s /rest/project/proj1/mirror_list | grep '{"current":1,"server_id":1,"url":"127.0.0.1:1294"},{"current":1,"server_id":3,"url":"127.0.0.1:1314"},{"current":0,"server_id":2,"url":"127.0.0.1:1304"}' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.033/t/lib/Dockerfile.environ.mariadb new/MirrorCache-1.034/t/lib/Dockerfile.environ.mariadb --- old/MirrorCache-1.033/t/lib/Dockerfile.environ.mariadb 2022-05-18 14:28:29.000000000 +0200 +++ new/MirrorCache-1.034/t/lib/Dockerfile.environ.mariadb 2022-06-07 10:53:05.000000000 +0200 @@ -5,7 +5,6 @@ RUN sed -i 's,http://download.opensuse.org,http://mirrorcache.opensuse.org/download,g' /etc/zypp/repos.d/*repo RUN zypper ar -f http://mirrorcache.opensuse.org/repositories/openSUSE:infrastructure:MirrorCache/openSUSE_Leap_15.3 mc -RUN zypper ar -f http://mirrorcache.opensuse.org/repositories/home:/andriinikitin:/branches:/devel:/languages:/perl:/CPAN-M/15.3 pm RUN zypper ar -f http://mirrorcache.opensuse.org/repositories/devel:/languages:/perl/openSUSE_Leap_15.3 perl RUN zypper --gpg-auto-import-keys ref diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.033/t/lib/Dockerfile.environ.postgresql new/MirrorCache-1.034/t/lib/Dockerfile.environ.postgresql --- old/MirrorCache-1.033/t/lib/Dockerfile.environ.postgresql 2022-05-18 14:28:29.000000000 +0200 +++ new/MirrorCache-1.034/t/lib/Dockerfile.environ.postgresql 2022-06-07 10:53:05.000000000 +0200 @@ -5,7 +5,6 @@ RUN sed -i 's,http://download.opensuse.org,http://mirrorcache.opensuse.org/download,g' /etc/zypp/repos.d/*repo RUN zypper ar -f http://mirrorcache.opensuse.org/repositories/openSUSE:infrastructure:MirrorCache/openSUSE_Leap_15.3 mc -RUN zypper ar -f http://mirrorcache.opensuse.org/repositories/home:/andriinikitin:/branches:/devel:/languages:/perl:/CPAN-M/15.3 pm RUN zypper --gpg-auto-import-keys ref # install MirrorCache here to fetch all dependencies diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.033/t/lib/Dockerfile.systemd.mariadb new/MirrorCache-1.034/t/lib/Dockerfile.systemd.mariadb --- old/MirrorCache-1.033/t/lib/Dockerfile.systemd.mariadb 2022-05-18 14:28:29.000000000 +0200 +++ new/MirrorCache-1.034/t/lib/Dockerfile.systemd.mariadb 2022-06-07 10:53:05.000000000 +0200 @@ -5,7 +5,6 @@ RUN sed -i 's,http://download.opensuse.org,https://mirrorcache.opensuse.org/download,g' /etc/zypp/repos.d/*repo RUN zypper ar -f https://mirrorcache.opensuse.org/repositories/openSUSE:infrastructure:MirrorCache/openSUSE_Leap_15.3 mc -RUN zypper ar -f https://download.opensuse.org/repositories/home:/andriinikitin:/branches:/devel:/languages:/perl:/CPAN-M/15.3 pm RUN zypper ar -f https://download.opensuse.org/repositories/devel:/languages:/perl/openSUSE_Leap_15.3 perl RUN zypper --gpg-auto-import-keys ref diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.033/t/lib/Dockerfile.systemd.postgresql new/MirrorCache-1.034/t/lib/Dockerfile.systemd.postgresql --- old/MirrorCache-1.033/t/lib/Dockerfile.systemd.postgresql 2022-05-18 14:28:29.000000000 +0200 +++ new/MirrorCache-1.034/t/lib/Dockerfile.systemd.postgresql 2022-06-07 10:53:05.000000000 +0200 @@ -5,7 +5,6 @@ RUN sed -i 's,http://download.opensuse.org,http://mirrorcache.opensuse.org/download,g' /etc/zypp/repos.d/*repo RUN zypper ar -f http://mirrorcache.opensuse.org/repositories/openSUSE:infrastructure:MirrorCache/openSUSE_Leap_15.3 mc -RUN zypper ar -f http://mirrorcache.opensuse.org/repositories/home:/andriinikitin:/branches:/devel:/languages:/perl:/CPAN-M/15.3 pm RUN zypper --gpg-auto-import-keys ref # install MirrorCache here to fetch all dependencies diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.033/templates/report/mirror/index.html.ep new/MirrorCache-1.034/templates/report/mirror/index.html.ep --- old/MirrorCache-1.033/templates/report/mirror/index.html.ep 1970-01-01 01:00:00.000000000 +0100 +++ new/MirrorCache-1.034/templates/report/mirror/index.html.ep 2022-06-07 10:53:05.000000000 +0200 @@ -0,0 +1,31 @@ +% layout 'bootstrap'; +% title 'Mirror Report'; + +% content_for 'ready_function' => begin + setupReportTable(); +% end + +<div class="row"> + <div class="col-sm-12"> + <h2><%= title %></h2> + + %= include 'layouts/info' + + <table id="servers" class="reporttable table table-striped"> + <thead> + <tr> + <th class="col_value">Region</th> + <th class="col_value">Country</th> + <th class="col_value">Url</th> + % for my $project (@$projects) { + <th class="col_value"><%= $project %> score</th> + <th class="col_value"><%= $project %> victim</th> + % } + </tr> + </thead> + <tbody> + </tbody> + </table> + <input type="hidden" id="reporttable_api_url" value="/rest/repmirror"/> + </div> +</div> ++++++ MirrorCache.obsinfo ++++++ --- /var/tmp/diff_new_pack.KNLQIW/_old 2022-06-15 00:34:17.790702577 +0200 +++ /var/tmp/diff_new_pack.KNLQIW/_new 2022-06-15 00:34:17.794702582 +0200 @@ -1,5 +1,5 @@ name: MirrorCache -version: 1.033 -mtime: 1652876909 -commit: f58f3923539b52c99a595c5654134826a15191c9 +version: 1.034 +mtime: 1654591985 +commit: 2650bc3c1b58c91f67aabc3e58ca518015eca3cc ++++++ cache.tar.xz ++++++