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-08-19 17:56:13
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/MirrorCache (Old)
 and      /work/SRC/openSUSE:Factory/.MirrorCache.new.2083 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "MirrorCache"

Fri Aug 19 17:56:13 2022 rev:17 rq:998020 version:1.037

Changes:
--------
--- /work/SRC/openSUSE:Factory/MirrorCache/MirrorCache.changes  2022-06-30 
13:18:39.521546715 +0200
+++ /work/SRC/openSUSE:Factory/.MirrorCache.new.2083/MirrorCache.changes        
2022-08-19 17:59:01.320420212 +0200
@@ -1,0 +2,15 @@
+Thu Aug 11 15:10:05 UTC 2022 - Andrii Nikitin <andrii.niki...@suse.com>
+
+- Update to version 1.037:
+  * Add configuration for redirect for projects (#293)
+
+-------------------------------------------------------------------
+Fri Aug 05 12:14:42 UTC 2022 - Andrii Nikitin <andrii.niki...@suse.com>
+
+- Update to version 1.036:
+ * Fix mirrors report when missing folders (#288)
+ * Tweak systemd files (#291)
+ * Rework mirrors report (#290)
+ * Rescan if no country hit (#292)
+
+-------------------------------------------------------------------

Old:
----
  MirrorCache-1.035.obscpio

New:
----
  MirrorCache-1.037.obscpio

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ MirrorCache.spec ++++++
--- /var/tmp/diff_new_pack.o67KlN/_old  2022-08-19 17:59:01.756421125 +0200
+++ /var/tmp/diff_new_pack.o67KlN/_new  2022-08-19 17:59:01.760421134 +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.035
+Version:        1.037
 Release:        0
 Summary:        WebApp to redirect and manage mirrors
 License:        GPL-2.0-or-later

++++++ MirrorCache-1.035.obscpio -> MirrorCache-1.037.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/MirrorCache-1.035/dist/salt/profile/mirrorcache/files/usr/share/mirrorcache/sql/projects.sql
 
new/MirrorCache-1.037/dist/salt/profile/mirrorcache/files/usr/share/mirrorcache/sql/projects.sql
--- 
old/MirrorCache-1.035/dist/salt/profile/mirrorcache/files/usr/share/mirrorcache/sql/projects.sql
    2022-06-23 06:47:51.000000000 +0200
+++ 
new/MirrorCache-1.037/dist/salt/profile/mirrorcache/files/usr/share/mirrorcache/sql/projects.sql
    2022-08-11 15:29:02.000000000 +0200
@@ -9,18 +9,18 @@
 insert into project(name,path) select '15.2 
repo','/distribution/leap/15.2/repo';
 insert into project(name,path) select '15.2 
debug','/debug/distribution/leap/15.2/repo';
 insert into project(name,path) select '15.2 
source','/source/distribution/leap/15.2/repo';
-insert into project(name,path) select '15.2 
update','/update/distribution/leap/15.2/repo';
+insert into project(name,path) select '15.2 
update','/update/distribution/leap/15.2';
 
 insert into project(name,path) select '15.3 ISO','/distribution/leap/15.3/iso';
 insert into project(name,path) select '15.3 
repo','/distribution/leap/15.3/repo';
 insert into project(name,path) select '15.3 
debug','/debug/distribution/leap/15.3/repo';
 insert into project(name,path) select '15.3 
source','/source/distribution/leap/15.3/repo';
-insert into project(name,path) select '15.3 
update','/update/distribution/leap/15.3/repo';
-insert into project(name,path) select '15.3 
port','/port/aarch64/distribution/leap/15.3';
+insert into project(name,path) select '15.3 
update','/update/distribution/leap/15.3';
+insert into project(name,path) select '15.3 
port','/ports/aarch64/distribution/leap/15.3';
 
 insert into project(name,path) select '15.4 ISO','/distribution/leap/15.4/iso';
 insert into project(name,path) select '15.4 
repo','/distribution/leap/15.4/repo';
 insert into project(name,path) select '15.4 
debug','/debug/distribution/leap/15.4/repo';
 insert into project(name,path) select '15.4 
source','/source/distribution/leap/15.4/repo';
-insert into project(name,path) select '15.4 
update','/update/distribution/leap/15.4/repo';
-insert into project(name,path) select '15.4 
port','/port/aarch64/distribution/leap/15.4';
+insert into project(name,path) select '15.4 
update','/update/distribution/leap/15.4';
+insert into project(name,path) select '15.4 
port','/ports/aarch64/distribution/leap/15.4';
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/MirrorCache-1.035/dist/systemd/mirrorcache-hypnotoad.service 
new/MirrorCache-1.037/dist/systemd/mirrorcache-hypnotoad.service
--- old/MirrorCache-1.035/dist/systemd/mirrorcache-hypnotoad.service    
2022-06-23 06:47:51.000000000 +0200
+++ new/MirrorCache-1.037/dist/systemd/mirrorcache-hypnotoad.service    
2022-08-11 15:29:02.000000000 +0200
@@ -1,7 +1,7 @@
 [Unit]
 Description=MirrorCache webApp
 Before=apache2.service
-After=postgresql.service nss-lookup.target
+After=postgresql.service mariadb.service nss-lookup.target
 
 [Service]
 User=mirrorcache
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/MirrorCache-1.035/dist/systemd/mirrorcache.service 
new/MirrorCache-1.037/dist/systemd/mirrorcache.service
--- old/MirrorCache-1.035/dist/systemd/mirrorcache.service      2022-06-23 
06:47:51.000000000 +0200
+++ new/MirrorCache-1.037/dist/systemd/mirrorcache.service      2022-08-11 
15:29:02.000000000 +0200
@@ -1,7 +1,7 @@
 [Unit]
 Description=MirrorCache webApp
 Before=apache2.service
-After=postgresql.service nss-lookup.target
+After=postgresql.service mariadb.service nss-lookup.target
 
 [Service]
 User=mirrorcache
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/MirrorCache-1.035/lib/MirrorCache/Datamodule.pm 
new/MirrorCache-1.037/lib/MirrorCache/Datamodule.pm
--- old/MirrorCache-1.035/lib/MirrorCache/Datamodule.pm 2022-06-23 
06:47:51.000000000 +0200
+++ new/MirrorCache-1.037/lib/MirrorCache/Datamodule.pm 2022-08-11 
15:29:02.000000000 +0200
@@ -53,6 +53,7 @@
 
 has 'at';
 has '_mime';
+has 'mirror_country';
 
 my $TYPES = Mojolicious::Types->new;
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/MirrorCache-1.035/lib/MirrorCache/Schema/Result/Project.pm 
new/MirrorCache-1.037/lib/MirrorCache/Schema/Result/Project.pm
--- old/MirrorCache-1.035/lib/MirrorCache/Schema/Result/Project.pm      
2022-06-23 06:47:51.000000000 +0200
+++ new/MirrorCache-1.037/lib/MirrorCache/Schema/Result/Project.pm      
2022-08-11 15:29:02.000000000 +0200
@@ -23,6 +23,8 @@
   { data_type => "varchar", is_nullable => 0, size => 64 },
   "path",
   { data_type => "varchar", is_nullable => 0, size => 512 },
+  "redirect",
+  { data_type => "varchar", is_nullable => 0, size => 512 },
   db_sync_last => {
         data_type   => 'timestamp',
         is_nullable => 1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/MirrorCache-1.035/lib/MirrorCache/Schema/ResultSet/Project.pm 
new/MirrorCache-1.037/lib/MirrorCache/Schema/ResultSet/Project.pm
--- old/MirrorCache-1.035/lib/MirrorCache/Schema/ResultSet/Project.pm   
2022-06-23 06:47:51.000000000 +0200
+++ new/MirrorCache-1.037/lib/MirrorCache/Schema/ResultSet/Project.pm   
2022-08-11 15:29:02.000000000 +0200
@@ -13,20 +13,23 @@
     my $dbh = $self->result_source->schema->storage->dbh;
 
     my $sql = <<"END_SQL";
-select sum(case when diff = 0 then 1 else 0 end) as current, sum(case when 
diff = 0 then 0 else 1 end) as outdated
+    select sum(case when diff = 0 then 1 else 0 end) as current, sum(case when 
diff = 0 or diff is null then 0 else 1 end) as outdated
 from (
-select server_id, max(case when d1 <> d2 then 1 else 0 end) diff
+select
+    server_id,
+    max(case when d1 <> d2 or d1 is null then 1 else 0 end) diff
 from (
-select prj.etalon, f.path, f.id, fd.folder_id, fds.server_id, 
fds.folder_diff_id as d1, fds2.folder_diff_id as d2
-from project prj
+select s.id as server_id, prj.name, fds.folder_diff_id d1, fds2.folder_diff_id 
d2
+from server s
+join project prj on prj.name = ?
 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
-where prj.name = ?
+left join folder_diff_server fds  on fds.folder_diff_id = fd.id and 
fds.server_id = s.id
+join folder_diff_server fds2 on fds2.folder_diff_id = fd.id and fds2.server_id 
= prj.etalon
 ) x
-group by server_id) xx
+group by server_id
+having  sum(case when d1 is not null then 1 else 0 end) > 0
+) xx
 END_SQL
     my $prep = $dbh->prepare($sql);
     $prep->execute($name);
@@ -38,19 +41,22 @@
     my $dbh = $self->result_source->schema->storage->dbh;
 
     my $sql = <<"END_SQL";
-select server_id, min(case when d1 = d2 then 1 else 0 end) as current, 
concat(s.hostname, s.urldir) as url
+select
+    s.id server_id,
+    min(case when d1 = d2 then 1 else 0 end) as current,
+    concat(s.hostname, s.urldir) as url
 from (
-select fds.server_id, fds.folder_diff_id as d1, fds2.folder_diff_id as d2
-from project prj
+select s.id as server_id, prj.name, fds.folder_diff_id d1, fds2.folder_diff_id 
d2
+from server s
+join project prj on prj.name = ?
 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
-where prj.name = ?
+left join folder_diff_server fds  on fds.folder_diff_id = fd.id and 
fds.server_id = s.id
+join folder_diff_server fds2 on fds2.folder_diff_id = fd.id and fds2.server_id 
= prj.etalon
 ) x
 join server s on x.server_id = s.id
-group by x.server_id, s.id
+group by s.id, s.hostname, s.urldir
+having sum(case when d1 is not null then 1 else 0 end) > 0
 order by current desc, url
 END_SQL
     my $prep = $dbh->prepare($sql);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/MirrorCache-1.035/lib/MirrorCache/Schema/ResultSet/Server.pm 
new/MirrorCache-1.037/lib/MirrorCache/Schema/ResultSet/Server.pm
--- old/MirrorCache-1.035/lib/MirrorCache/Schema/ResultSet/Server.pm    
2022-06-23 06:47:51.000000000 +0200
+++ new/MirrorCache-1.037/lib/MirrorCache/Schema/ResultSet/Server.pm    
2022-08-11 15:29:02.000000000 +0200
@@ -254,33 +254,57 @@
     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
+with
+etalon as (
+    select prj.id project_id, prj.name, f.id as folder_id, fd2.id as diff_id, 
f.path
+    from project prj
+    join folder f on f.path like concat(prj.path,'%')
+    join folder_diff fd2 on fd2.folder_id = f.id
+    join folder_diff_server fds2 on fd2.id = fds2.folder_diff_id and 
fds2.server_id = prj.etalon
+    -- where prj.name = 'repositories'
+    $where1
+),
+project_folder_count as (
+    select project_id, count(*) cnt
+    from etalon
+    group by project_id
+)
+select s.id, s.region, s.country,
+    concat(s.hostname, s.urldir) as url,
+    project,
+    round(case when project_folder_count.cnt > 3 then s_eq * 100 / 
project_folder_count.cnt when s_eq =  project_folder_count.cnt then 100 else 50 
end, 0) score,
+    s_eq, s_ne, victim, project_folder_count.cnt
 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;
+select
+    cmp.project_id,
+    cmp.name project,
+    cmp.server_id,
+    sum(s_eq) s_eq, sum(s_ne) s_ne,
+    max(example) victim
+from (
+    select
+        etalon.project_id,
+        etalon.name,
+        case when etalon.diff_id = fd.id then 1 else 0 end as s_eq,
+        case when etalon.diff_id != fd.id or fd.id is null then 1 else 0 end 
as s_ne,
+        case when etalon.diff_id != fd.id or fd.id is null then path else '' 
end example,
+        fds.server_id
+    from etalon
+    left join folder_diff fd on fd.folder_id = etalon.folder_id
+    left join folder_diff_server fds on fds.folder_diff_id = fd.id
+) cmp
+group by server_id, project_id, name
+) smry
+join project_folder_count on project_folder_count.project_id = smry.project_id
+join server s on smry.server_id = s.id
+order by region, country, score, url, project;
 END_SQL
     my $prep = $dbh->prepare($sql);
     if ($project && $region) {
-        $prep->execute($project, $region);
+        $prep->execute($project, $project, $region);
     } elsif ($project) {
         $prep->execute($project);
     } elsif ($region) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/MirrorCache-1.035/lib/MirrorCache/WebAPI/Controller/Report/Mirrors.pm 
new/MirrorCache-1.037/lib/MirrorCache/WebAPI/Controller/Report/Mirrors.pm
--- old/MirrorCache-1.035/lib/MirrorCache/WebAPI/Controller/Report/Mirrors.pm   
1970-01-01 01:00:00.000000000 +0100
+++ new/MirrorCache-1.037/lib/MirrorCache/WebAPI/Controller/Report/Mirrors.pm   
2022-08-11 15:29:02.000000000 +0200
@@ -0,0 +1,57 @@
+# 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::Mirrors;
+use Mojo::Base 'Mojolicious::Controller';
+use Mojo::JSON qw(decode_json encode_json);
+
+sub index {
+    my ($self) = @_;
+    my $project  = $self->param('project');
+    my $projects = $self->mcproject->list_full;
+
+    if ($project && $project ne "all") {
+        my @projects_new;
+        for my $p (@$projects) {
+            push @projects_new, $p if (0 == rindex $p->{name},  $project,    
0) ||
+                                      (0 == rindex $p->{alias}, $project,    
0) ||
+                                      (0 == rindex $p->{alias}, "c$project", 
0) ||
+                                      ( lc($project) eq 'tumbleweed' && 0 == 
rindex $p->{alias}, 'tw', 0  );
+        }
+        $projects = \@projects_new if scalar(@projects_new);
+    }
+
+    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->stash;
+        $self->render(
+            "report/mirrors/index",
+            mirrors  => $hash,
+            projects => $projects
+        );
+    };
+    my $error = $@;
+    if ($error) {
+         print STDERR "RESPMIRRORSREPORT : " . $error . "\n";
+         return $self->render(json => {error => $error}, status => 404);
+    }
+}
+
+1;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/MirrorCache-1.035/lib/MirrorCache/WebAPI/Controller/Rest/Project.pm 
new/MirrorCache-1.037/lib/MirrorCache/WebAPI/Controller/Rest/Project.pm
--- old/MirrorCache-1.035/lib/MirrorCache/WebAPI/Controller/Rest/Project.pm     
2022-06-23 06:47:51.000000000 +0200
+++ new/MirrorCache-1.037/lib/MirrorCache/WebAPI/Controller/Rest/Project.pm     
2022-08-11 15:29:02.000000000 +0200
@@ -1,4 +1,4 @@
-# Copyright (C) 2021 SUSE LLC
+# 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
@@ -44,4 +44,12 @@
     $self->render(json => { current => $rec->{current}, outdated => 
$rec->{outdated} });
 }
 
+sub list {
+    my ($self) = @_;
+
+    my $list = $self->mcproject->list_full;
+
+    $self->render(json => $list);
+}
+
 1;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/MirrorCache-1.035/lib/MirrorCache/WebAPI/Plugin/Dir.pm 
new/MirrorCache-1.037/lib/MirrorCache/WebAPI/Plugin/Dir.pm
--- old/MirrorCache-1.035/lib/MirrorCache/WebAPI/Plugin/Dir.pm  2022-06-23 
06:47:51.000000000 +0200
+++ new/MirrorCache-1.037/lib/MirrorCache/WebAPI/Plugin/Dir.pm  2022-08-11 
15:29:02.000000000 +0200
@@ -68,7 +68,7 @@
     return $c
       if _render_hashes($dm)
       || _render_small($dm)
-      || _redirect_geo($dm)
+      || _redirect_project_ln_geo($dm)
       || _redirect_normalized($dm)
       || _render_stats($dm)
       || _local_render($dm, 0) # check if we should render local
@@ -151,17 +151,25 @@
     return $c->render(status => 425, text => "Waiting in queue, at " . 
strftime("%Y-%m-%d %H:%M:%S", gmtime time) . " position: $pos");
 }
 
-sub _redirect_geo {
+# this combines similar checks for redirecting as specified in DB links and 
projects, as well as subsidiaries
+sub _redirect_project_ln_geo {
     my $dm = shift;
-    my $route = $dm->route;
     my ($path, $trailing_slash) = $dm->path;
 
     my $c = $dm->c;
-    my $ln;
-    $ln = $root->detect_ln($path);
+    # each project may have a redirect defined in DB, so all requests are 
redirected for it
+    my $redirect = $c->mcproject->redirect($path, $dm->region);
+    if ($redirect) {
+        $dm->redirect($dm->scheme . '://' . $redirect . $path);
+        $c->stat->redirect_to_region($dm);
+        return 1;
+    }
+
+    my $ln = $root->detect_ln($path);
     if ($ln) {
         # redirect to the symlink
         $dm->redirect($dm->route . $ln);
+        $c->stat->redirect_to_region($dm);
         return 1;
     }
     return undef if $trailing_slash || $path eq '/' || $dm->mirrorlist;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/MirrorCache-1.035/lib/MirrorCache/WebAPI/Plugin/Helpers.pm 
new/MirrorCache-1.037/lib/MirrorCache/WebAPI/Plugin/Helpers.pm
--- old/MirrorCache-1.035/lib/MirrorCache/WebAPI/Plugin/Helpers.pm      
2022-06-23 06:47:51.000000000 +0200
+++ new/MirrorCache-1.037/lib/MirrorCache/WebAPI/Plugin/Helpers.pm      
2022-08-11 15:29:02.000000000 +0200
@@ -143,6 +143,21 @@
         }
         $app->helper( trusted_addr => sub { %trust_addr{ 
shift->tx->original_remote_address } });
     }
+
+    $app->helper(
+        'region_name' => sub {
+            shift;
+            my $reg = shift;
+            return 'Unknown'       unless $reg;
+            return 'Oceania'       if $reg eq 'oc';
+            return 'North America' if $reg eq 'na';
+            return 'South America' if $reg eq 'sa';
+            return 'Africa'        if $reg eq 'af';
+            return 'Europe'        if $reg eq 'eu';
+            return 'Asia'          if $reg eq 'as';
+            return 'Unknown';
+        });
+
 }
 
 sub _current_user {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/MirrorCache-1.035/lib/MirrorCache/WebAPI/Plugin/Project.pm 
new/MirrorCache-1.037/lib/MirrorCache/WebAPI/Plugin/Project.pm
--- old/MirrorCache-1.035/lib/MirrorCache/WebAPI/Plugin/Project.pm      
2022-06-23 06:47:51.000000000 +0200
+++ new/MirrorCache-1.037/lib/MirrorCache/WebAPI/Plugin/Project.pm      
2022-08-11 15:29:02.000000000 +0200
@@ -22,11 +22,15 @@
 my @projects;
 my %projects_path;
 my %projects_alias;
+my %projects_redirect;
+my %projects_region_redirect;
 
 sub register {
     my ($self, $app) = @_;
 
     $app->helper('mcproject.list' => \&_list);
+    $app->helper('mcproject.list_full' => \&_list_full);
+    $app->helper('mcproject.redirect' => \&_redirect);
     return $self;
 }
 
@@ -39,14 +43,27 @@
         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
+        $alias =~ tr/ //ds;  # remove spaces
+        $alias =~ tr/\.//ds; # remove dots
+        $alias = "c$alias" if $alias =~ /^\d/;
+        $alias = lc($alias);
         $projects_path{$name} = $p->path;
         $projects_alias{$name} = $alias;
+        my $redirect = $p->redirect;
+        next unless $redirect;
+        my @parts = split ';', $redirect;
+        for my $r (@parts) {
+            my $prefix = substr($r,0,3);
+            my $c = chop($prefix);
+            if (':' eq $c) {
+                $projects_region_redirect{$prefix}{$name} = substr($r,3);
+            } else {
+                $projects_redirect{$name} = $r;
+            }
+        }
     }
 }
 
