Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package MirrorCache for openSUSE:Factory checked in at 2023-02-16 16:57:08 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/MirrorCache (Old) and /work/SRC/openSUSE:Factory/.MirrorCache.new.22824 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "MirrorCache" Thu Feb 16 16:57:08 2023 rev:24 rq:1066141 version:1.056 Changes: -------- --- /work/SRC/openSUSE:Factory/MirrorCache/MirrorCache.changes 2023-01-19 16:44:28.333852717 +0100 +++ /work/SRC/openSUSE:Factory/.MirrorCache.new.22824/MirrorCache.changes 2023-02-16 16:57:13.392295114 +0100 @@ -1,0 +2,33 @@ +Thu Feb 09 09:41:42 UTC 2023 - Andrii Nikitin <andrii.niki...@suse.com> + +- Update to version 1.056: + * Improve symlink handling when remote root references another remote root (#353) + * ui: add content-disposition inline to render_file (#355) + * Don't send users to Bugzilla by @AdamMajer (#357) + * Add leap 15.5 to project list (#358) + * Add GNOME iso to Current pattern (#356) + +------------------------------------------------------------------- +Thu Feb 02 12:01:48 UTC 2023 - Andrii Nikitin <andrii.niki...@suse.com> + +- Update to version 1.055: + * Handle redirects with RootRemote (#351) + * Add sha512 to hash (#352) + * Do not urlencode plus character (#352) + +------------------------------------------------------------------- +Fri Jan 27 13:16:17 UTC 2023 - Andrii Nikitin <andrii.niki...@suse.com> + +- Update to version 1.054: + * Optimise sql for erasing folder_diff_server (#347) + * Fix huge files rendered from REDIRECT_HUGE only for metalink (#348) + +------------------------------------------------------------------- +Thu Jan 26 07:23:04 UTC 2023 - Andrii Nikitin <andrii.niki...@suse.com> + +- Update to version 1.053: + * Add MIRROR_PROVIDER to share mirror list (#343) + * Fix REDIRECT_HUGE with local files (#345) + * ui: Dont encode colon character in file names (#346) + +------------------------------------------------------------------- Old: ---- MirrorCache-1.052.obscpio New: ---- MirrorCache-1.056.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ MirrorCache.spec ++++++ --- /var/tmp/diff_new_pack.6eIJvz/_old 2023-02-16 16:57:14.004297776 +0100 +++ /var/tmp/diff_new_pack.6eIJvz/_new 2023-02-16 16:57:14.008297793 +0100 @@ -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) perl(Config::IniFiles) %define build_requires %{assetpack_requires} rubygem(sass) tidy sysuser-shadow sysuser-tools Name: MirrorCache -Version: 1.052 +Version: 1.056 Release: 0 Summary: WebApp to redirect and manage mirrors License: GPL-2.0-or-later ++++++ MirrorCache-1.052.obscpio -> MirrorCache-1.056.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.052/.github/workflows/publish-docs.yml new/MirrorCache-1.056/.github/workflows/publish-docs.yml --- old/MirrorCache-1.052/.github/workflows/publish-docs.yml 2023-01-12 09:40:42.000000000 +0100 +++ new/MirrorCache-1.056/.github/workflows/publish-docs.yml 2023-02-09 09:52:35.000000000 +0100 @@ -3,6 +3,9 @@ push: branches: - master + paths: + - README.md + - docs/** jobs: build: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.052/assets/javascripts/admintable.js new/MirrorCache-1.056/assets/javascripts/admintable.js --- old/MirrorCache-1.052/assets/javascripts/admintable.js 2023-01-12 09:40:42.000000000 +0100 +++ new/MirrorCache-1.056/assets/javascripts/admintable.js 2023-02-09 09:52:35.000000000 +0100 @@ -396,7 +396,7 @@ }); // setup admin table - var url = $("#admintable_api_url").val(); + var url = $("#admintable_api_url").val() + window.location.search; var table = $('.admintable'); var dataTable = table.DataTable({ order: [ @@ -419,6 +419,58 @@ search: { regex: true, }, + fnInitComplete: function(oSettings, json) { + if (url === "/rest/server" || url == "/rest/myserver") { + // 1. aggregate regions + // 2. show panel + // 3. create elements + var servers = json.Server; + if (!servers) + servers = json.MyServer; + if (!servers) + return; + + var regions = {}; + for (const server of servers) { + var region = server.region; + if (region) { + if(!regions[region]) regions[region] = 1 + else regions[region] += 1; + } + var extra = server[ "extra regions" ]; + if (extra) { + for (const x of extra.split(",")) { + if (x && (!region || region != x)) { + if(!regions[x]) regions[x] = 1 + else regions[x] += 1; + } + } + } + } + if (Object.keys(regions).length < 2) return; + + var panel = document.getElementById("regionspanel"); + if (!panel) return; + var ul = document.getElementById("regionspanelul"); + if (!ul) return; + Object.keys(regions).forEach(function(key) { + var li = document.createElement("li"); + // li.className = 'inline-block'; + li.style.float = 'left'; + li.style.margin = '0 15px'; + var a = document.createElement("a"); + a.textContent = key + " (" + regions[key] + ")"; + if (url == "/rest/myserver") { + a.setAttribute("href", "/app/myserver?region=" + key); + } else { + a.setAttribute("href", "/app/server?region=" + key); + } + li.appendChild(a); + ul.appendChild(li); + }); + panel.style.display = "block"; + } + }, }); dataTable.rowData = []; dataTable.emptyRow = emptyRow; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.052/assets/javascripts/browse.js new/MirrorCache-1.056/assets/javascripts/browse.js --- old/MirrorCache-1.052/assets/javascripts/browse.js 2023-01-12 09:40:42.000000000 +0100 +++ new/MirrorCache-1.056/assets/javascripts/browse.js 2023-02-09 09:52:35.000000000 +0100 @@ -1,3 +1,11 @@ + +function encodeComponentExceptColon(str) { + return ( + encodeURIComponent(str) + .replace(/%(3A|2B)/g, (str, hex) => String.fromCharCode(parseInt(hex, 16))) + ); +} + function setupBrowseTable(path) { // read columns from empty HTML table rendered by the server var columns = []; @@ -19,9 +27,9 @@ } var desc = row['desc']; if (desc) { - data = '<a style="--desc: \'' + desc + '\'" href="' + path + encodeURIComponent(d) + t + '">' + data + '</a>'; + data = '<a style="--desc: \'' + desc + '\'" href="' + path + encodeComponentExceptColon(d) + t + '">' + data + '</a>'; } else { - data = '<a href="' + path + encodeURIComponent(d) + t + '">' + data + '</a>'; + data = '<a href="' + path + encodeComponentExceptColon(d) + t + '">' + data + '</a>'; } } if(type === 'sort'){ @@ -42,9 +50,9 @@ if(type === 'display' && data > 0){ data = new Date(data * 1000).toLocaleString().replace(/.\d+$/, "").replace(/:\d\d (AM|PM)$/, " $1"); if(row['name'].slice(-1) != '/') { - data = '<a href="' + path + encodeURIComponent(row['name']) + '.mirrorlist">' + data + '</a>'; + data = '<a href="' + path + encodeComponentExceptColon(row['name']) + '.mirrorlist">' + data + '</a>'; } else { - data = '<a href="' + path + encodeURIComponent(row['name'].slice(0,-1)) + '/">' + data + '</a>'; + data = '<a href="' + path + encodeComponentExceptColon(row['name'].slice(0,-1)) + '/">' + data + '</a>'; } } return data; @@ -71,7 +79,7 @@ } while (Math.round(Math.abs(data) * 10) >= 1024 && u < units.length - 1); data = data.toFixed(1) + ' ' + units[u]; } - data = '<a href="' + path + encodeURIComponent(row['name']) + '.mirrorlist">' + data + '</a>'; + data = '<a href="' + path + encodeComponentExceptColon(row['name']) + '.mirrorlist">' + data + '</a>'; } return data; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.052/dist/salt/profile/mirrorcache/files/usr/share/mirrorcache/sql/projects.sql new/MirrorCache-1.056/dist/salt/profile/mirrorcache/files/usr/share/mirrorcache/sql/projects.sql --- old/MirrorCache-1.052/dist/salt/profile/mirrorcache/files/usr/share/mirrorcache/sql/projects.sql 2023-01-12 09:40:42.000000000 +0100 +++ new/MirrorCache-1.056/dist/salt/profile/mirrorcache/files/usr/share/mirrorcache/sql/projects.sql 2023-02-09 09:52:35.000000000 +0100 @@ -5,22 +5,24 @@ insert into project(name,path) select 'TW source','/source/tumbleweed/repo'; insert into project(name,path) select 'TW history','/history'; -insert into project(name,path) select '15.2 ISO','/distribution/leap/15.2/iso'; -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'; - 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'; +insert into project(name,path) select '15.3 update','/update/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'; +insert into project(name,path) select '15.4 update','/update/leap/15.4'; insert into project(name,path) select '15.4 port','/ports/aarch64/distribution/leap/15.4'; + +insert into project(name,path) select '15.5 ISO','/distribution/leap/15.5/iso'; +insert into project(name,path) select '15.5 repo','/distribution/leap/15.5/repo'; +insert into project(name,path) select '15.5 debug','/debug/distribution/leap/15.5/repo'; +insert into project(name,path) select '15.5 source','/source/distribution/leap/15.5/repo'; +insert into project(name,path) select '15.5 update','/update/leap/15.5'; +insert into project(name,path) select '15.5 port','/ports/aarch64/distribution/leap/15.5'; + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.052/lib/MirrorCache/Config.pm new/MirrorCache-1.056/lib/MirrorCache/Config.pm --- old/MirrorCache-1.052/lib/MirrorCache/Config.pm 2023-01-12 09:40:42.000000000 +0100 +++ new/MirrorCache-1.056/lib/MirrorCache/Config.pm 2023-02-09 09:52:35.000000000 +0100 @@ -39,6 +39,8 @@ has plugin_status => $ENV{MIRRORCACHE_PLUGIN_STATUS}; +has mirror_provider => $ENV{MIRRORCACHE_MIRROR_PROVIDER}; + has db_provider => undef; has custom_footer_message => $ENV{MIRRORCACHE_CUSTOM_FOOTER_MESSAGE}; @@ -50,7 +52,7 @@ my $cfg; $cfg = Config::IniFiles->new(-file => $cfgfile, -fallback => 'default') if $cfgfile; if ($cfg) { - for my $k (qw/root redirect browser_agent_mask/) { + for my $k (qw/root redirect mirror_provider browser_agent_mask/) { if (my $v = $cfg->val('default', $k)) { $self->$k($v); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.052/lib/MirrorCache/Datamodule.pm new/MirrorCache-1.056/lib/MirrorCache/Datamodule.pm --- old/MirrorCache-1.052/lib/MirrorCache/Datamodule.pm 2023-01-12 09:40:42.000000000 +0100 +++ new/MirrorCache-1.056/lib/MirrorCache/Datamodule.pm 2023-02-09 09:52:35.000000000 +0100 @@ -46,6 +46,7 @@ has [ 'torrent', 'magnet', 'btih' ]; has [ 'json', 'jsontable' ]; has [ 'folder_id', 'file_id', 'file_age', 'folder_sync_last', 'folder_scan_last' ]; # shortcut to requested folder and file, if known +has [ 'real_folder_id' ]; has root_country => ($ENV{MIRRORCACHE_ROOT_COUNTRY} ? lc($ENV{MIRRORCACHE_ROOT_COUNTRY}) : ""); has '_root_region'; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.052/lib/MirrorCache/Schema/Result/Hash.pm new/MirrorCache-1.056/lib/MirrorCache/Schema/Result/Hash.pm --- old/MirrorCache-1.052/lib/MirrorCache/Schema/Result/Hash.pm 2023-01-12 09:40:42.000000000 +0100 +++ new/MirrorCache-1.056/lib/MirrorCache/Schema/Result/Hash.pm 2023-02-09 09:52:35.000000000 +0100 @@ -26,6 +26,8 @@ { data_type => "char", size => 40 }, "sha256", { data_type => "char", size => 64 }, + "sha512", + { data_type => "char", size => 128 }, "piece_size", { data_type => "int" }, "pieces", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.052/lib/MirrorCache/Schema/Result/MyServer.pm new/MirrorCache-1.056/lib/MirrorCache/Schema/Result/MyServer.pm --- old/MirrorCache-1.052/lib/MirrorCache/Schema/Result/MyServer.pm 2023-01-12 09:40:42.000000000 +0100 +++ new/MirrorCache-1.056/lib/MirrorCache/Schema/Result/MyServer.pm 2023-02-09 09:52:35.000000000 +0100 @@ -44,6 +44,11 @@ __PACKAGE__->set_primary_key("id"); -# __PACKAGE__->has_many('id' => 'MirrorCache::Schema::Result::ServerAdmin'); +__PACKAGE__->has_many( + "server_capability_declaration", + "MirrorCache::Schema::Result::ServerCapabilityDeclaration", + { "foreign.server_id" => "self.id" }, + { cascade_copy => 0, cascade_delete => 0, join_type => 'left' }, +); 1; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.052/lib/MirrorCache/Schema/Result/Redirect.pm new/MirrorCache-1.056/lib/MirrorCache/Schema/Result/Redirect.pm --- old/MirrorCache-1.052/lib/MirrorCache/Schema/Result/Redirect.pm 2023-01-12 09:40:42.000000000 +0100 +++ new/MirrorCache-1.056/lib/MirrorCache/Schema/Result/Redirect.pm 2023-02-09 09:52:35.000000000 +0100 @@ -18,7 +18,7 @@ =cut -__PACKAGE__->table("folder"); +__PACKAGE__->table("redirect"); =head1 ACCESSORS diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.052/lib/MirrorCache/Schema/Result/Server.pm new/MirrorCache-1.056/lib/MirrorCache/Schema/Result/Server.pm --- old/MirrorCache-1.052/lib/MirrorCache/Schema/Result/Server.pm 2023-01-12 09:40:42.000000000 +0100 +++ new/MirrorCache-1.056/lib/MirrorCache/Schema/Result/Server.pm 2023-02-09 09:52:35.000000000 +0100 @@ -58,4 +58,12 @@ { cascade_copy => 0, cascade_delete => 0 }, ); + +__PACKAGE__->has_many( + "server_capability_declaration", + "MirrorCache::Schema::Result::ServerCapabilityDeclaration", + { "foreign.server_id" => "self.id" }, + { cascade_copy => 0, cascade_delete => 0, join_type => 'left' }, +); + 1; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.052/lib/MirrorCache/Schema/Result/ServerCapabilityDeclaration.pm new/MirrorCache-1.056/lib/MirrorCache/Schema/Result/ServerCapabilityDeclaration.pm --- old/MirrorCache-1.052/lib/MirrorCache/Schema/Result/ServerCapabilityDeclaration.pm 2023-01-12 09:40:42.000000000 +0100 +++ new/MirrorCache-1.056/lib/MirrorCache/Schema/Result/ServerCapabilityDeclaration.pm 2023-02-09 09:52:35.000000000 +0100 @@ -40,7 +40,7 @@ "prefix", ], }, - is_nullable => 1, + is_nullable => 0, }, enabled => { data_type => 'integer', @@ -52,4 +52,6 @@ { data_type => "varchar", is_nullable => 1, size => 256 }, ); +__PACKAGE__->set_primary_key("server_id", "capability"); + 1; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.052/lib/MirrorCache/Schema/ResultSet/File.pm new/MirrorCache-1.056/lib/MirrorCache/Schema/ResultSet/File.pm --- old/MirrorCache-1.052/lib/MirrorCache/Schema/ResultSet/File.pm 2023-01-12 09:40:42.000000000 +0100 +++ new/MirrorCache-1.056/lib/MirrorCache/Schema/ResultSet/File.pm 2023-02-09 09:52:35.000000000 +0100 @@ -96,7 +96,7 @@ case when coalesce(file.size, 0::bigint) = 0::bigint and coalesce(hash.size, 0::bigint) != 0::bigint then hash.size else file.size end size, case when coalesce(file.mtime, 0::bigint) = 0::bigint and coalesce(hash.mtime, 0::bigint) != 0::bigint then hash.mtime else file.mtime end mtime, coalesce(hash.target, file.target) target, -file.dt, hash.md5, hash.sha1, hash.sha256, hash.piece_size, hash.pieces, +file.dt, hash.md5, hash.sha1, hash.sha256, hash.sha512, hash.piece_size, hash.pieces, (DATE_PART('day', now() - file.dt) * 24 * 3600 + DATE_PART('hour', now() - file.dt) * 3600 + DATE_PART('minute', now() - file.dt) * 60 + @@ -119,7 +119,7 @@ case when coalesce(file.size, 0) = 0 and coalesce(hash.size, 0) != 0 then hash.size else file.size end size, case when coalesce(file.mtime, 0) = 0 and coalesce(hash.mtime, 0) != 0 then hash.mtime else file.mtime end mtime, coalesce(hash.target, file.target) target, -file.dt, hash.md5, hash.sha1, hash.sha256, hash.piece_size, hash.pieces, +file.dt, hash.md5, hash.sha1, hash.sha256, hash.sha512, hash.piece_size, hash.pieces, TIMESTAMPDIFF(SECOND, file.dt, CURRENT_TIMESTAMP(3)) as age from file left join hash on file_id = id and @@ -172,7 +172,7 @@ case when coalesce(file.size, 0::bigint) = 0::bigint and coalesce(hash.size, 0::bigint) != 0::bigint then hash.size else file.size end size, case when coalesce(file.mtime, 0::bigint) = 0::bigint and coalesce(hash.mtime, 0::bigint) != 0::bigint then hash.mtime else file.mtime end mtime, coalesce(hash.target, file.target) target, -file.dt, hash.md5, hash.sha1, hash.sha256, hash.piece_size, hash.pieces, +file.dt, hash.md5, hash.sha1, hash.sha256, hash.sha512, hash.piece_size, hash.pieces, hash.zlengths, hash.zblock_size, hash.zhashes, (DATE_PART('day', now() - file.dt) * 24 * 3600 + DATE_PART('hour', now() - file.dt) * 3600 + @@ -194,7 +194,7 @@ case when coalesce(file.size, 0) = 0 and coalesce(hash.size, 0) != 0 then hash.size else file.size end size, case when coalesce(file.mtime, 0) = 0 and coalesce(hash.mtime, 0) != 0 then hash.mtime else file.mtime end mtime, coalesce(hash.target, file.target) target, -file.dt, hash.md5, hash.sha1, hash.sha256, hash.piece_size, hash.pieces, +file.dt, hash.md5, hash.sha1, hash.sha256, hash.sha512, hash.piece_size, hash.pieces, hash.zlengths, hash.zblock_size, hash.zhashes, TIMESTAMPDIFF(SECOND, file.dt, CURRENT_TIMESTAMP(3)) as age from file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.052/lib/MirrorCache/Schema/ResultSet/Folder.pm new/MirrorCache-1.056/lib/MirrorCache/Schema/ResultSet/Folder.pm --- old/MirrorCache-1.052/lib/MirrorCache/Schema/ResultSet/Folder.pm 2023-01-12 09:40:42.000000000 +0100 +++ new/MirrorCache-1.056/lib/MirrorCache/Schema/ResultSet/Folder.pm 2023-02-09 09:52:35.000000000 +0100 @@ -1,4 +1,4 @@ -# Copyright (C) 2020 SUSE LLC +# Copyright (C) 2020-2023 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 @@ -171,13 +171,14 @@ my $dbh = $schema->storage->dbh; my $sql = <<'END_SQL'; -select id, sync_last, scan_last, '' as pathto +select id, sync_last, scan_last, '' as pathto, path from folder where path = ? union -select id, NULL, NULL, pathto -from redirect +select folder.id, sync_last, scan_last, pathto, path +from redirect left join folder on pathto = path where pathfrom = ? +order by pathto desc limit 1 END_SQL my $prep = $dbh->prepare($sql); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.052/lib/MirrorCache/Schema/ResultSet/Hash.pm new/MirrorCache-1.056/lib/MirrorCache/Schema/ResultSet/Hash.pm --- old/MirrorCache-1.052/lib/MirrorCache/Schema/ResultSet/Hash.pm 2023-01-12 09:40:42.000000000 +0100 +++ new/MirrorCache-1.056/lib/MirrorCache/Schema/ResultSet/Hash.pm 2023-02-09 09:52:35.000000000 +0100 @@ -22,7 +22,7 @@ use DBI qw(:sql_types); sub store { - my ($self, $file_id, $mtime, $size, $md5hex, $sha1hex, $sha256hex, $block_size, $pieceshex, $zlengths, $zblock_size, $zhashes, $target) = @_; + my ($self, $file_id, $mtime, $size, $md5hex, $sha1hex, $sha256hex, $sha512hex, $block_size, $pieceshex, $zlengths, $zblock_size, $zhashes, $target) = @_; my $rsource = $self->result_source; my $schema = $rsource->schema; @@ -31,14 +31,15 @@ my $sql; if ($dbh->{Driver}->{Name} eq 'Pg') { $sql = <<'END_SQL'; -insert into hash(file_id, mtime, size, md5, sha1, sha256, piece_size, pieces, zlengths, zblock_size, zhashes, target, dt) -values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, now()) +insert into hash(file_id, mtime, size, md5, sha1, sha256, sha512, piece_size, pieces, zlengths, zblock_size, zhashes, target, dt) +values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, now()) ON CONFLICT (file_id) DO UPDATE SET size = excluded.size, mtime = excluded.mtime, md5 = excluded.md5, sha1 = excluded.sha1, sha256 = excluded.sha256, + sha512 = excluded.sha512, piece_size = excluded.piece_size, pieces = excluded.pieces, zlengths = excluded.zlengths, @@ -49,14 +50,15 @@ END_SQL } else { $sql = <<'END_SQL'; -insert into hash(file_id, mtime, size, md5, sha1, sha256, piece_size, pieces, zlengths, zblock_size, zhashes, target, dt) -values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP(3)) +insert into hash(file_id, mtime, size, md5, sha1, sha256, sha512, piece_size, pieces, zlengths, zblock_size, zhashes, target, dt) +values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP(3)) ON DUPLICATE KEY UPDATE size = values(size), mtime = values(mtime), md5 = values(md5), sha1 = values(sha1), sha256 = values(sha256), + sha512 = values(sha512), piece_size = values(piece_size), pieces = values(pieces), zlengths = values(zlengths), @@ -73,12 +75,13 @@ $prep->bind_param( 4, $md5hex, SQL_CHAR); $prep->bind_param( 5, $sha1hex, SQL_CHAR); $prep->bind_param( 6, $sha256hex, SQL_CHAR); - $prep->bind_param( 7, $block_size, SQL_INTEGER); - $prep->bind_param( 8, $pieceshex, SQL_VARCHAR); - $prep->bind_param( 9, $zlengths, SQL_VARCHAR); - $prep->bind_param(10, $zblock_size, SQL_INTEGER); - $prep->bind_param(11, $zhashes, SQL_VARBINARY); # we must force varbinary, otherwise driver will corrupt hashes trying to handle unicode - $prep->bind_param(12, $target, SQL_VARCHAR); + $prep->bind_param( 7, $sha512hex, SQL_CHAR); + $prep->bind_param( 8, $block_size, SQL_INTEGER); + $prep->bind_param( 9, $pieceshex, SQL_VARCHAR); + $prep->bind_param(10, $zlengths, SQL_VARCHAR); + $prep->bind_param(11, $zblock_size, SQL_INTEGER); + $prep->bind_param(12, $zhashes, SQL_VARBINARY); # we must force varbinary, otherwise driver will corrupt hashes trying to handle unicode + $prep->bind_param(13, $target, SQL_VARCHAR); $prep->execute(); } @@ -97,7 +100,7 @@ } my $sql = <<"END_SQL"; -select file.name, hash.mtime, hash.size, md5, sha1, sha256, piece_size, pieces, hash.target, hash.dt +select file.name, hash.mtime, hash.size, md5, sha1, sha256, sha512, piece_size, pieces, hash.target, hash.dt from hash left join file on file_id = id where file_id in ( select id from file where folder_id = ? ) $time_constraint_condition limit 100000 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.052/lib/MirrorCache/Schema/ResultSet/Server.pm new/MirrorCache-1.056/lib/MirrorCache/Schema/ResultSet/Server.pm --- old/MirrorCache-1.052/lib/MirrorCache/Schema/ResultSet/Server.pm 2023-01-12 09:40:42.000000000 +0100 +++ new/MirrorCache-1.056/lib/MirrorCache/Schema/ResultSet/Server.pm 2023-02-09 09:52:35.000000000 +0100 @@ -323,4 +323,34 @@ return $server_arrayref; } +sub check_sync { + my ($self, $h) = @_; + + my $rsource = $self->result_source; + my $schema = $rsource->schema; + my $dbh = $schema->storage->dbh; + + my $sql = 'select * from server where id = ?'; + my $exist = $dbh->selectrow_hashref($sql, undef, $h->{id}); + + unless ($exist) { + $sql = 'insert into server(id, hostname, urldir, enabled, region, country, score, lat, lng) select ?, ?, ?, ?, ?, ?, ?, ?, ?'; + $dbh->prepare($sql)->execute($h->{id}, $h->{hostname}, $h->{urldir}, $h->{enabled}, $h->{region}, $h->{country}, $h->{score}, $h->{lat}, $h->{lng}); + return 2; + } + + return 0 if $exist->{hostname} ne $h->{hostname}; + + my $eq = 1; + for my $key (qw(urldir enabled region country score lat lng)) { + next if ($exist->{$key} // '') eq ($h->{$key} // ''); + $eq = 0; + last; + } + return 1 if $eq; + $sql = 'update server set urldir = ?, enabled = ?, region = ?, country = ?, score = ?, lat = ?, lng = ? where id = ? and hostname = ?'; + $dbh->prepare($sql)->execute($h->{urldir}, $h->{enabled}, $h->{region}, $h->{country}, $h->{score}, $h->{lat}, $h->{lng}, $h->{id}, $h->{hostname}); + return 3; +} + 1; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.052/lib/MirrorCache/Task/Cleanup.pm new/MirrorCache-1.056/lib/MirrorCache/Task/Cleanup.pm --- old/MirrorCache-1.052/lib/MirrorCache/Task/Cleanup.pm 2023-01-12 09:40:42.000000000 +0100 +++ new/MirrorCache-1.056/lib/MirrorCache/Task/Cleanup.pm 2023-02-09 09:52:35.000000000 +0100 @@ -38,11 +38,11 @@ my $sql; if ($schema->pg) { $sql = <<'END_SQL'; -update minion_jobs set state = 'failed', result = concat("killed; old result: ", result) where state = 'active' and started < now() - interval '1 hour' +update minion_jobs set state = 'failed', result = '"killed"' where state = 'active' and started < now() - interval '1 hour' END_SQL } else { $sql = <<'END_SQL'; -update minion_jobs set state = 'failed', result = concat_ws("", "killed; old result: ", result) where state = 'active' and started < now() - interval 2 hour +update minion_jobs set state = 'failed', result = '"killed"' where state = 'active' and started < now() - interval 1 hour END_SQL } eval { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.052/lib/MirrorCache/Task/FolderHashesCreate.pm new/MirrorCache-1.056/lib/MirrorCache/Task/FolderHashesCreate.pm --- old/MirrorCache-1.052/lib/MirrorCache/Task/FolderHashesCreate.pm 2023-01-12 09:40:42.000000000 +0100 +++ new/MirrorCache-1.056/lib/MirrorCache/Task/FolderHashesCreate.pm 2023-02-09 09:52:35.000000000 +0100 @@ -91,13 +91,14 @@ $target = $dest->basename if $dest->dirname eq '.' || $dest->dirname eq "$indir$path"; } }; - return $schema->resultset('Hash')->store($file_id, undef, 0, undef, undef, undef, undef, undef, undef, undef, undef, $target) if $target; + return $schema->resultset('Hash')->store($file_id, undef, 0, undef, undef, undef, undef, undef, undef, undef, undef, undef, $target) if $target; open my $fh, "<", $f or die "Unable to open $f!"; my $stat = stat($fh); my $mtime = $stat->mtime; my $d1 = Digest::SHA->new(1); my $d256 = Digest::SHA->new(256); + my $d512 = Digest::SHA->new(512); my $dmd5 = Digest::MD5->new; my @pieces; my $data; @@ -117,6 +118,7 @@ $d1->add($data); $d256->add($data); + $d512->add($data); $dmd5->add($data); } close $fh; @@ -127,7 +129,7 @@ $zsync_block_size = $zsync->block_size; $zsync_hashes = $zsync->digest; } - $schema->resultset('Hash')->store($file_id, $mtime, $size, $dmd5->hexdigest, $d1->hexdigest, $d256->hexdigest, $block_size, $pieceshex, $zsync_lengths, $zsync_block_size, $zsync_hashes); + $schema->resultset('Hash')->store($file_id, $mtime, $size, $dmd5->hexdigest, $d1->hexdigest, $d256->hexdigest, $d512->hexdigest, $block_size, $pieceshex, $zsync_lengths, $zsync_block_size, $zsync_hashes); } 1; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.052/lib/MirrorCache/Task/FolderHashesImport.pm new/MirrorCache-1.056/lib/MirrorCache/Task/FolderHashesImport.pm --- old/MirrorCache-1.052/lib/MirrorCache/Task/FolderHashesImport.pm 2023-01-12 09:40:42.000000000 +0100 +++ new/MirrorCache-1.056/lib/MirrorCache/Task/FolderHashesImport.pm 2023-02-09 09:52:35.000000000 +0100 @@ -73,7 +73,7 @@ eval { $rsHash = $schema->resultset('Hash') unless $rsHash; $rsHash->store($file->id, $hash->{mtime}, $hash->{size}, $hash->{md5}, - $hash->{sha1}, $hash->{sha256}, $hash->{piece_size}, $hash->{pieces}, undef, undef, undef, $hash->{target}); + $hash->{sha1}, $hash->{sha256}, $hash->{sha512}, $hash->{piece_size}, $hash->{pieces}, undef, undef, undef, $hash->{target}); if (my $hdt = $hash->{dt}) { my $hDt = Mojo::Date->new($hdt); $last_import = $hDt if !$last_import || ( $hdt && $last_import->epoch < $hDt->epoch); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.052/lib/MirrorCache/Task/FolderSync.pm new/MirrorCache-1.056/lib/MirrorCache/Task/FolderSync.pm --- old/MirrorCache-1.052/lib/MirrorCache/Task/FolderSync.pm 2023-01-12 09:40:42.000000000 +0100 +++ new/MirrorCache-1.056/lib/MirrorCache/Task/FolderSync.pm 2023-02-09 09:52:35.000000000 +0100 @@ -153,7 +153,7 @@ if ($dbfileids{$file}) { my $id = delete $dbfileidstodelete{$file}; if ( - (defined $size && defined $mtime) && ($size != $dbfilesizes{$file} || $mtime != $dbfilemtimes{$file}) + (defined $size && defined $mtime) && ($size != ($dbfilesizes{$file} // -1) || $mtime != ($dbfilemtimes{$file} // -1)) || (defined $target && $target ne ($dbfiletargets{$file} // '')) ) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.052/lib/MirrorCache/Task/MirrorProviderSync.pm new/MirrorCache-1.056/lib/MirrorCache/Task/MirrorProviderSync.pm --- old/MirrorCache-1.052/lib/MirrorCache/Task/MirrorProviderSync.pm 1970-01-01 01:00:00.000000000 +0100 +++ new/MirrorCache-1.056/lib/MirrorCache/Task/MirrorProviderSync.pm 2023-02-09 09:52:35.000000000 +0100 @@ -0,0 +1,70 @@ +# Copyright (C) 2023 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::MirrorProviderSync; +use Mojo::Base 'Mojolicious::Plugin'; +use Mojo::JSON qw(decode_json); +use MirrorCache::Utils 'datetime_now'; + +sub register { + my ($self, $app) = @_; + $app->minion->add_task(mirror_provider_sync => sub { _run($app, @_) }); +} + +my $DELAY = int($ENV{MIRRORCACHE_MIRROR_PROVIDER_SYNC_RETRY_INTERVAL} // 10 * 60); + +sub _run { + my ($app, $job, $once) = @_; + my $minion = $app->minion; + + return $job->finish('No mirror provider configured') + unless my $mirror_provider = $app->mcconfig->mirror_provider; + + return $job->finish('Previous job is still active') + unless my $guard = $minion->guard('mirror_provider_sync', 300); + + # getting list of mirrors to sync + my $ua = Mojo::UserAgent->new->max_redirects(10); + + my $url = $mirror_provider; + my $got = $ua->get($url)->result; + + return $job->fail('Request to MIRROR_PROVIDER ' . $url . ' failed, response code ' . $got->code) + if $got->code > 299; + + my $server_list = $got->json; + + return $job->fail('Failed to interpret json as array in MIRROR_PROVIDER request:' . $url) + unless ref $server_list eq 'ARRAY' && @$server_list; + + my $schema = $app->schema; + my $rsServer = $schema->resultset('Server'); + + for my $server (@$server_list) { + next unless $server->{hostname} && $server->{id}; + + my $fail = 1; + my $res = $rsServer->check_sync($server); + unless ($res) { + $job->note($server->{hostname} => 'Failed to sync ' . datetime_now) ; + } elsif ($res > 1) { + $job->note($server->{hostname} => 'Synced ' . datetime_now) ; + } + } + + return $job->retry({delay => $DELAY}) unless $once; +} + +1; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.052/lib/MirrorCache/Task/MirrorScan.pm new/MirrorCache-1.056/lib/MirrorCache/Task/MirrorScan.pm --- old/MirrorCache-1.052/lib/MirrorCache/Task/MirrorScan.pm 2023-01-12 09:40:42.000000000 +0100 +++ new/MirrorCache-1.056/lib/MirrorCache/Task/MirrorScan.pm 2023-02-09 09:52:35.000000000 +0100 @@ -52,7 +52,7 @@ return $job->finish('folder sync job is still active') unless my $guard_r = $minion->guard('folder_sync' . $path, 360); - ($folder_id, $realfolder_id, $anotherpath, $latestdt, $max_dt, $dbfiles, $dbfileids, $dbfileprefixes) + ($folder_id, $realfolder_id, $anotherpath, $latestdt, $max_dt, $dbfiles, $dbfileids, $dbfileprefixes) = _dbfiles($app, $job, $path); } return undef unless $dbfiles; @@ -68,7 +68,13 @@ my $schema = $app->schema; my $folder = $schema->resultset('Folder')->find({path => $path}); return undef unless $folder && $folder->id; # folder is not added to db yet - my $realpath = $app->mc->root->realpath($path); + my $realpath; + if ($app->mc->root->is_remote && !$ENV{MIRRORCACHE_ROOT_NFS}) { + my $redirect = $schema->resultset('Redirect')->find({pathfrom => $path}); + $realpath = $redirect->pathto if $redirect; + } else { + $realpath = $app->mc->root->realpath($path); + } my $realfolder = $schema->resultset('Folder')->find({path => $realpath}) if $realpath; # we collect max(dt) here to avoid race with new files added to DB my $latestdt = $schema->resultset('File')->find({folder_id => $realfolder? $realfolder->id : $folder->id}, { @@ -121,6 +127,7 @@ my $sid = $folder_on_mirror->{server_id}; if ($tx->result->code > 399 ) { my $sql = 'delete from folder_diff_server where server_id = ? and folder_diff_id in (select id from folder_diff where folder_id = ?)'; + $sql = 'delete folder_diff_server from folder_diff_server join folder_diff on folder_diff_id = id where server_id = ? and folder_id = ?' unless $schema->pg; # otherwise mariadb is bad at execution plan eval { $schema->storage->dbh->prepare($sql)->execute($sid, $folder_id); 1; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.052/lib/MirrorCache/WebAPI/Controller/Rest/ServerLocation.pm new/MirrorCache-1.056/lib/MirrorCache/WebAPI/Controller/Rest/ServerLocation.pm --- old/MirrorCache-1.052/lib/MirrorCache/WebAPI/Controller/Rest/ServerLocation.pm 2023-01-12 09:40:42.000000000 +0100 +++ new/MirrorCache-1.056/lib/MirrorCache/WebAPI/Controller/Rest/ServerLocation.pm 2023-02-09 09:52:35.000000000 +0100 @@ -36,4 +36,21 @@ ); } +sub list { + my ($self) = @_; + + my $region = $self->param("region"); + + return $self->render(code => 501, text => 'Expected parameter "region"') unless $region; + + my $sql = "select * from server where region = ? or exists( select 1 from server_capability_declaration where server_id = server.id and capability = 'region' and concat(',', extra, ',') like concat('%',?::text,'%'));"; + + $sql =~ s/::text//g unless $self->schema->pg; + + + my $res = $self->schema->storage->dbh->selectall_arrayref($sql, {Columns => {}}, $region, $region); + + return $self->render(json => $res); +} + 1; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.052/lib/MirrorCache/WebAPI/Controller/Rest/Table.pm new/MirrorCache-1.056/lib/MirrorCache/WebAPI/Controller/Rest/Table.pm --- old/MirrorCache-1.052/lib/MirrorCache/WebAPI/Controller/Rest/Table.pm 2023-01-12 09:40:42.000000000 +0100 +++ new/MirrorCache-1.056/lib/MirrorCache/WebAPI/Controller/Rest/Table.pm 2023-02-09 09:52:35.000000000 +0100 @@ -24,13 +24,13 @@ my %tables = ( Server => { keys => [['id'], ['hostname'],], - cols => ['id', 'sponsor', 'sponsor url', 'hostname', 'urldir', 'enabled', 'region', 'country', 'comment', 'public notes'], + cols => ['id', 'sponsor', 'sponsor url', 'hostname', 'urldir', 'enabled', 'region', 'extra regions', 'country', 'comment', 'public notes'], required => ['id', 'hostname', 'urldir'], defaults => {urldir => '', sponsor => '', 'sponsor_url' => ''}, }, MyServer => { keys => [['id'], ['hostname'],], - cols => ['id', 'sponsor', 'sponsor url', 'hostname', 'urldir', 'enabled', 'region', 'country', 'comment', 'public notes'], + cols => ['id', 'sponsor', 'sponsor url', 'hostname', 'urldir', 'enabled', 'region', 'extra regions', 'country', 'comment', 'public notes'], required => ['id', 'hostname', 'urldir'], defaults => {urldir => '', sponsor => '', 'sponsor_url' => ''}, }, @@ -51,6 +51,41 @@ my $table = $self->param("table"); my %search; + my %x; + my $region = $self->req->param('region'); + + if ($table eq 'Server' || $table eq 'MyServer') { + %x = ( + join => 'server_capability_declaration', + '+select' => [ 'server_capability_declaration.extra' ], + '+as' => [ 'extra_regions' ], + ); + + my $a = 'region'; + my $pattern = '(^|,)' . $region . '(,|$)'; + my $regexp = $self->schema->pg ? '~' : 'REGEXP'; + my $isnull = "IS NULL"; + unless ($region) { + $search{'-or'} = [ + [ "server_capability_declaration.capability" => $a ], + [ "server_capability_declaration.capability" => \$isnull ] + ]; + + } else { + $search{'-and'} = [ + '-or' => [ + [ "server_capability_declaration.capability" => $a ], + [ "server_capability_declaration.capability" => \$isnull ] + ], + '-or' => [ + 'region' => $region, + 'server_capability_declaration.extra' => { $regexp, $pattern } + ] + ]; + } + $x{join} = [ 'server_admin', 'server_capability_declaration' ] if $table eq 'MyServer'; + } + for my $key (@{$tables{$table}->{keys}}) { my $have = 1; @@ -68,16 +103,11 @@ eval { unless (_myserver($self->req)) { my $rs = $self->schema->resultset($table); - @result = %search ? $rs->search(\%search) : $rs->all; + @result = $rs->search(%search ? \%search : {}, %x? \%x : {}); } else { my $rs = $self->schema->resultset('Server'); $search{username} = $self->current_username; - @result = %search ? $rs->search( - \%search, - { - join => 'server_admin', - } - ) : $rs->all; + @result = $rs->search(\%search, \%x); } }; my $error = $@; @@ -121,6 +151,8 @@ my $error; my $id; + my $extra_regions = delete $entry{'extra_regions'}; + try { $id = $self->schema->resultset($table)->create(\%entry)->id; } catch { $error = shift; }; if ($error) { @@ -128,6 +160,12 @@ } try { $self->schema->resultset('ServerAdmin')->create({server_id => $id, username => $username}); } catch { $error = shift; } if _myserver($self->req); + try { $self->schema->resultset('ServerCapabilityDeclaration')->create({server_id => $id, capability => 'region', enabled => 1, extra => $extra_regions}); } catch { $error = shift; } if $extra_regions; + + if ($error) { + return $self->render(json => {error => $error}, status => 400); + } + my %event_data; for my $k (keys %entry) { next if !$entry{$k} or "$entry{$k}" eq ''; @@ -168,6 +206,7 @@ my @event_data; for my $k (keys %{ $entry }) { next if !$entry->{$k} || ("$entry->{$k}" eq '' && (!$rc->$k || $rc->$k . '' eq '')); + next if $k eq 'extra_regions'; if (!$rc->$k or $rc->$k . '' eq '') { push @event_data, {"new $k" => $entry->{$k}}; } elsif ($entry->{$k} ne $rc->$k) { @@ -178,7 +217,18 @@ } my $name = 'mc_' . lc $table . '_update'; $self->emit_event($name, \@event_data, $self->current_user->id); + my $extra_regions = delete $entry->{'extra_regions'}; $rc->update($entry); + if ($table eq 'Server' || $table eq 'MyServer') { + my $xrc = $schema->resultset('ServerCapabilityDeclaration')->find({server_id => $self->param('id'), capability => 'region'}); + unless ($xrc) { + $schema->resultset('ServerCapabilityDeclaration')->create({server_id => $self->param('id'), capability => 'region', enabled => 1, extra => $extra_regions}) if $extra_regions; + } elsif ($extra_regions && $extra_regions ne ($xrc->extra // '')) { + $xrc->update({ extra => $extra_regions }); + } elsif ($xrc->extra && !$extra_regions) { + $xrc->delete; + } + } $ret = 1; } else { @@ -236,8 +286,10 @@ $res = $rs->find({id => $self->param('id')}); if ($res) { my %event_data; - for my $k (@{$tables{$table}->{cols}}) { + for my $c (@{$tables{$table}->{cols}}) { + my $k = $c; # without this next line corrupts %tables $k =~ tr/ /_/; + next if $k eq 'extra_regions'; next if !$res->$k or $res->$k . '' eq ''; $event_data{$k} = $res->$k; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.052/lib/MirrorCache/WebAPI/Plugin/Backstage.pm new/MirrorCache-1.056/lib/MirrorCache/WebAPI/Plugin/Backstage.pm --- old/MirrorCache-1.052/lib/MirrorCache/WebAPI/Plugin/Backstage.pm 2023-01-12 09:40:42.000000000 +0100 +++ new/MirrorCache-1.056/lib/MirrorCache/WebAPI/Plugin/Backstage.pm 2023-02-09 09:52:35.000000000 +0100 @@ -47,6 +47,7 @@ qw(MirrorCache::Task::MirrorScan), qw(MirrorCache::Task::MirrorLocation), qw(MirrorCache::Task::MirrorProbe), + qw(MirrorCache::Task::MirrorProviderSync), qw(MirrorCache::Task::FolderHashesCreate), qw(MirrorCache::Task::FolderHashesImport), qw(MirrorCache::Task::FolderSyncScheduleFromMisses), @@ -60,6 +61,9 @@ if (defined $ENV{MIRRORCACHE_PERMANENT_JOBS}) { @permanent_jobs = split /[:,\s]+/, $ENV{MIRRORCACHE_PERMANENT_JOBS}; } + if ($app->mcconfig->mirror_provider) { + push @permanent_jobs, 'mirror_provider_sync'; + } } sub register { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.052/lib/MirrorCache/WebAPI/Plugin/Dir.pm new/MirrorCache-1.056/lib/MirrorCache/WebAPI/Plugin/Dir.pm --- old/MirrorCache-1.052/lib/MirrorCache/WebAPI/Plugin/Dir.pm 2023-01-12 09:40:42.000000000 +0100 +++ new/MirrorCache-1.056/lib/MirrorCache/WebAPI/Plugin/Dir.pm 2023-02-09 09:52:35.000000000 +0100 @@ -143,7 +143,8 @@ my $rsFolder = shift; my $c = $dm->c; - my $folder_id = $dm->folder_id; + my $folder_id = $dm->real_folder_id; + $folder_id = $dm->folder_id unless $folder_id; unless ($folder_id) { $rsFolder = $c->app->schema->resultset('Folder') unless $rsFolder; if (my $folder = $rsFolder->find({path => $dm->root_subtree . $dir})) { @@ -180,7 +181,7 @@ } $c->log->error('pedantic: ' . ($dm->pedantic // 'undef')) if $MCDEBUG; - if ($path =~ m/.*(Media|Current)\.iso(\.sha256(\.asc)?)?/ && $dm->pedantic) { + if ($path =~ m/(GNOME_.*|.*(Media|Current|Next))\.iso(\.sha256(\.asc)?)?/ && $dm->pedantic) { my $ln = $root->detect_ln_in_the_same_folder($path); $c->log->error("ln for $path : " . ($ln // 'null')) if $MCDEBUG; if ($ln) { @@ -280,6 +281,11 @@ return _render_top_folders($dm) if $ENV{MIRRORCACHE_TOP_FOLDERS} && $path eq '/'; return $root->render_file_if_nfs($dm, $path) if $root->is_remote; + # root is only local now + if (defined($dm->c->param('realpath'))) { + my $realpath = $root->realpath($path); + return $dm->redirect($dm->route . $realpath . '/') if $realpath; + } if ($root->is_dir($path)) { return $dm->redirect($dm->route . $path . '/') if !$trailing_slash && $path ne '/'; return _render_dir($dm, $path); @@ -297,30 +303,47 @@ my $file_pattern_in_folder = $trailing_slash && ($dm->regex || $dm->glob) && ($dm->metalink || $dm->meta4 || $dm->mirrorlist); $c->log->error($c->dumper('$file_pattern_in_folder', $file_pattern_in_folder)) if $MCDEBUG; - if ( (!$trailing_slash && $path ne '/') || $file_pattern_in_folder ) { + my $it_must_be_folder = ( $trailing_slash || $path eq '/'); + my $folder_or_pattern = $it_must_be_folder || $file_pattern_in_folder; + { # this bracked to simplify diff my $f = Mojo::File->new($path); - my $dirname = ($file_pattern_in_folder? $path : $f->dirname); + my $dirname = ($folder_or_pattern? $path : $f->dirname); $dirname = $root->realpath($dirname); - $dirname = $dm->root_subtree . ($file_pattern_in_folder? $path : $f->dirname) unless $dirname; - $c->log->error($c->dumper('dirname:', $dirname)) if $MCDEBUG; - if (my $parent_folder = $rsFolder->find({path => $dirname})) { - my $realpath_subtree = $root->realpath($dm->root_subtree . ($file_pattern_in_folder? $path : $f->dirname)) // $dirname; + $dirname = $dm->root_subtree . ($folder_or_pattern? $path : $f->dirname) unless $dirname; + $c->log->error($c->dumper('dirname:', $dirname, 'path:', $path, 'trail:', $trailing_slash)) if $MCDEBUG; + if (my $parent_folder = $rsFolder->find_folder_or_redirect($dirname)) { + my $realpath_subtree; + if ($root->is_remote && $parent_folder && $parent_folder->{path} ne $dirname) { + $realpath_subtree = $parent_folder->{path}; + } else { + $realpath_subtree = $root->realpath($dm->root_subtree . ($folder_or_pattern? $path : $f->dirname)) // $dirname; + } if ($dirname eq $realpath_subtree) { - if ($dirname eq $f->dirname || $file_pattern_in_folder) { - $dm->folder_id($parent_folder->id); - $dm->folder_sync_last($parent_folder->sync_last); - $dm->folder_scan_last($parent_folder->scan_last); + if ($dirname eq $f->dirname || $folder_or_pattern) { + $dm->folder_id($parent_folder->{id}); + $dm->folder_sync_last($parent_folder->{sync_last}); + $dm->folder_scan_last($parent_folder->{scan_last}); } } else { my $another_folder = $rsFolder->find({path => $dm->root_subtree . $f->dirname}); - $c->log->error($c->dumper('another_folder:', $another_folder)) if $MCDEBUG; - return undef unless $another_folder; # nothing found, proceed to _guess_what_to_render + $c->log->error($c->dumper('another_folder:', $another_folder->{id})) if $MCDEBUG; + return undef unless $another_folder || $it_must_be_folder; # nothing found, proceed to _guess_what_to_render + if ($parent_folder) { + $dm->real_folder_id($parent_folder->{id}); + $dm->folder_id($another_folder->{id}) if $another_folder; + $dm->folder_sync_last($parent_folder->{sync_last}); + $dm->folder_scan_last($parent_folder->{scan_last}); + } + } + if ($it_must_be_folder && !$file_pattern_in_folder) { + $dm->real_folder_id($parent_folder->{id}); + return _render_dir($dm, $path, $rsFolder); } my $xtra = ''; $xtra = '.zsync' if $dm->zsync && !$dm->accept_zsync; my $file; - $c->log->error($c->dumper('parent_folder:', $parent_folder->path)) if $MCDEBUG; - $file = $schema->resultset('File')->find_with_hash($parent_folder->id, $f->basename, $xtra, $dm->regex, $dm->glob_regex) if $parent_folder; + $c->log->error($c->dumper('parent_folder:', $parent_folder->{path})) if $MCDEBUG && $parent_folder; + $file = $schema->resultset('File')->find_with_hash($parent_folder->{id}, $f->basename, $xtra, $dm->regex, $dm->glob_regex) if $parent_folder; $c->log->error($c->dumper('file:', $f->basename, $file)) if $MCDEBUG; # folders are stored with trailing slash in file table, so they will not be selected here @@ -333,11 +356,6 @@ $path = $path . '.zsync'; } - $c->log->error($c->dumper('file_size: ', $file->{size} // 'undef', 'huge_file_size: ', $mc_config->huge_file_size)) if $MCDEBUG; - if ($root->is_remote && !$dm->extra && $file->{size} && $mc_config->redirect_huge && $mc_config->huge_file_size <= $file->{size}) { - $dm->redirect($dm->scheme . '://' . $mc_config->redirect_huge . $path); - return 1; - } if ($file->{target}) { # redirect to the symlink $dm->redirect($dm->route . $dirname . '/' . $file->{target}); @@ -349,16 +367,7 @@ return 1; } } - } elsif (my $folder = $rsFolder->find_folder_or_redirect($dm->root_subtree . $path)) { - $c->log->error('found redirect : ', $folder->{pathto}) if $MCDEBUG && $folder->{pathto}; - return $dm->redirect($folder->{pathto}) if $folder->{pathto}; - # folder must have trailing slash, otherwise it will be a challenge to render links on webpage - $dm->folder_id($folder->{id}); - $dm->folder_sync_last($folder->{sync_last}); - $dm->folder_scan_last($folder->{scan_last}); - $dm->file_id(-1); - return _render_dir($dm, $path, $rsFolder, $folder) if ($folder->{sync_last}); - } + } # bracked to simplify diff return undef; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.052/lib/MirrorCache/WebAPI/Plugin/RenderFileFromMirror.pm new/MirrorCache-1.056/lib/MirrorCache/WebAPI/Plugin/RenderFileFromMirror.pm --- old/MirrorCache-1.052/lib/MirrorCache/WebAPI/Plugin/RenderFileFromMirror.pm 2023-01-12 09:40:42.000000000 +0100 +++ new/MirrorCache-1.056/lib/MirrorCache/WebAPI/Plugin/RenderFileFromMirror.pm 2023-02-09 09:52:35.000000000 +0100 @@ -1,4 +1,4 @@ -# Copyright (C) 2020-2022 SUSE LLC +# Copyright (C) 2020-2023 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,7 +44,11 @@ my $root = $c->mc->root; my $f = Mojo::File->new($filepath); my $dirname = $f->dirname; - my $realdirname = $root->realpath($f->dirname); + my $realfolder_id = $dm->real_folder_id; + my $realdirname; + unless ($realfolder_id) { + $realdirname = $root->realpath($f->dirname); + } $realdirname = $dirname unless $realdirname; my $basename = $f->basename; $basename = $file->{name} if $file; @@ -72,8 +76,7 @@ } $schema->resultset('Folder')->set_wanted($folder_id) if $need_update; } - my $realfolder_id = 0; - if ($realdirname ne $dirname) { + if (!$realfolder_id && $realdirname ne $dirname) { my $realfolder = $schema->resultset('Folder')->find({path => $realdirname}); $realfolder_id = $realfolder->id if $realfolder; $c->log->error($c->dumper('RENDER FOLDER REAL', $realfolder_id ? $realfolder_id : 'NULL')) if $MCDEBUG; @@ -114,7 +117,7 @@ my $fullurl; # baseurl with path and filename if ($dm->metalink || $dm->meta4 || $dm->torrent || $dm->zsync || $dm->magnet) { if (!$root->is_remote) { - $baseurl = $root->redirect($dm, $dirname); # we must pass $path here because it potenially has impact + $baseurl = $root->redirect($dm, $filepath); # we must pass $path here because it potenially has impact } elsif ($file->{size} && $mc_config->redirect_huge && $mc_config->huge_file_size <= $file->{size}) { $baseurl = $dm->scheme . '://' . $mc_config->redirect_huge . $filepath; } else { @@ -281,6 +284,7 @@ md5 => $file->{md5}, sha1 => $file->{sha1}, sha256 => $file->{sha256}, + sha512 => $file->{sha512}, }; my @regions = $c->subsidiary->regions($region, $country); @@ -309,6 +313,11 @@ } unless ($mirror) { + if ($root->is_remote && $file->{size} && $mc_config->redirect_huge && $mc_config->huge_file_size <= $file->{size}) { + $dm->redirect($dm->scheme . '://' . $mc_config->redirect_huge . $filepath); + return 1; + } + $root->render_file($dm, $filepath); return 1; } @@ -448,6 +457,11 @@ $writer->characters($sha256); $writer->endTag('hash'); } + if (my $sha512 = $file->{sha512}) { + $writer->startTag('hash', type => 'sha-512'); + $writer->characters($sha512); + $writer->endTag('hash'); + } if (my $piece_size = $file->{piece_size}) { $writer->startTag('pieces', length => $piece_size, type => 'sha-1'); for my $piece (grep {$_} split /(.{40})/, $file->{pieces}) { @@ -560,7 +574,8 @@ my $md5 = $file->{md5}; my $sha1 = $file->{sha1}; my $sha256 = $file->{sha256}; - if ($md5 || $sha1 || $sha256) { + my $sha512 = $file->{sha512}; + if ($md5 || $sha1 || $sha256 || $sha512) { $writer->startTag('verification'); if ($md5) { $writer->startTag('hash', type => 'md5'); @@ -577,6 +592,11 @@ $writer->characters($sha256); $writer->endTag('hash'); } + if ($sha512) { + $writer->startTag('hash', type => 'sha-512'); + $writer->characters($sha512); + $writer->endTag('hash'); + } if (my $piece_size = $file->{piece_size}) { $writer->startTag('pieces', length => $piece_size, type => 'sha-1'); my $piecen = 0; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.052/lib/MirrorCache/WebAPI/Plugin/RootLocal.pm new/MirrorCache-1.056/lib/MirrorCache/WebAPI/Plugin/RootLocal.pm --- old/MirrorCache-1.052/lib/MirrorCache/WebAPI/Plugin/RootLocal.pm 2023-01-12 09:40:42.000000000 +0100 +++ new/MirrorCache-1.056/lib/MirrorCache/WebAPI/Plugin/RootLocal.pm 2023-02-09 09:52:35.000000000 +0100 @@ -85,7 +85,7 @@ } else { 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); + $res = !!$c->render_file(filepath => $rootpath . $root_subtree . $filepath, content_type => $dm->mime, content_disposition => 'inline'); } $c->stat->redirect_to_root($dm, $not_miss); return $res; @@ -102,7 +102,7 @@ eval { $size = -s $full if -f $full; }; return undef unless ((defined $size) && $size <= $max_size); my $c = $dm->c; - $c->render_file(filepath => $full, content_type => $dm->mime); + $c->render_file(filepath => $full, content_type => $dm->mime, content_disposition => 'inline'); return 1; } @@ -161,7 +161,7 @@ sub _detect_ln_in_the_same_folder { my ($dir, $file) = @_; - return undef unless $file && $file =~ m/.*(Media|Current)\.iso(\.sha256)?/; + return undef unless $file && $file =~ m/(GNOME_.*|.*(Media|Current|Next))\.iso(\.sha256(\.asc)?)?/; my $dest; for my $root (@roots) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.052/lib/MirrorCache/WebAPI/Plugin/RootRemote.pm new/MirrorCache-1.056/lib/MirrorCache/WebAPI/Plugin/RootRemote.pm --- old/MirrorCache-1.052/lib/MirrorCache/WebAPI/Plugin/RootRemote.pm 2023-01-12 09:40:42.000000000 +0100 +++ new/MirrorCache-1.056/lib/MirrorCache/WebAPI/Plugin/RootRemote.pm 2023-02-09 09:52:35.000000000 +0100 @@ -75,7 +75,7 @@ } elsif ($deep) { my $path1 = $path . '/'; my $rootlocation = $self->rooturl; - my $url = $rootlocation . $path1; + my $url = $rootlocation . $path1 . '?realpath'; my $ua = Mojo::UserAgent->new->max_redirects(0)->request_timeout(1); my $tx = $ua->head($url, {'User-Agent' => 'MirrorCache/detect_redirect'}); my $res = $tx->res; @@ -83,6 +83,8 @@ # redirect on oneself if ($res->is_redirect && $res->headers) { my $location1 = $res->headers->location; + $location1 =~ s/\?realpath=?$//; + return substr($location1,0,-1) if '/' eq substr($location1,0,1); if ($location1 && $path1 ne substr($location1, -length($path1))) { my $i = rindex($location1, $rootlocation, 0); if ($i ne -1) { @@ -129,7 +131,7 @@ my $c = $dm->c; if ($nfs && $dm->must_render_from_root && -f $nfs . $filepath) { - $c->render_file(filepath => $nfs . $filepath, content_type => $dm->mime); + $c->render_file(filepath => $nfs . $filepath, content_type => $dm->mime, content_disposition => 'inline'); $c->stat->redirect_to_root($dm, $not_miss); return 1; } @@ -146,7 +148,7 @@ my $c = $dm->c; return undef unless($dm->must_render_from_root && -f $nfs . $filepath); - $c->render_file(filepath => $nfs . $filepath, content_type => $dm->mime); + $c->render_file(filepath => $nfs . $filepath, content_type => $dm->mime, content_disposition => 'inline'); $c->stat->redirect_to_root($dm, 1); return 1; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.052/lib/MirrorCache/WebAPI.pm new/MirrorCache-1.056/lib/MirrorCache/WebAPI.pm --- old/MirrorCache-1.052/lib/MirrorCache/WebAPI.pm 2023-01-12 09:40:42.000000000 +0100 +++ new/MirrorCache-1.056/lib/MirrorCache/WebAPI.pm 2023-02-09 09:52:35.000000000 +0100 @@ -173,6 +173,8 @@ my $rest = $r->any('/rest'); 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_location')->name('rest_server_location')->to('server_location#list'); + $rest_r->get('/server/location') ->to('server_location#list'); $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'); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.052/lib/MirrorCache/resources/migrations/Pg.sql new/MirrorCache-1.056/lib/MirrorCache/resources/migrations/Pg.sql --- old/MirrorCache-1.052/lib/MirrorCache/resources/migrations/Pg.sql 2023-01-12 09:40:42.000000000 +0100 +++ new/MirrorCache-1.056/lib/MirrorCache/resources/migrations/Pg.sql 2023-02-09 09:52:35.000000000 +0100 @@ -370,3 +370,5 @@ ); -- 28 -- do nothing +-- 29 +alter table hash add column if not exists sha512 varchar(128); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.052/lib/MirrorCache/resources/migrations/mysql.sql new/MirrorCache-1.056/lib/MirrorCache/resources/migrations/mysql.sql --- old/MirrorCache-1.052/lib/MirrorCache/resources/migrations/mysql.sql 2023-01-12 09:40:42.000000000 +0100 +++ new/MirrorCache-1.056/lib/MirrorCache/resources/migrations/mysql.sql 2023-02-09 09:52:35.000000000 +0100 @@ -371,3 +371,5 @@ update popular_os set version = '\\1' where version = '\1'; update popular_os set version = '\\2' where version = '\2'; update popular_os set version = '\\3' where version = '\3'; +-- 29 up +alter table hash add column if not exists sha512 varchar(128); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.052/t/environ/01-smoke-mirror-provider.sh new/MirrorCache-1.056/t/environ/01-smoke-mirror-provider.sh --- old/MirrorCache-1.052/t/environ/01-smoke-mirror-provider.sh 1970-01-01 01:00:00.000000000 +0100 +++ new/MirrorCache-1.056/t/environ/01-smoke-mirror-provider.sh 2023-02-09 09:52:35.000000000 +0100 @@ -0,0 +1,69 @@ +#!lib/test-in-container-environ.sh +set -ex + +mcmirror=$(environ mc5 $(pwd)) +mcna=$(environ mc1 $(pwd)) +mcnaeast=$(environ mc2 $(pwd)) + +ap8=$(environ ap8) +ap7=$(environ ap7) + +$mcmirror/gen_env MIRRORCACHE_MODE=mirror_provider + + +$mcmirror/start + +$mcmirror/sql "insert into server(hostname,urldir,enabled,country,region) select '$($ap7/print_address)','','t','us','na'" +$mcmirror/sql "insert into server(hostname,urldir,enabled,country,region) select '$($ap8/print_address)','','t','us','na'" + +$mcmirror/sql "insert into server_capability_declaration(server_id, capability, enabled, extra) select '1','region','t','na-east,na-west'" + +$mcmirror/curl /rest/server_location?region=na | grep $($ap7/print_address) | grep $($ap8/print_address) +$mcmirror/curl /rest/server_location?region=na-east | grep $($ap7/print_address) +rc=0 +$mcmirror/curl /rest/server_location?region=na-east | grep $($ap8/print_address) || rc=$? +test $rc -gt 0 + +$mcna/gen_env MIRRORCACHE_MIRROR_PROVIDER=$($mcmirror/print_address)/rest/server_location?region=na MIRRORCACHE_MIRROR_PROVIDER_SYNC_RETRY_INTERVAL=1 +$mcna/start +# the job importing mirrors should schedule automatically because mirror_provider_region is defined +$mcna/backstage/shoot + +$mcna/sql_test 2 = 'select count(*) from server' # both servers are imported +$mcna/curl /rest/server | grep $($ap7/print_address) | grep $($ap8/print_address) | grep '\bus\b' + + +$mcnaeast/gen_env MIRRORCACHE_INI=$mcnaeast/conf.ini + +( +echo mirror_provider=$($mcmirror/print_address)/rest/server_location?region=na-east +echo +echo [db] +echo +) >> $mcnaeast/conf.ini + + +$mcnaeast/start +# the job importing mirrors should schedule automatically because mirror_provider_region is defined +$mcnaeast/backstage/shoot + +$mcnaeast/sql_test 1 = 'select count(*) from server' # only one servers are imported + +rc=0 +$mcnaeast/curl /rest/server | grep $($ap7/print_address) | grep '"country":"us"' +$mcnaeast/curl /rest/server | grep $($ap8/print_address) || rc=$? + +test $rc -gt 0 + +sleep 1 +echo change server on mcmirror, make sure in gets synced +$mcmirror/sql "update server set country = 'ca' where id = 1"; +$mcna/sql_test us = "select country from server where id = 1" +$mcna/backstage/shoot +$mcna/sql_test ca = "select country from server where id = 1" + +echo now restart job on mc1, manually check that job is ok +sleep 1 +$mcna/backstage/shoot + +echo success diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.052/t/environ/02-files-hashes-import.sh new/MirrorCache-1.056/t/environ/02-files-hashes-import.sh --- old/MirrorCache-1.052/t/environ/02-files-hashes-import.sh 2023-01-12 09:40:42.000000000 +0100 +++ new/MirrorCache-1.056/t/environ/02-files-hashes-import.sh 2023-02-09 09:52:35.000000000 +0100 @@ -8,45 +8,48 @@ # hq mirros: ap1 ap2 # na mirros: ap3 ap4 -for i in 9 6; do +for i in 6 9; do x=$(environ mc$i $(pwd)) - mkdir -p $x/dt/{folder1,folder2,folder3,folder4} - echo $x/dt/{folder1,folder2,folder3,folder4}/{file1.1,file2.1}.dat | xargs -n 1 touch - echo 1111111111 > $x/dt/folder1/file1.1.dat - echo 1111111112 > $x/dt/folder2/file1.1.dat eval mc$i=$x done for i in 1 2 3 4; do x=$(environ ap$i) + $x/start + eval ap$i=$x +done + +for x in mc9 ap1 ap2 ap3 ap4; do mkdir -p $x/dt/{folder1,folder2,folder3,folder4} echo $x/dt/{folder1,folder2,folder3,folder4}/{file1.1,file2.1}.dat | xargs -n 1 touch echo 1111111111 > $x/dt/folder1/file1.1.dat echo 1111111112 > $x/dt/folder2/file1.1.dat - eval ap$i=$x - $x/start + ( cd $x/dt ; ln -s folder1 link1 ) + mkdir $x/dt/folder1/child1 + touch $x/dt/folder1/child1/child1.1.dat done + # set the same modification time for file1.1.dat -touch -d "$(date -R -r $mc9/dt/folder1/file1.1.dat)" {$mc6,$ap1,$ap2,$ap3,$ap4}/dt/folder1/file1.1.dat +touch -d "$(date -R -r $mc9/dt/folder1/file1.1.dat)" {$ap1,$ap2,$ap3,$ap4}/dt/folder1/file1.1.dat hq_address=$($mc9/print_address) na_address=$($mc6/print_address) na_interface=127.0.0.2 # deploy db -$mc9/gen_env MIRRORCACHE_HASHES_COLLECT=1 MIRRORCACHE_HASHES_PIECES_MIN_SIZE=5 "MIRRORCACHE_TOP_FOLDERS='folder1 folder2 folder3 folder4'" MIRRORCACHE_BRANDING=SUSE MIRRORCACHE_WORKERS=4 MIRRORCACHE_DAEMON=1 +$mc9/gen_env MIRRORCACHE_HASHES_COLLECT=1 MIRRORCACHE_HASHES_PIECES_MIN_SIZE=5 "MIRRORCACHE_TOP_FOLDERS='folder1 folder2 folder3 folder4 link1'" MIRRORCACHE_BRANDING=SUSE MIRRORCACHE_WORKERS=4 MIRRORCACHE_DAEMON=1 $mc9/backstage/shoot -$mc9/sql "insert into subsidiary(hostname,region) select '$na_address','na'" +$mc9/sql "insert into subsidiary(hostname,region) select '$na_address','eu'" $mc9/start $mc9/sql "insert into server(hostname,urldir,enabled,country,region) select '$($ap1/print_address)','','t','jp','as'" $mc9/sql "insert into server(hostname,urldir,enabled,country,region) select '$($ap2/print_address)','','t','jp','as'" -$mc6/gen_env MIRRORCACHE_REGION=na MIRRORCACHE_HEADQUARTER=$hq_address "MIRRORCACHE_TOP_FOLDERS='folder1 folder2 folder3'" MIRRORCACHE_HASHES_IMPORT=1 +$mc6/gen_env MIRRORCACHE_REGION=na MIRRORCACHE_HEADQUARTER=$hq_address "MIRRORCACHE_TOP_FOLDERS='folder1 folder2 folder3 link1'" MIRRORCACHE_HASHES_IMPORT=1 MIRRORCACHE_ROOT=http://$($mc9/print_address) $mc6/start -$mc6/sql "insert into server(hostname,urldir,enabled,country,region) select '$($ap3/print_address)','','t','us','na'" -$mc6/sql "insert into server(hostname,urldir,enabled,country,region) select '$($ap4/print_address)','','t','ca','na'" +$mc6/sql "insert into server(hostname,urldir,enabled,country,region) select '$($ap3/print_address)','','t','de','eu'" +$mc6/sql "insert into server(hostname,urldir,enabled,country,region) select '$($ap4/print_address)','','t','dk','eu'" for i in 9 6; do mc$i/backstage/job -e folder_sync -a '["/folder1"]' @@ -58,10 +61,11 @@ curl -s "http://$na_address/folder1/?hashes&since=2021-01-01" | grep file1.1.dat for i in 9 6; do - test b2c5860a03d2c4f1f049a3b2409b39a8 == $(mc$i/sql 'select md5 from hash where file_id=1') - test 5179db3d4263c9cb4ecf0edbc653ca460e3678b7 == $(mc$i/sql 'select sha1 from hash where file_id=1') - test 63d19a99ef7db94ddbb1e4a5083062226551cd8197312e3aa0aa7c369ac3e458 == $(mc$i/sql 'select sha256 from hash where file_id=1') - test 5179db3d4263c9cb4ecf0edbc653ca460e3678b7 == $(mc$i/sql 'select pieces from hash where file_id=1') + test b2c5860a03d2c4f1f049a3b2409b39a8 == $(mc$i/sql 'select md5 from hash where file_id=2') + test 5179db3d4263c9cb4ecf0edbc653ca460e3678b7 == $(mc$i/sql 'select sha1 from hash where file_id=2') + test 63d19a99ef7db94ddbb1e4a5083062226551cd8197312e3aa0aa7c369ac3e458 == $(mc$i/sql 'select sha256 from hash where file_id=2') + test 5179db3d4263c9cb4ecf0edbc653ca460e3678b7 == $(mc$i/sql 'select pieces from hash where file_id=2') + test 2a276e680779492af2ed54ba5661ac5f35b39e363c95a55ddfac644c1aca2c3f68333225362e66536460999a7f86b1f2dc7e8ef469e3dc5042ad07d491f13de2 == $(mc$i/sql 'select sha512 from hash where file_id=2') done mc9/curl -sL /folder1/file1.1.dat.metalink | grep 63d19a99ef7db94ddbb1e4a5083062226551cd8197312e3aa0aa7c369ac3e458 @@ -81,13 +85,13 @@ curl -si "http://$hq_address/folder2?hashes" mc9/backstage/shoot mc9/backstage/shoot -q hashes -test d8f5889697e9ec5ba9a8ab4aede6e7d1d7858884e81db19b3e9780d6a64671a3 == $(mc9/sql 'select sha256 from hash where file_id=3') +test d8f5889697e9ec5ba9a8ab4aede6e7d1d7858884e81db19b3e9780d6a64671a3 == $(mc9/sql 'select sha256 from hash where file_id=4') mc6/backstage/job -e folder_sync -a '["/folder2"]' mc6/backstage/shoot mc6/backstage/shoot -q hashes -test d8f5889697e9ec5ba9a8ab4aede6e7d1d7858884e81db19b3e9780d6a64671a3 == $(mc6/sql 'select sha256 from hash where file_id=3') +test d8f5889697e9ec5ba9a8ab4aede6e7d1d7858884e81db19b3e9780d6a64671a3 == $(mc6/sql 'select sha256 from hash where file_id=4') DELAY=1; echo Import folder unknown on master, but relay on automatic retry @@ -97,11 +101,11 @@ mc9/backstage/shoot mc9/backstage/shoot -q hashes -test e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 == $(mc9/sql 'select sha256 from hash where file_id=5') +test e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 == $(mc9/sql 'select sha256 from hash where file_id=6') sleep $DELAY mc6/backstage/shoot -q hashes -test e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 == $(mc6/sql 'select sha256 from hash where file_id=5') +test e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 == $(mc6/sql 'select sha256 from hash where file_id=6') test -n "$(mc6/sql 'select hash_last_import from folder where id=3')" @@ -112,26 +116,81 @@ mc9/backstage/job -e folder_sync -a '["/folder4"]' mc9/backstage/shoot mc9/backstage/shoot -q hashes -test e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 == $(mc9/sql 'select sha256 from hash where file_id=7') test e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 == $(mc9/sql 'select sha256 from hash where file_id=8') +test e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 == $(mc9/sql 'select sha256 from hash where file_id=9') -mc9/sql 'delete from hash where file_id=8' +mc9/sql 'delete from hash where file_id=9' mc6/backstage/job -e folder_sync -a '["/folder4"]' mc6/backstage/shoot MIRRORCACHE_HASHES_IMPORT_RETRY_DELAY=$DELAY mc6/backstage/shoot -q hashes -test e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 == $(mc9/sql 'select sha256 from hash where file_id=7') -test -z "$(mc9/sql 'select sha256 from hash where file_id=8')" +test e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 == $(mc9/sql 'select sha256 from hash where file_id=8') +test -z "$(mc9/sql 'select sha256 from hash where file_id=9')" echo Recalculate hashes on HQ mc9/backstage/job -e folder_hashes_create -a '["/folder4"]' mc9/backstage/shoot sleep $DELAY mc6/backstage/shoot -q hashes # this should retry the import because some hashes were missing -test e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 == $(mc9/sql 'select sha256 from hash where file_id=7') -test e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 == $(mc6/sql 'select sha256 from hash where file_id=8') +test e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 == $(mc9/sql 'select sha256 from hash where file_id=8') +test e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 == $(mc6/sql 'select sha256 from hash where file_id=9') test -n "$(mc6/sql 'select hash_last_import from folder where id=3')" +echo check how symlink work +cnt=$(mc6/sql 'select count(*) from file') + +mc9/curl -I /link1/file1.1.dat +mc9/curl -I /link1/child1/child1.1.dat +mc6/curl -I /link1/file1.1.dat +mc6/curl -I /link1/child1/child1.1.dat + +mc9/backstage/job folder_sync_schedule_from_misses +mc9/backstage/job folder_sync_schedule +mc9/backstage/job mirror_scan_schedule +mc9/backstage/shoot +mc9/backstage/shoot -q hashes + +mc9/curl -I /link1/ | grep '200 OK' # no redirects +mc9/curl -I /link1/file1.1.dat | grep -E "$($ap1/print_address)|$($ap2/print_address)" +mc9/curl -I /link1/child1/child1.1.dat | grep -E "$($ap1/print_address)|$($ap2/print_address)" + +mc6/backstage/job folder_sync_schedule_from_misses +mc6/backstage/job folder_sync_schedule +mc6/backstage/job mirror_scan_schedule +mc6/backstage/shoot +mc6/backstage/shoot -q hashes + +echo number of files will increase for 1 because of child1.1.dat + + +mc6/curl -I /link1/ | grep '200 OK' # no redirects +mc6/curl -I /link1/child1/ | grep '200 OK' # no redirects +mc6/sql_test $((cnt+1)) == 'select count(*) from file' +# mc6/sql_test /link1 == 'select pathfrom from redirect' + +mc6/sql_test /link1 == "select path from folder where path = '/link1'" + +mc6/sql_test 0 -lt "select count(*) from folder_diff join folder on folder_id = folder.id where path ='/link1'" + +mc6/curl -I /link1/file1.1.dat | grep -E "$($ap4/print_address)|$($ap3/print_address)" + +mc6/curl /link1/file1.1.dat.meta4 \ + | grep -C20 $($ap3/print_address)/link1/file1.1.dat \ + | grep -C20 $($ap3/print_address)/folder1/file1.1.dat \ + | grep -C20 $($ap4/print_address)/link1/file1.1.dat \ + | grep $($ap4/print_address)/folder1/file1.1.dat + + +mc6/sql_test /link1/child1 == "select path from folder where path = '/link1/child1'" + +mc6/sql_test 0 -lt "select count(*) from folder_diff join folder on folder_id = folder.id where path ='/link1/child1'" + +mc6/curl -I /link1/child1/child1.1.dat | grep -E "$($ap4/print_address)|$($ap3/print_address)" + +mc6/curl /link1/child1/child1.1.dat.meta4 \ + | grep -C20 $($ap3/print_address)/link1/child1/child1.1.dat \ + | grep -C20 $($ap3/print_address)/folder1/child1/child1.1.dat \ + echo success diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.052/t/environ/02-files-hashes.sh new/MirrorCache-1.056/t/environ/02-files-hashes.sh --- old/MirrorCache-1.052/t/environ/02-files-hashes.sh 2023-01-12 09:40:42.000000000 +0100 +++ new/MirrorCache-1.056/t/environ/02-files-hashes.sh 2023-02-09 09:52:35.000000000 +0100 @@ -26,9 +26,11 @@ $mc/db/sql "select * from file" $mc/db/sql "select * from hash" -test b2c5860a03d2c4f1f049a3b2409b39a8 == $($mc/db/sql 'select md5 from hash where file_id=1') -test 5179db3d4263c9cb4ecf0edbc653ca460e3678b7 == $($mc/db/sql 'select sha1 from hash where file_id=1') -test 63d19a99ef7db94ddbb1e4a5083062226551cd8197312e3aa0aa7c369ac3e458 == $($mc/db/sql 'select sha256 from hash where file_id=1') +test b2c5860a03d2c4f1f049a3b2409b39a8 == $($mc/sql 'select md5 from hash where file_id=1') +test 5179db3d4263c9cb4ecf0edbc653ca460e3678b7 == $($mc/sql 'select sha1 from hash where file_id=1') +test 63d19a99ef7db94ddbb1e4a5083062226551cd8197312e3aa0aa7c369ac3e458 == $($mc/sql 'select sha256 from hash where file_id=1') +test 2a276e680779492af2ed54ba5661ac5f35b39e363c95a55ddfac644c1aca2c3f68333225362e66536460999a7f86b1f2dc7e8ef469e3dc5042ad07d491f13de2 == $($mc/sql 'select sha512 from hash where file_id=1') + test 5179db3d4263c9cb4ecf0edbc653ca460e3678b7 == $($mc/db/sql 'select pieces from hash where file_id=1') # this value 96ff97ccb1 is also reported by zsyncmake (the last bytes are hashes): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.052/t/environ/03-headquarter-subsidiaries-remote.sh new/MirrorCache-1.056/t/environ/03-headquarter-subsidiaries-remote.sh --- old/MirrorCache-1.052/t/environ/03-headquarter-subsidiaries-remote.sh 2023-01-12 09:40:42.000000000 +0100 +++ new/MirrorCache-1.056/t/environ/03-headquarter-subsidiaries-remote.sh 2023-02-09 09:52:35.000000000 +0100 @@ -2,6 +2,7 @@ set -ex # root : ng9 +# a mirror in headquarter : ng8 # mc environ by number: # 9 - headquarter @@ -9,6 +10,7 @@ # 7 - EU subsidiary root=$(environ ng9) +mirror=$(environ ng8) mkdir -p $root/dt/{folder1,folder2,folder3} echo $root/dt/{folder1,folder2,folder3}/{file1.1,file2.1}.dat | xargs -n 1 touch @@ -18,13 +20,21 @@ HUGE_FILE_SIZE=9 FAKEURL="notexists${RANDOM}.com" echo -n 1234 > $root/dt/folder1/repodata/filebig1.1.dat +echo -n 1234 > $root/dt/folder1/repodata/filebig2.1.dat echo -n 123 > $root/dt/folder1/repodata/filesmall1.1.dat echo -n 123456789 > $root/dt/folder1/repodata/filehuge1.1.dat +echo -n 123456789 > $root/dt/folder1/repodata/filehuge2.1.dat echo repomdcontent > $root/dt/folder1/repodata/repomd.xml touch $root/dt/folder1/repodata/repomd.xml.asc $root/start +$mirror/start +cp -r $root/dt/* $mirror/dt/ +rm $mirror/dt/folder1/repodata/filebig1.1.dat +rm $mirror/dt/folder1/repodata/filehuge1.1.dat + + mc9=$(environ mc9 $(pwd)) $mc9/gen_env "MIRRORCACHE_TOP_FOLDERS='folder1 folder2 folder3'" \ MIRRORCACHE_SCHEDULE_RETRY_INTERVAL=0 \ @@ -39,6 +49,9 @@ $mc9/sql "insert into subsidiary(hostname,region) select 'naaddress.com','na'" $mc9/sql "insert into subsidiary(hostname,region) select 'euaddress.net','eu'" + +$mc9/sql "insert into server(hostname,urldir,country,region,enabled) select '$($mirror/print_address)','','cn','as','t'" + $mc9/start $mc9/backstage/job -e folder_sync -a '["/folder1/repodata"]' @@ -71,10 +84,14 @@ echo check huge files are redirected to FAKEURL, but we need to scan folder first $mc9/curl -I --interface $hq_interface /download/folder1/repodata/filehuge1.1.dat | grep "Location: http://$FAKEURL/folder1/repodata/filehuge1.1.dat" -$mc9/curl -I --interface $hq_interface /download/folder1/repodata/filebig1.1.dat | grep "Location: " | grep $($root/print_address) +$mc9/curl -I --interface $hq_interface /download/folder1/repodata/filebig1.1.dat | grep "Location: " | grep $($root/print_address) # we removed it from mirror +$mc9/curl -I --interface $hq_interface /download/folder1/repodata/filebig2.1.dat | grep "Location: " | grep $($mirror/print_address) # this is on mirror $mc9/curl --interface $hq_interface /download/folder1/repodata/filehuge1.1.dat.meta4 | grep "http://$FAKEURL/folder1/repodata/filehuge1.1.dat" -$mc9/curl --interface $hq_interface /download/folder1/repodata/filehuge1.1.dat.mirrorlist | grep "http://$FAKEURL/folder1/repodata/filehuge1.1.dat" +$mc9/curl --interface $hq_interface /download/folder1/repodata/filehuge1.1.dat.mirrorlist | grep "http://$FAKEURL/folder1/repodata/filehuge1.1.dat" # we removed it from mirror + +$mc9/curl -I --interface $hq_interface /download/folder1/repodata/filehuge1.1.dat | grep "http://$FAKEURL/folder1/repodata/filehuge1.1.dat" # we removed it from mirror +$mc9/curl -I --interface $hq_interface /download/folder1/repodata/filehuge2.1.dat | grep $($mirror/print_address)/folder1/repodata/filehuge2.1.dat # this is on mirror echo success diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.052/templates/app/myserver/index.html.ep new/MirrorCache-1.056/templates/app/myserver/index.html.ep --- old/MirrorCache-1.052/templates/app/myserver/index.html.ep 2023-01-12 09:40:42.000000000 +0100 +++ new/MirrorCache-1.056/templates/app/myserver/index.html.ep 2023-02-09 09:52:35.000000000 +0100 @@ -8,6 +8,13 @@ <div class="row"> <div class="col-sm-12"> <h2><%= title %></h2> + <div id="regionspanelparent" class="card"> + <div id="regionspanel" style="display: none"> + <span style="float: left">Regions</span> + <ul id="regionspanelul" style="float: left"> + </ul> + </div> + </div> %= include 'layouts/info' @@ -20,6 +27,7 @@ <th class="col_value">Hostname</th> <th class="col_value">Urldir</th> <th class="col_value">Region</th> + <th class="col_value">Extra Regions</th> <th class="col_value">Country</th> <th class="col_value">Comment</th> <th class="col_value">Public Notes</th> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.052/templates/app/server/index.html.ep new/MirrorCache-1.056/templates/app/server/index.html.ep --- old/MirrorCache-1.052/templates/app/server/index.html.ep 2023-01-12 09:40:42.000000000 +0100 +++ new/MirrorCache-1.056/templates/app/server/index.html.ep 2023-02-09 09:52:35.000000000 +0100 @@ -8,6 +8,13 @@ <div class="row"> <div class="col-sm-12"> <h2><%= title %></h2> + <div id="regionspanelparent" class="card"> + <div id="regionspanel" style="display: none"> + <span style="float: left">Regions</span> + <ul id="regionspanelul" style="float: left"> + </ul> + </div> + </div> %= include 'layouts/info' @@ -20,6 +27,7 @@ <th class="col_value">Hostname</th> <th class="col_value">Urldir</th> <th class="col_value">Region</th> + <th class="col_value">Extra Regions</th> <th class="col_value">Country</th> <th class="col_value">Comment</th> <th class="col_value">Public Notes</th> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.052/templates/branding/openSUSE/footer.html.ep new/MirrorCache-1.056/templates/branding/openSUSE/footer.html.ep --- old/MirrorCache-1.052/templates/branding/openSUSE/footer.html.ep 2023-01-12 09:40:42.000000000 +0100 +++ new/MirrorCache-1.056/templates/branding/openSUSE/footer.html.ep 2023-02-09 09:52:35.000000000 +0100 @@ -7,7 +7,7 @@ <div class="list-inline"> <a class="list-inline-item" href="https://en.opensuse.org/Imprint">Legal notice</a> <a class="list-inline-item" href="https://github.com/openSUSE/MirrorCache">Source code</a> - <a class="list-inline-item" href="https://bugzilla.opensuse.org/">Report issue</a> + <a class="list-inline-item" href="https://github.com/openSUSE/MirrorCache/issues/new">Report issue</a> % if (my $ver = eval '$current_version') { <a>MirrorCache <%= $ver %></a> % } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.052/templates/branding/openSUSE/header.html.ep new/MirrorCache-1.056/templates/branding/openSUSE/header.html.ep --- old/MirrorCache-1.052/templates/branding/openSUSE/header.html.ep 2023-01-12 09:40:42.000000000 +0100 +++ new/MirrorCache-1.056/templates/branding/openSUSE/header.html.ep 2023-02-09 09:52:35.000000000 +0100 @@ -75,7 +75,7 @@ </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' + %= link_to '15.5 Mirrors' => url_for('/report/mirrors/155') => class => 'dropdown-item' </li><li> %= link_to 'TW Mirrors' => url_for('/report/mirrors/tumbleweed') => class => 'dropdown-item' </li><li> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/MirrorCache-1.052/templates/mirrorlist.html.ep new/MirrorCache-1.056/templates/mirrorlist.html.ep --- old/MirrorCache-1.052/templates/mirrorlist.html.ep 2023-01-12 09:40:42.000000000 +0100 +++ new/MirrorCache-1.056/templates/mirrorlist.html.ep 2023-02-09 09:52:35.000000000 +0100 @@ -65,6 +65,9 @@ % if ($file->{mtime}) { <li>Last modified: <%= $file->{hmtime} %> (Unix timestamp: <%= $file->{mtime} %>)</li> % } + % if ($file->{sha512}) { + <li>SHA-512 Hash: <%= $file->{sha512} %></li> + % } % if ($file->{sha256}) { <li>SHA-256 Hash: <%= $file->{sha256} %></li> % } ++++++ MirrorCache.obsinfo ++++++ --- /var/tmp/diff_new_pack.6eIJvz/_old 2023-02-16 16:57:14.396299481 +0100 +++ /var/tmp/diff_new_pack.6eIJvz/_new 2023-02-16 16:57:14.408299533 +0100 @@ -1,5 +1,5 @@ name: MirrorCache -version: 1.052 -mtime: 1673512842 -commit: 4b3bfe9c1345b5228f40fb6ad75626a67ab7aee5 +version: 1.056 +mtime: 1675932755 +commit: 5fa14eb5b8ef788ec3f70055537710294b79982b