@@ -61,4 +78,37 @@
     return \@res;
 }
 
+sub _list_full {
+    my ($c) = @_;
+    _init_if_needed($c);
+
+    my @res;
+    for my $p (@projects) {
+        my $name  = $p->name;
+        my $alias = $projects_alias{$name};
+        my $path  = $projects_path{$name};
+
+        my %prj = ( name => $name, alias => $alias, path => $path );
+        push @res, \%prj;
+    }
+    return \@res;
+}
+
+sub _redirect {
+    my ($c, $path, $region) = @_;
+    _init_if_needed($c);
+
+    for my $p (@projects) {
+        my $name  = $p->name;
+        my $redirect;
+        $redirect = $projects_region_redirect{$region}{$name} if $region;
+        $redirect = $projects_redirect{$name} unless $redirect;
+        next unless $redirect;
+        my $ppath  = $projects_path{$name};
+        return $redirect if (0 == rindex($path, $ppath, 0));
+    }
+    return '';
+}
+
+
 1;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/MirrorCache-1.035/lib/MirrorCache/WebAPI/Plugin/RenderFileFromMirror.pm 
new/MirrorCache-1.037/lib/MirrorCache/WebAPI/Plugin/RenderFileFromMirror.pm
--- old/MirrorCache-1.035/lib/MirrorCache/WebAPI/Plugin/RenderFileFromMirror.pm 
2022-06-23 06:47:51.000000000 +0200
+++ new/MirrorCache-1.037/lib/MirrorCache/WebAPI/Plugin/RenderFileFromMirror.pm 
2022-08-11 15:29:02.000000000 +0200
@@ -131,6 +131,7 @@
         $mirror = $mirrors_region[0]  if !$mirror && @mirrors_region;
         $mirror = $mirrors_rest[0]    if !$mirror && @mirrors_rest;
 
+        $dm->mirror_country($mirror->{country}) if $mirror;
         if ($dm->extra) {
             if ($mirror) {
                 $c->stat->redirect_to_mirror($mirror->{mirror_id}, $dm);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/MirrorCache-1.035/lib/MirrorCache/WebAPI/Plugin/Stat.pm 
new/MirrorCache-1.037/lib/MirrorCache/WebAPI/Plugin/Stat.pm
--- old/MirrorCache-1.035/lib/MirrorCache/WebAPI/Plugin/Stat.pm 2022-06-23 
06:47:51.000000000 +0200
+++ new/MirrorCache-1.037/lib/MirrorCache/WebAPI/Plugin/Stat.pm 2022-08-11 
15:29:02.000000000 +0200
@@ -68,7 +68,7 @@
     $path = $dm->root_subtree . $path;
     my $rows = $self->rows;
     my @rows = defined $rows? @$rows : ();
-    push @rows, [ $dm->ip_sha1, scalar $dm->agent, scalar ($path . 
$trailing_slash), $dm->country, datetime_now(), $mirror_id, $dm->folder_id, 
$dm->file_id, $dm->is_secure, $dm->is_ipv4, $dm->metalink? 1 : 0, 
$dm->mirrorlist? 1 : 0, $dm->is_head, $$, int($dm->elapsed*1000), 
$dm->file_age, $dm->folder_scan_last ];
+    push @rows, [ $dm->ip_sha1, scalar $dm->agent, scalar ($path . 
$trailing_slash), $dm->country, datetime_now(), $mirror_id, $dm->folder_id, 
$dm->file_id, $dm->is_secure, $dm->is_ipv4, $dm->metalink? 1 : 0, 
$dm->mirrorlist? 1 : 0, $dm->is_head, $$, int($dm->elapsed*1000), 
$dm->file_age, $dm->folder_scan_last, $dm->mirror_country ];
     my $cnt = @rows;
     if ($cnt >= $FLUSH_COUNT) {
         $self->rows(undef);
@@ -108,13 +108,15 @@
             $dbh = $self->schema->storage->dbh;
             my $prep = $dbh->prepare($sql);
             for my $row (@rows) {
-                my $folder_id  = $row->[6];
+                my $folder_id = $row->[6];
                 my $mirror_id = $row->[5];
+                my $country   = $row->[3];
+                my $country_m = pop @$row;
                 my $scan_last = pop @$row;
                 my $file_age  = pop @$row;
                 $prep->execute(@$row);
                 if ($folder_id) {
-                    next if $mirror_id > 0;
+                    next if $mirror_id > 0 && (!$country || !$country_m || 
($country eq $country_m));
                     next if $mirror_id < -1;
                     my $agent      = $row->[1];
                     next unless -1 == index($agent, 'bot');
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/MirrorCache-1.035/lib/MirrorCache/WebAPI/Plugin/Subsidiary.pm 
new/MirrorCache-1.037/lib/MirrorCache/WebAPI/Plugin/Subsidiary.pm
--- old/MirrorCache-1.035/lib/MirrorCache/WebAPI/Plugin/Subsidiary.pm   
2022-06-23 06:47:51.000000000 +0200
+++ new/MirrorCache-1.037/lib/MirrorCache/WebAPI/Plugin/Subsidiary.pm   
2022-08-11 15:29:02.000000000 +0200
@@ -152,7 +152,7 @@
     my ($c, $region, $country) = @_;
     $region = $country if ($country && $subsidiary_country{$country});
     my $url;
-    $url = $subsidiary_urls{$region} if $region;
+    $url = $subsidiary_urls{$region} if $region && !$subsidiary_local{$region};
     my @res = ($url? $region : '');
 
     for my $s (@regions) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/MirrorCache-1.035/lib/MirrorCache/WebAPI.pm 
new/MirrorCache-1.037/lib/MirrorCache/WebAPI.pm
--- old/MirrorCache-1.035/lib/MirrorCache/WebAPI.pm     2022-06-23 
06:47:51.000000000 +0200
+++ new/MirrorCache-1.037/lib/MirrorCache/WebAPI.pm     2022-08-11 
15:29:02.000000000 +0200
@@ -161,6 +161,7 @@
     my $rest_r    = $rest->any('/')->to(namespace => 
'MirrorCache::WebAPI::Controller::Rest');
     $rest_r->get('/server')->name('rest_server')->to('table#list', table => 
'Server');
     $rest_r->get('/server/:id')->to('table#list', table => 'Server');
+    $rest_r->get('/project')->to('project#list');
     $rest_r->get('/project/:name')->to('project#show');
     
$rest_r->get('/project/:name/mirror_summary')->to('project#mirror_summary');
     $rest_r->get('/project/:name/mirror_list')->to('project#mirror_list');
@@ -195,6 +196,8 @@
 
     my $report_r = $r->any('/report')->to(namespace => 
'MirrorCache::WebAPI::Controller::Report');
     $report_r->get('/mirror')->name('report_mirror')->to('mirror#index');
+    $report_r->get('/mirrors')->name('report_mirrors')->to('mirrors#index');
+    
$report_r->get('/mirrors/:project')->name('report_mirrors_project')->to('mirrors#index');
 
     my $app_r = $r->any('/app')->to(namespace => 
'MirrorCache::WebAPI::Controller::App');
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/MirrorCache-1.035/lib/MirrorCache/resources/migrations/Pg.sql 
new/MirrorCache-1.037/lib/MirrorCache/resources/migrations/Pg.sql
--- old/MirrorCache-1.035/lib/MirrorCache/resources/migrations/Pg.sql   
2022-06-23 06:47:51.000000000 +0200
+++ new/MirrorCache-1.037/lib/MirrorCache/resources/migrations/Pg.sql   
2022-08-11 15:29:02.000000000 +0200
@@ -294,3 +294,6 @@
     body text
 );
 create index if not exists report_content_dt_inx on report_body(report_id, dt);
+-- 25 up
+alter table project add column if not exists redirect varchar(512);
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/MirrorCache-1.035/lib/MirrorCache/resources/migrations/mysql.sql 
new/MirrorCache-1.037/lib/MirrorCache/resources/migrations/mysql.sql
--- old/MirrorCache-1.035/lib/MirrorCache/resources/migrations/mysql.sql        
2022-06-23 06:47:51.000000000 +0200
+++ new/MirrorCache-1.037/lib/MirrorCache/resources/migrations/mysql.sql        
2022-08-11 15:29:02.000000000 +0200
@@ -291,3 +291,6 @@
     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);
+-- 25 up
+alter table project add column if not exists redirect varchar(512);
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/MirrorCache-1.035/t/environ/14-project-hq.sh 
new/MirrorCache-1.037/t/environ/14-project-hq.sh
--- old/MirrorCache-1.035/t/environ/14-project-hq.sh    2022-06-23 
06:47:51.000000000 +0200
+++ new/MirrorCache-1.037/t/environ/14-project-hq.sh    2022-08-11 
15:29:02.000000000 +0200
@@ -77,8 +77,33 @@
     mc$i/backstage/shoot
 done
 
+echo project1 to eu if we contacted hq from na
+$mc9/sql "update project set redirect = 'na:$eu_address/download' where id = 1"
+echo project2 to na even if we contacted hq from as or eu
+$mc9/sql "update project set redirect = 
'as:$na_address/download;eu:$na_address/download' where id = 2"
+echo test project1 redirects
+$mc9/curl --interface $na_interface -I  /download/project1/folder1/file1.1.dat 
| grep "$eu_address"
+$mc9/curl --interface $eu_interface -I  /download/project1/folder1/file1.1.dat 
| grep "$eu_address"
+
+echo test project2 redirects
+$mc9/curl --interface $eu_interface -I  /download/project2/folder1/file1.1.dat 
| grep "$na_address"
+$mc9/curl --interface $as_interface -I  /download/project2/folder1/file1.1.dat 
| grep "$na_address"
+$mc9/curl --interface $as_interface -IL /download/project2/folder1/file1.1.dat 
| grep -E "$($ap3/print_address)|$($ap4/print_address)"
+$mc9/curl --interface $na_interface -I  /download/project2/folder1/file1.1.dat 
| grep "$na_address"
+
+echo "Let's pretend proj 2 has no good mirrors in as and we redirect all 
requests from it to na subsidiary"
+$mc8/sql "update project set redirect = '$na_address/download' where id = 2"
+
+echo project1 redirects to regular mirror
+$mc8/curl -I /download/project1/folder1/file1.1.dat | grep -E 
"$($ap7/print_address)|$($ap8/print_address)"
+
+echo project2 redirects to na
+$mc8/curl -I /download/project2/folder1/file1.1.dat | grep 
$na_address/download/project2/folder1/file1.1.dat
+$mc8/curl -IL /download/project2/folder1/file1.1.dat | grep -E 
"$($ap3/print_address)|$($ap4/print_address)"
+
+
 # all countries present in report
-mc9/curl -s /rest/repmirror \
+$mc9/curl -s /rest/repmirror \
           | grep '"country":"br"' \
           | grep '"country":"de"' \
           | grep '"country":"dk"' \
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/MirrorCache-1.035/t/environ/14-project-report.sh 
new/MirrorCache-1.037/t/environ/14-project-report.sh
--- old/MirrorCache-1.035/t/environ/14-project-report.sh        1970-01-01 
01:00:00.000000000 +0100
+++ new/MirrorCache-1.037/t/environ/14-project-report.sh        2022-08-11 
15:29:02.000000000 +0200
@@ -0,0 +1,66 @@
+#!lib/test-in-container-environ.sh
+set -exo pipefail
+
+mc=$(environ mc $(pwd))
+
+$mc/start
+
+ap8=$(environ ap8)
+ap7=$(environ ap7)
+ap6=$(environ ap6)
+ap5=$(environ ap5)
+ap4=$(environ ap4)
+
+for x in $mc $ap7 $ap8 $ap6 $ap5 $ap4; 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
+
+$ap4/start
+$ap5/start
+$ap6/start
+$ap7/start
+$ap8/start
+
+# remove some files and folders
+rm $ap7/dt/project1/folder2/file2.1.dat
+rm $ap7/dt/project2/folder2/file2.1.dat
+rm -r $ap5/dt/project2/folder2/
+rm -r $ap5/dt/project1/
+rm -r $ap4/dt/project2/
+
+$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','de','eu'"
+$mc/sql "insert into server(hostname,urldir,enabled,country,region) select 
'$($ap5/print_address)','','t','cn','as'"
+$mc/sql "insert into server(hostname,urldir,enabled,country,region) select 
'$($ap4/print_address)','','t','jp','as'"
+
+$mc/sql "insert into project(name,path,etalon) select 'proj1','/project1', 3"
+$mc/sql "insert into project(name,path,etalon) select '2.0 
1','/project2/folder1', 3"
+$mc/sql "insert into project(name,path,etalon) select '2.0 
2','/project2/folder2', 3"
+
+$mc/backstage/job -e folder_sync -a '["/project1/folder1"]'
+$mc/backstage/job -e mirror_scan -a '["/project1/folder1"]'
+$mc/backstage/job -e folder_sync -a '["/project1/folder2"]'
+$mc/backstage/job -e mirror_scan -a '["/project1/folder2"]'
+$mc/backstage/job -e folder_sync -a '["/project2/folder1"]'
+$mc/backstage/job -e mirror_scan -a '["/project2/folder1"]'
+$mc/backstage/job -e folder_sync -a '["/project2/folder2"]'
+$mc/backstage/job -e mirror_scan -a '["/project2/folder2"]'
+$mc/backstage/shoot
+
+$mc/backstage/job -e report -a '["once"]'
+$mc/backstage/shoot
+
+$mc/curl /report/mirrors | tidy --drop-empty-elements no | \
+   grep -A5 -F '<div class="repo">' | \
+   grep -A4 -F '<a class="repouncertain"' | \
+   grep -A3 -F '"diff in: /project2/folder2"' | \
+   grep -A2 -F '"http://127.0.0.1:1304/project2/folder2";>' | \
+   grep -C3 '\b2\b' | \
+   grep -C3 -F '</a>'
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/MirrorCache-1.035/t/environ/14-project.sh 
new/MirrorCache-1.037/t/environ/14-project.sh
--- old/MirrorCache-1.035/t/environ/14-project.sh       2022-06-23 
06:47:51.000000000 +0200
+++ new/MirrorCache-1.037/t/environ/14-project.sh       2022-08-11 
15:29:02.000000000 +0200
@@ -8,8 +8,10 @@
 ap8=$(environ ap8)
 ap7=$(environ ap7)
 ap6=$(environ ap6)
+ap5=$(environ ap5)
+ap4=$(environ ap4)
 
-for x in $mc $ap7 $ap8 $ap6; do
+for x in $mc $ap7 $ap8 $ap6 $ap5 $ap4; do
     mkdir -p $x/dt/{folder1,folder2,folder3}
     mkdir -p $x/dt/project1/{folder1,folder2,folder3}
     mkdir -p $x/dt/project2/{folder1,folder2,folder3}
@@ -18,29 +20,34 @@
     echo $x/dt/project2/{folder1,folder2,folder3}/{file1.1,file2.1}.dat | 
xargs -n 1 touch
 done
 
+$ap4/start
+$ap5/start
 $ap6/start
 $ap7/start
 $ap8/start
 
 # remove a file from ap7
 rm $ap7/dt/project1/folder2/file2.1.dat
+rm -r $ap5/dt/project1/folder2/
+rm -r $ap4/dt/project1/
 
 $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','de','eu'"
+$mc/sql "insert into server(hostname,urldir,enabled,country,region) select 
'$($ap5/print_address)','','t','cn','as'"
+$mc/sql "insert into server(hostname,urldir,enabled,country,region) select 
'$($ap4/print_address)','','t','jp','as'"
 
 $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"]'
+$mc/backstage/job -e mirror_scan -a '["/project1/folder1"]'
 $mc/backstage/shoot
 
 
 $mc/curl -s /rest/project/proj1
 $mc/curl -s /rest/project/proj1/mirror_summary
-$mc/curl -s /rest/project/proj1/mirror_summary | grep -E '"current":"?3' | 
grep -E '"outdated":"?0'
-
+$mc/curl -s /rest/project/proj1/mirror_summary | grep -E '"current":"?4' | 
grep -E '"outdated":"?0'
 
 $mc/backstage/job -e folder_sync -a '["/project1/folder2"]'
 $mc/backstage/job -e mirror_scan -a '["/project1/folder2"]'
@@ -53,10 +60,16 @@
 $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"}]'
+rc=0
+$mc/curl -s /rest/repmirror  | grep -F 
'"country":"jp","proj1score":"0","proj1victim":"","proj2score":"100","proj2victim":"","region":"as","url":"'$($ap4/print_address)'"'
 || rc=$?
+echo proj1 is not on ap4, so it shouldnt appear in repmirror at all
+test $rc -gt 0
+
+$mc/curl -s /rest/repmirror  | grep -F 
'{"country":"cn","proj1score":"50","proj1victim":"","proj2score":"100","proj2victim":"","region":"as","url":"127.0.0.1:1284"},{"country":"jp","proj2score":"100","proj2victim":"","region":"as","url":"127.0.0.1:1274"},{"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_summary | grep -E '"current":"?2' | 
grep -E '"outdated":"?2'
+
+$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":"?4"?,"url":"127.0.0.1:1284"},{"current":"?0"?,"server_id":"?2"?,"url":"127.0.0.1:1304"}'
 
-# $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"}'
+echo success
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/MirrorCache-1.035/t/environ/16-rescan-no-country.sh 
new/MirrorCache-1.037/t/environ/16-rescan-no-country.sh
--- old/MirrorCache-1.035/t/environ/16-rescan-no-country.sh     1970-01-01 
01:00:00.000000000 +0100
+++ new/MirrorCache-1.037/t/environ/16-rescan-no-country.sh     2022-08-11 
15:29:02.000000000 +0200
@@ -0,0 +1,118 @@
+#!lib/test-in-container-environ.sh
+set -ex
+
+# This test is identical to 16-rescan, just it has hit in another country 
instead of miss
+
+mc=$(environ mc $(pwd))
+MIRRORCACHE_SCHEDULE_RETRY_INTERVAL=0
+
+MIRRORCACHE_RESCAN_INTERVAL=$((7 * 24 * 60 * 60)) # set one week to avoid 
automatic rescan
+
+$mc/gen_env 
MIRRORCACHE_SCHEDULE_RETRY_INTERVAL=$MIRRORCACHE_SCHEDULE_RETRY_INTERVAL \
+            MIRRORCACHE_RESCAN_INTERVAL=$MIRRORCACHE_RESCAN_INTERVAL \
+            MIRRORCACHE_RECKLESS=0
+
+$mc/start
+$mc/status
+
+ap8=$(environ ap8)
+$ap8/start
+$ap8/status
+
+for x in $mc $ap8; do
+    mkdir -p $x/dt/folder{1,2,3,4,5,6}
+    echo $x/dt/folder{2,3,4,5,6}/file1.1.dat | xargs -n 1 touch
+done
+
+for x in {1,2,3,4,5,6} ; do
+    $mc/backstage/job -e folder_sync -a '["/folder'$x'"]'
+done
+# folder1 must have some file, so scan is triggered
+touch $mc/dt/folder1/x
+
+$mc/sql "insert into server(hostname,urldir,enabled,country,region) select 
'$($ap8/print_address)','','t','de','eu'"
+
+$mc/backstage/shoot
+$mc/sql_test 6 == "select count(*) from minion_jobs where task='folder_sync'"
+$mc/backstage/job mirror_scan_schedule
+$mc/backstage/shoot
+$mc/sql_test 6 == "select count(*) from minion_jobs where task='mirror_scan'"
+
+# handling of misses:
+# if file is unknown - schedule resync unless it was performed up to 15 min ago
+# if file is earlier than 1h  - schedule rescan unless it was performed up to 
30 min ago
+# if file is earlier than 4h  - schedule rescan unless it was performed up to 
1 hour ago
+# if file is earlier than 24h - schedule rescan unless it was performed up to 
4 hours ago
+# if file is earlier than 72h - schedule rescan unless it was performed up to 
8 hours ago
+# if file is older than 72 hours - schedule rescan unless it was performed up 
to 1 24 hours ago
+
+S=10
+S1=$((60-$S))
+$mc/sql "update file set dt = now() - interval '1 hour' where folder_id in 
(select id from folder where path = '/folder2')"
+$mc/sql "update file set dt = now() - interval '4 hour' where folder_id in 
(select id from folder where path = '/folder3')"
+$mc/sql "update file set dt = now() - interval '24 hour' where folder_id in 
(select id from folder where path = '/folder4')"
+$mc/sql "update file set dt = now() - interval '72 hour' where folder_id in 
(select id from folder where path = '/folder5')"
+
+# should cover both Pg and MariaDB
+$mc/sql "update file set dt = dt + 2* interval '$S second'" || \
+  $mc/sql "update file set dt = date_add(dt, interval 2*$S second)"
+
+$mc/sql "update file set dt = dt - interval '72 hour' where folder_id in 
(select id from folder where path = '/folder6')"
+
+touch $mc/dt/folder1/file1.1.dat
+cp $mc/dt/folder1/file1.1.dat $ap8/dt/folder1/file1.1.dat
+
+$mc/sql "update folder set scan_last = now() - interval '14 minute $S1 second' 
where path = '/folder1'"
+$mc/sql "update folder set scan_last = now() - interval '29 minute $S1 second' 
where path = '/folder2'"
+$mc/sql "update folder set scan_last = now() - interval '59 minute $S1 second' 
where path = '/folder3'"
+$mc/sql "update folder set scan_last = now() - interval '3 hour 59 minute $S1 
second' where path = '/folder4'"
+$mc/sql "update folder set scan_last = now() - interval '7 hour 59 minute $S1 
second' where path = '/folder5'"
+$mc/sql "update folder set scan_last = now() - interval '24 hour' where path = 
'/folder6'"
+
+$mc/sql "update folder set scan_requested = scan_last - interval '2 second', 
scan_scheduled = scan_last - interval '1 second'"
+$mc/sql 'select * from folder'
+
+
+$mc/curl -I /download/folder1/file1.1.dat | grep '200 OK'
+
+for x in {2,3,4,5,6} ; do
+    $mc/curl -IL /download/folder$x/file1.1.dat | grep -A20 '302 Found' | grep 
'200 OK'
+done
+
+$mc/backstage/job folder_sync_schedule
+$mc/backstage/shoot
+$mc/backstage/job mirror_scan_schedule
+$mc/backstage/shoot
+
+echo folder1 was resynced because it got new file
+$mc/sql_test 7 == "select count(*) from minion_jobs where task='folder_sync'"
+echo still nothing was scheduled, except folder1 which got new files and 
folder6 which reaches max refresh time 24 hours
+$mc/sql_test 8 == "select count(*) from minion_jobs where task='mirror_scan'"
+
+sleep $S
+# c/sql "update folder set scan_last = scan_last + interval '$S second'"
+$mc/backstage/job folder_sync_schedule
+$mc/backstage/job mirror_scan_schedule
+$mc/backstage/shoot
+
+# no new jobs were submitted yet
+$mc/sql_test 7 == "select count(*) from minion_jobs where task='folder_sync'"
+$mc/sql_test 8 == "select count(*) from minion_jobs where task='mirror_scan'"
+
+for x in {1,2,3,4,5,6} ; do
+    $mc/curl -IL /download/folder$x/file1.1.dat | grep -A20 '302 Found' | grep 
'200 OK'
+done
+
+$mc/backstage/job mirror_scan_schedule
+$mc/backstage/shoot
+
+# Now all fodlers are scanned twice
+$mc/sql_test 12 == "select count(*) from minion_jobs where task='mirror_scan'"
+$mc/sql_test 7 == "select count(*) from minion_jobs where task='folder_sync'"
+
+echo only first request was miss
+$mc/sql_test 1 == "select count(*) from stat where mirror_id < 1"
+echo all other requests are hits for the mirror
+$mc/sql_test 11 == "select count(*) from stat where mirror_id = 1"
+
+echo success
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/MirrorCache-1.035/t/environ/16-rescan.sh 
new/MirrorCache-1.037/t/environ/16-rescan.sh
--- old/MirrorCache-1.035/t/environ/16-rescan.sh        2022-06-23 
06:47:51.000000000 +0200
+++ new/MirrorCache-1.037/t/environ/16-rescan.sh        2022-08-11 
15:29:02.000000000 +0200
@@ -71,9 +71,9 @@
 $mc/backstage/job mirror_scan_schedule
 $mc/backstage/shoot
 
-# folder1 was resynced because it got new file
+echo folder1 was resynced because it got new file
 $mc/sql_test 7 == "select count(*) from minion_jobs where task='folder_sync'"
-# still nothing was scheduled, except folder1 which got new files and folder6 
which reaches max refresh time 24 hours
+echo still nothing was scheduled, except folder1 which got new files and 
folder6 which reaches max refresh time 24 hours
 $mc/sql_test 8 == "select count(*) from minion_jobs where task='mirror_scan'"
 
 sleep $S
@@ -96,3 +96,5 @@
 # Now all fodlers are scanned twice
 $mc/sql_test 12 == "select count(*) from minion_jobs where task='mirror_scan'"
 $mc/sql_test 7 == "select count(*) from minion_jobs where task='folder_sync'"
+
+echo success
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/MirrorCache-1.035/templates/app/folder/show.html.ep 
new/MirrorCache-1.037/templates/app/folder/show.html.ep
--- old/MirrorCache-1.035/templates/app/folder/show.html.ep     2022-06-23 
06:47:51.000000000 +0200
+++ new/MirrorCache-1.037/templates/app/folder/show.html.ep     2022-08-11 
15:29:02.000000000 +0200
@@ -60,7 +60,7 @@
             <div><span>Path: </span><%= $folder->{path} %></div>
             <div><span>Last sync: </span><%= $folder->{sync_last} %></div>
             <div><span>Sync Scheduled: </span><%= $folder->{sync_scheduled} 
%></div>
-            <div><span>Sync Requested: </span><%= $folder->{sync_requested} 
%></div>            
+            <div><span>Sync Requested: </span><%= $folder->{sync_requested} 
%></div>
         </div>
     </div>
 </div>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/MirrorCache-1.035/templates/branding/openSUSE/footer.html.ep 
new/MirrorCache-1.037/templates/branding/openSUSE/footer.html.ep
--- old/MirrorCache-1.035/templates/branding/openSUSE/footer.html.ep    
2022-06-23 06:47:51.000000000 +0200
+++ new/MirrorCache-1.037/templates/branding/openSUSE/footer.html.ep    
2022-08-11 15:29:02.000000000 +0200
@@ -2,7 +2,7 @@
   <div class="container">
     <div class="d-flex justify-content-between">
       <div class="footer-copyright">
-        &copy; 2021 SUSE LLC., openSUSE contributors
+        &copy; 2021,2022 SUSE LLC., openSUSE contributors
       </div>
       <div class="list-inline">
         <a class="list-inline-item" 
href="https://en.opensuse.org/Imprint";>Legal notice</a>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/MirrorCache-1.035/templates/branding/openSUSE/header.html.ep 
new/MirrorCache-1.037/templates/branding/openSUSE/header.html.ep
--- old/MirrorCache-1.035/templates/branding/openSUSE/header.html.ep    
2022-06-23 06:47:51.000000000 +0200
+++ new/MirrorCache-1.037/templates/branding/openSUSE/header.html.ep    
2022-08-11 15:29:02.000000000 +0200
@@ -58,6 +58,22 @@
           % }
         </div>
       </li>
+      <li class="nav-item dropdown" id="reports">
+      <a href="#" class="nav-link dropdown-toggle" data-toggle="dropdown" 
role="button" aria-haspopup="true" aria-expanded="false" 
data-submenu>Reports<span class="caret"></span></a>
+        <ul class="dropdown-menu">
+          <li>
+          %= link_to 'All Mirrors'  => url_for('/report/mirrors') => class => 
'dropdown-item'
+          </li><li>
+          %= link_to '15.4 Mirrors'  => url_for('/report/mirrors/154') => 
class => 'dropdown-item'
+          </li><li>
+          %= link_to '15.3 Mirrors'  => url_for('/report/mirrors/153') => 
class => 'dropdown-item'
+          </li><li>
+          %= link_to 'TW Mirrors'  => url_for('/report/mirrors/tumbleweed') => 
class => 'dropdown-item'
+          </li><li>
+          %= link_to 'Build Service Mirrors'  => 
url_for('/report/mirrors/repositories') => class => 'dropdown-item'
+          </li>
+        </ul>
+      </li>
     </ul>
 
   </div>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/MirrorCache-1.035/templates/branding/openSUSE/report_mirrors_banner.html.ep 
new/MirrorCache-1.037/templates/branding/openSUSE/report_mirrors_banner.html.ep
--- 
old/MirrorCache-1.035/templates/branding/openSUSE/report_mirrors_banner.html.ep 
    1970-01-01 01:00:00.000000000 +0100
+++ 
new/MirrorCache-1.037/templates/branding/openSUSE/report_mirrors_banner.html.ep 
    2022-08-11 15:29:02.000000000 +0200
@@ -0,0 +1,32 @@
+<div class="report-mirrors-banner">
+
+      <p class="alert alert-danger">
+        The information on this page is generated directly off the
+        database used by the
+        <a href="http://download.opensuse.org/";>master download server</a>
+        every 30 minutes.
+        Under normal circumstances, there <em>should</em> be no need to use
+        mirrors directly, and look at this table for that purpose.
+        If you <em>do</em> see the need, there might be something wrong
+        and it would be appreciated if you notify the admins about it,
+        so it can be fixed for the benefit of yourself, and possibly
+        many other users.
+      </p>
+
+      <p class="alert alert-warning">
+        This page lists mirror servers where you can download openSUSE.
+        For specific distributions, we recommend <a 
href="http://get.opensuse.org/";>get.opensuse.org</a>
+        to help with choosing the correct download, which will automatically 
choose the best mirror.
+        The report uses heuristic scoring system to show how outdated the 
mirrors are. Scoring is done for tree of folders,
+       so a mirror with a bad score can still have recent files in some 
locations and may be useful.
+       Meaning of colors:
+        <span style="color:green">up to date</span>, <span 
style="color:olive">quite up to date</span>, <span style="color:grey">might be 
outdated</span>, <span style="color:red">outdated</span>.
+       The state is also dynamic, so the score and the colors may change on 
next refresh.
+      </p>
+
+      <p class="alert alert-info">
+        If you have a server with some space left, and want to help with 
making the openSUSE experience better for other users,
+        <a href="https://en.opensuse.org/openSUSE:Mirror_howto";>become a 
mirror</a>!
+      </p>
+
+</div>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/MirrorCache-1.035/templates/report/mirrors/index.html.ep 
new/MirrorCache-1.037/templates/report/mirrors/index.html.ep
--- old/MirrorCache-1.035/templates/report/mirrors/index.html.ep        
1970-01-01 01:00:00.000000000 +0100
+++ new/MirrorCache-1.037/templates/report/mirrors/index.html.ep        
2022-08-11 15:29:02.000000000 +0200
@@ -0,0 +1,162 @@
+% layout 'bootstrap';
+
+% my $first_old = '';
+% my $first;
+% for my $project (@$projects) {
+%     my $name   = $project->{name};
+%     ($first) = split ' ', $name, 2;
+%     if ($first_old && $first_old ne $first) {
+%         $first = '';
+%         last;
+%     }
+%     $first_old = $first;
+% }
+% if ($first) {
+%     title 'Mirrors Report for ' . $first;
+% } else {
+%     title 'Mirrors Report';
+% }
+
+% content_for 'head' => begin
+
+<style type='text/css'>
+
+a.repoperfect {
+    color: green
+}
+
+a.repogood {
+    color: olive
+}
+
+a.repouncertain {
+    color: grey
+}
+
+a.repooutdated {
+    color: red
+}
+
+div.repo {
+    float: left;
+    margin-right: 2px;
+}
+
+tr:nth-child(even) {
+    background-color: #88888822;
+}
+
+tr:hover td {
+    background-color: #88888840;
+}
+
+tr:nth-child(even):hover td {
+    background-color: #88888820;
+}
+
+td.newregion, tr:hover td.newregion {
+    background: #fff;
+    font-size: 1.2rem;
+    padding: 1.5rem 0.5rem;
+}
+
+@media (prefers-color-scheme: dark) {
+    td.newregion, tr:hover td.newregion {
+        background: #212529;
+    }
+    thead th {
+        background: linear-gradient(to top, #787878 0%, #363a3c 1px, #363a3c 
100%);
+    }
+    thead th:nth-child(even), thead th:nth-child(1), thead th:nth-child(3) {
+        background: linear-gradient(to top, #787878 0%, #2f3236 1px, #2f3236 
100%);
+    }
+}
+
+</style>
+% end
+
+%= include_branding 'report_mirrors_banner'
+
+<form>
+<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">Country</th>
+                    <th class="col_value">Url</th>
+                    % my $first_old = '';
+                    % for my $project (@$projects) {
+                    %     my $name   = $project->{name};
+                    %     (my $first) = split ' ', $name, 2;
+                    %     $first = "Leap $first"   if 0 == rindex $first, 
"15.", 0;
+                    %     $first = "Build Service" if $first eq 'repositories';
+                    %     $first = "Tumbleweed"    if $first eq 'TW';
+                    %     if ($first_old ne $first) {
+                    %          if ($first_old) {
+                    </th>
+                    %          }
+                    <th class="col_value"><%= $first %>
+                    %     }
+                    %     $first_old = $first;
+                    % }
+                    </th>
+                </tr>
+            </thead>
+            <tbody>
+                % my $prev_region = '';
+                % for my $m (@$mirrors) {
+                %     my $region = $m->{region};
+                %     if ($region ne $prev_region) {
+                %         my $region_name = region_name($region);
+                <tr><td colspan="24" class="newregion"><%= $region_name 
%>:</td></tr>
+                %     }
+                %     $prev_region = $region;
+                <tr>
+                %     my $url = $m->{url};
+                    <td><%= $m->{country} %></td>
+                    <td><a href="http://<%= $url %>"><%=$url%></a></td>
+                %     my $inner = '';
+                %     $first_old = '';
+                    <td>
+                %     for my $project (@$projects) {
+                %         my $name    = $project->{name};
+                %         my $alias   = $project->{alias};
+                %         my $path    = $project->{path};
+                %         my $column1 = $alias . "score";
+                %         my $column2 = $alias . "victim";
+                %         my ($first, $second) = split ' ', $name;
+                %         my $victim = $m->{$column2};
+                %         if ($first_old && $first_old ne $first) {
+                    </td><td>
+                %         }
+                %         $first_old = $first;
+                %         my $x = $m->{$column1};
+                %         if (!defined $x) {
+                %             next;
+                %         }
+                %         $second = 'repo' unless $second;
+                %         my $style = 'repooutdated';
+                %         $style = 'repouncertain'  if $x >= 30;
+                %         $style = 'repogood'       if $x >= 75;
+                %         $style = 'repoperfect'    if $x == 100;
+                        <div class="repo">
+                            <a class="<%=$style%>"
+                %         if ($victim) {
+                               title="diff in: <%=$victim%>"
+                %         }
+                %         $second = 'iso' if $second eq 'ISO' || $second eq 
'ISOs';
+                               href="http://<%=$url . $path%>">
+                                <%= $second %>
+                            </a>
+                        </div>
+                %     }
+                    </td>
+                % }
+            </tbody>
+        </table>
+    </div>
+</div>
+</form>

++++++ MirrorCache.obsinfo ++++++
--- /var/tmp/diff_new_pack.o67KlN/_old  2022-08-19 17:59:02.052421746 +0200
+++ /var/tmp/diff_new_pack.o67KlN/_new  2022-08-19 17:59:02.056421755 +0200
@@ -1,5 +1,5 @@
 name: MirrorCache
-version: 1.035
-mtime: 1655959671
-commit: 04f97244031e34226a586781f8a35cca0ec01c4a
+version: 1.037
+mtime: 1660224542
+commit: 5281a9c79509f79aec972de25f37d12b0e099b85
 

Reply via email to