mirror of
https://github.com/tuna/mirror-web.git
synced 2025-12-25 20:32:46 +00:00
Merge branch 'wip-fancy-status'
Signed-off-by: 王邈 <shankerwangmiao@gmail.com>
This commit is contained in:
commit
9086cd90f6
|
|
@ -1,4 +1,4 @@
|
|||
{% for footer-i in (1...2) %}
|
||||
{% for footer-i in (1..2) %}
|
||||
{% unless page.legacy and footer-i == 1 %}
|
||||
<div id="footerwrap" class="tuna-foot-{{ footer-i }}"{% if page.legacy %} style="visibility: visible;"{% endif %}>
|
||||
<div class="container">
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
<script src="/static/js/bootstrap-select.min.js"></script>
|
||||
{% unless page.legacy %}
|
||||
<script src="/static/js/vue.min.js"></script>
|
||||
<script src="/static/js/timeago.min.js"></script>
|
||||
{% endunless %}
|
||||
<script src="/static/js/markup.min.js"></script>
|
||||
<script src="/static/js/webfont.js"></script>
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ body {
|
|||
font-weight: bold;
|
||||
}
|
||||
#mirror-list {
|
||||
:hover {
|
||||
tr:hover {
|
||||
background-color: $color_row_hover;
|
||||
@include dark{
|
||||
background-color: $color_row_hover_dark;
|
||||
|
|
@ -237,12 +237,18 @@ body {
|
|||
.text-right-xs {
|
||||
text-align: right;
|
||||
}
|
||||
.text-left-xs {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
/* Small devices (tablets, 768px and up) */
|
||||
@media (min-width: 768px) {
|
||||
.text-right-sm {
|
||||
text-align: right;
|
||||
}
|
||||
.text-left-sm {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
/* Medium devices (desktops, 992px and up) */
|
||||
|
|
@ -250,6 +256,9 @@ body {
|
|||
.text-right-md {
|
||||
text-align: right;
|
||||
}
|
||||
.text-left-md {
|
||||
text-align: left;
|
||||
}
|
||||
.table.flat-md {
|
||||
td,tr,th {
|
||||
float: none;
|
||||
|
|
@ -268,10 +277,13 @@ body {
|
|||
.text-right-lg {
|
||||
text-align: right;
|
||||
}
|
||||
.text-left-lg {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 992px) {
|
||||
.table.flat-md {
|
||||
.table.flat-md {
|
||||
word-break: break-all;
|
||||
@media (max-width: 992px) {
|
||||
display: block;
|
||||
td,th,tr,tbody,thead {
|
||||
display: block;
|
||||
|
|
@ -407,3 +419,69 @@ body {
|
|||
display: none;
|
||||
}
|
||||
|
||||
|
||||
// Scrolling columns in status page
|
||||
.tuna-roll {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
> div {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
top: 0;
|
||||
|
||||
animation-timing-function: ease;
|
||||
animation-duration: 1s;
|
||||
animation-iteration-count: 1;
|
||||
}
|
||||
|
||||
@keyframes tuna-roll-enter {
|
||||
0% {
|
||||
transform: translateY(100%);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes tuna-roll-leave {
|
||||
0% {
|
||||
transform: none;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateY(-100%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{% for attrb in (1..2) %}
|
||||
@for $i from 0 to 7 {
|
||||
{% cycle "g1": "[data-tuna-roll-cur", ".row[data-tuna-roll-freeze" %}="#{$i}"] {
|
||||
.tuna-roll > div {
|
||||
animation-name: tuna-roll-leave;
|
||||
transform: translateY(100%);
|
||||
}
|
||||
.tuna-roll > div[data-tuna-roll-seq~="#{$i}"] {
|
||||
animation-name: tuna-roll-enter;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{% cycle "g2": "[data-tuna-roll-cur", ".row[data-tuna-roll-freeze" %}="-1"] {
|
||||
.tuna-roll > div {
|
||||
transform: translateY(100%);
|
||||
animation-name: tuna-roll-leave;
|
||||
animation-duration: 0s;
|
||||
}
|
||||
.tuna-roll > div[data-tuna-roll-seq~="0"] {
|
||||
transform: none;
|
||||
animation-name: tuna-roll-enter;
|
||||
animation-duration: 0s;
|
||||
}
|
||||
}
|
||||
{% endfor %}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ var vmMirList = new Vue({
|
|||
test: "hello",
|
||||
mirrorList: [],
|
||||
filter: "",
|
||||
rawMirrorList: [],
|
||||
},
|
||||
created () {
|
||||
this.refreshMirrorList();
|
||||
|
|
@ -69,57 +70,70 @@ var vmMirList = new Vue({
|
|||
refreshMirrorList () {
|
||||
var self = this;
|
||||
$.getJSON("/static/tunasync.json", (status_data) => {
|
||||
var mirrors = [], mir_data = $.merge(status_data, unlisted);
|
||||
var mir_uniq = {}; // for deduplication
|
||||
|
||||
mir_data.sort((a, b) => { return a.name < b.name ? -1: 1 });
|
||||
|
||||
for(var k in mir_data) {
|
||||
var d = mir_data[k];
|
||||
if (d.status == "disabled") {
|
||||
continue;
|
||||
}
|
||||
if (options[d.name] != undefined ) {
|
||||
d = $.extend(d, options[d.name]);
|
||||
}
|
||||
d.label = label_map[d.status];
|
||||
d.help_url = help_url[d.name];
|
||||
d.is_new = new_mirrors[d.name];
|
||||
d.description = descriptions[d.name];
|
||||
d.show_status = (d.status != "success");
|
||||
if (d.is_master === undefined) {
|
||||
d.is_master = true;
|
||||
}
|
||||
// Strip the second component of last_update
|
||||
if (d.last_update_ts) {
|
||||
let date = new Date(d.last_update_ts * 1000);
|
||||
if (date.getFullYear() > 2000) {
|
||||
d.last_update = `${('000'+date.getFullYear()).substr(-4)}-${('0'+(date.getMonth()+1)).substr(-2)}-${('0'+date.getDate()).substr(-2)}` +
|
||||
` ${('0'+date.getHours()).substr(-2)}:${('0'+date.getMinutes()).substr(-2)}`;
|
||||
} else {
|
||||
d.last_update = "0000-00-00 00:00";
|
||||
}
|
||||
} else {
|
||||
d.last_update = d.last_update.replace(/(\d\d:\d\d):\d\d(\s\+\d\d\d\d)?/, '$1');
|
||||
}
|
||||
if (d.name in mir_uniq) {
|
||||
let other = mir_uniq[d.name];
|
||||
if (other.last_update > d.last_update) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
mir_uniq[d.name] = d;
|
||||
}
|
||||
for (k in mir_uniq) {
|
||||
mirrors.push(mir_uniq[k]);
|
||||
}
|
||||
self.mirrorList = mirrors;
|
||||
var unlisted_mir = unlisted.map(d => processMirrorItem(d))
|
||||
status_data = status_data.map(d => processMirrorItem(d));
|
||||
var mir_data = $.merge(unlisted_mir, status_data);
|
||||
status_data = sortAndUniqMirrors(status_data);
|
||||
mir_data = sortAndUniqMirrors(mir_data).filter(d => !(d.status == "disabled"));
|
||||
self.mirrorList = mir_data;
|
||||
self.rawMirrorList = status_data;
|
||||
setTimeout(() => {self.refreshMirrorList()}, 10000);
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
var stringifyTime = function(ts){
|
||||
var date = new Date(ts * 1000);
|
||||
var str = "";
|
||||
var ago = "";
|
||||
if (date.getFullYear() > 2000) {
|
||||
str = `${('000'+date.getFullYear()).substr(-4)}-${('0'+(date.getMonth()+1)).substr(-2)}-${('0'+date.getDate()).substr(-2)}` +
|
||||
` ${('0'+date.getHours()).substr(-2)}:${('0'+date.getMinutes()).substr(-2)}`;
|
||||
ago = timeago.format(date);
|
||||
} else {
|
||||
str = "0000-00-00 00:00";
|
||||
ago = "Never";
|
||||
}
|
||||
return [str, ago];
|
||||
}
|
||||
|
||||
var sortAndUniqMirrors = function(mirs){
|
||||
mirs.sort((a, b) => { return a.name < b.name ? -1: 1 });
|
||||
return mirs.reduce((acc, cur)=>{
|
||||
if(acc.length > 1 && acc[acc.length - 1].name == cur.name){
|
||||
if(acc[acc.length - 1].last_update_ts && cur.last_update_ts){
|
||||
if(acc[acc.length - 1].last_update_ts < cur.last_update_ts){
|
||||
acc[acc.length - 1] = cur;
|
||||
}
|
||||
} else if(cur.last_update_ts){
|
||||
acc[acc.length - 1] = cur;
|
||||
}
|
||||
}else{
|
||||
acc.push(cur);
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
}
|
||||
|
||||
var processMirrorItem = function(d){
|
||||
if (options[d.name] != undefined ) {
|
||||
d = $.extend(d, options[d.name]);
|
||||
}
|
||||
d.label = label_map[d.status];
|
||||
d.help_url = help_url[d.name];
|
||||
d.is_new = new_mirrors[d.name];
|
||||
d.description = descriptions[d.name];
|
||||
d.show_status = (d.status != "success");
|
||||
if (d.is_master === undefined) {
|
||||
d.is_master = true;
|
||||
}
|
||||
// Strip the second component of last_update
|
||||
[d.last_update, d.last_update_ago] = stringifyTime(d.last_update_ts);
|
||||
[d.last_ended, d.last_ended_ago] = stringifyTime(d.last_ended_ts);
|
||||
[d.next_schedule, d.next_schedule_ago] = stringifyTime(d.next_schedule_ts);
|
||||
return d;
|
||||
}
|
||||
|
||||
var vmIso = new Vue({
|
||||
el: "#isoModal",
|
||||
|
|
|
|||
|
|
@ -1,53 +1,34 @@
|
|||
---
|
||||
---
|
||||
$(document).ready(() => {
|
||||
var mir_tmpl = $("#template").text();
|
||||
function readableFileSize(size) {
|
||||
var units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
||||
var i = 0;
|
||||
while(size >= 1024) {
|
||||
size /= 1024;
|
||||
++i;
|
||||
}
|
||||
return size.toFixed(1) + ' ' + units[i];
|
||||
}
|
||||
$.get("/static/status/disk.json", (d) => {
|
||||
var used_percent = Math.round(d.used_kb * 100 / d.total_kb);
|
||||
$('#disk-usage-bar')
|
||||
.attr("aria-valuenow", used_percent)
|
||||
.css("width", used_percent + "%")
|
||||
.html("<strong>" + readableFileSize(d.used_kb * 1024) + " / " + readableFileSize(d.total_kb * 1024) + "</strong>");
|
||||
});
|
||||
function readableFileSize(size) {
|
||||
var units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
||||
var i = 0;
|
||||
while(size >= 1024) {
|
||||
size /= 1024;
|
||||
++i;
|
||||
}
|
||||
return size.toFixed(1) + ' ' + units[i];
|
||||
}
|
||||
$.get("/static/status/disk.json", (d) => {
|
||||
var used_percent = Math.round(d.used_kb * 100 / d.total_kb);
|
||||
$('#disk-usage-bar')
|
||||
.attr("aria-valuenow", used_percent)
|
||||
.css("width", used_percent + "%")
|
||||
.html("<strong>" + readableFileSize(d.used_kb * 1024) + " / " + readableFileSize(d.total_kb * 1024) + "</strong>");
|
||||
});
|
||||
|
||||
const SCROLL_INTERVAL = 2000;
|
||||
|
||||
window.refreshMirrorList = () => {
|
||||
$.getJSON("/static/tunasync.json", (status_data) => {
|
||||
var mirrors=[], mir_data=status_data;
|
||||
|
||||
mir_data.sort((a, b) => { return a.name < b.name ? -1: 1 });
|
||||
|
||||
for(var k in mir_data) {
|
||||
var d = mir_data[k];
|
||||
if (d.is_master === undefined) {
|
||||
d.is_master = true;
|
||||
}
|
||||
// Strip the second component of last_update
|
||||
if (d.last_update_ts) {
|
||||
let date = new Date(d.last_update_ts * 1000);
|
||||
if (date.getFullYear() > 2000) {
|
||||
d.last_update = `${('000'+date.getFullYear()).substr(-4)}-${('0'+(date.getMonth()+1)).substr(-2)}-${('0'+date.getDate()).substr(-2)}` +
|
||||
` ${('0'+date.getHours()).substr(-2)}:${('0'+date.getMinutes()).substr(-2)}`;
|
||||
} else {
|
||||
d.last_update = "0000-00-00 00:00";
|
||||
}
|
||||
} else {
|
||||
d.last_update = d.last_update.replace(/(\d\d:\d\d):\d\d(\s\+\d\d\d\d)?/, '$1');
|
||||
}
|
||||
mirrors.push(d);
|
||||
}
|
||||
var result = Mark.up(mir_tmpl, {mirrors: mirrors});
|
||||
$('#mirror-list').html(result);
|
||||
});
|
||||
setTimeout(refreshMirrorList, 10000);
|
||||
};
|
||||
refreshMirrorList();
|
||||
var step = 0;
|
||||
const doScroll = function() {
|
||||
const $target = $('#mirror-list');
|
||||
const max = parseInt($target.attr('data-tuna-roll-max'), 10);
|
||||
$('#mirror-list .row:hover:not([data-tuna-roll-freeze])').attr('data-tuna-roll-freeze', step % max);
|
||||
$('#mirror-list .row:not(:hover)[data-tuna-roll-freeze]').removeAttr('data-tuna-roll-freeze');
|
||||
step += 1;
|
||||
if(step < 0) step = 0;
|
||||
$target.attr('data-tuna-roll-cur', step % max);
|
||||
}
|
||||
setInterval(doScroll, SCROLL_INTERVAL);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e=e||self).timeago={})}(this,function(e){"use strict";var r=["second","minute","hour","day","week","month","year"];var a=["秒","分钟","小时","天","周","个月","年"];function t(e,t){n[e]=t}function i(e){return n[e]||n.en_US}var n={},c=[60,60,24,7,365/7/12,12];function o(e){return e instanceof Date?e:!isNaN(e)||/^\d+$/.test(e)?new Date(parseInt(e)):(e=(e||"").trim().replace(/\.\d+/,"").replace(/-/,"/").replace(/-/,"/").replace(/(\d)T(\d)/,"$1 $2").replace(/Z/," UTC").replace(/([+-]\d\d):?(\d\d)/," $1$2"),new Date(e))}function f(e,t){for(var n=e<0?1:0,r=e=Math.abs(e),a=0;e>=c[a]&&a<c.length;a++)e/=c[a];return(0===(a*=2)?9:1)<(e=~~e)&&(a+=1),t(e,a,r)[n].replace("%s",e)}function d(e,t){return(+(t=t?o(t):new Date)-+o(e))/1e3}var s="timeago-id";function l(e){return parseInt(e.getAttribute(s))}var p={},v=function(e){clearTimeout(e),delete p[e]};function h(e,t,n,r){v(l(e));var a=r.relativeDate,i=r.minInterval,o=d(t,a);e.innerText=f(o,n);var u=setTimeout(function(){h(e,t,n,r)},Math.min(1e3*Math.max(function(e){for(var t=1,n=0,r=Math.abs(e);e>=c[n]&&n<c.length;n++)e/=c[n],t*=c[n];return r=(r%=t)?t-r:t,Math.ceil(r)}(o),i||1),2147483647));p[u]=0,function(e,t){e.setAttribute(s,t)}(e,u)}t("en_US",function(e,t){if(0===t)return["just now","right now"];var n=r[~~(t/2)];return 1<e&&(n+="s"),[e+" "+n+" ago","in "+e+" "+n]}),t("zh_CN",function(e,t){if(0===t)return["刚刚","片刻后"];var n=a[~~(t/2)];return[e+" "+n+"前",e+" "+n+"后"]}),e.cancel=function(e){e?v(l(e)):Object.keys(p).forEach(v)},e.format=function(e,t,n){return f(d(e,n&&n.relativeDate),i(t))},e.register=t,e.render=function(e,t,n){var r=e.length?e:[e];return r.forEach(function(e){h(e,function(e){return e.getAttribute("datetime")}(e),i(t),n||{})}),r},Object.defineProperty(e,"__esModule",{value:!0})});
|
||||
85
status.html
85
status.html
|
|
@ -37,37 +37,78 @@ permalink: /status/
|
|||
<thead class="hidden-xs hidden-sm">
|
||||
<tr>
|
||||
<th class="col-xs-12 col-md-2">Name</th>
|
||||
<th class="col-xs-12 col-md-2">Last Update</th>
|
||||
<th class="col-xs-12 col-md-5">Upstream</th>
|
||||
<th class="col-xs-12 col-md-4" colspan=2>Last Update</th>
|
||||
<th class="col-xs-12 col-md-4">Upstream</th>
|
||||
<th class="col-xs-12 col-md-1">Status</th>
|
||||
<th class="col-xs-12 col-md-2">Size</th>
|
||||
<th class="col-xs-12 col-md-1">Size</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="mirror-list">
|
||||
<tbody id="mirror-list" data-tuna-roll-cur="-1" data-tuna-roll-max="6">
|
||||
{% raw %}
|
||||
<tr v-for="mir in rawMirrorList" :class="['row', 'status-'+mir.status, mir.last_ended_ts == mir.last_update_ts ? 'last-succ' : 'last-fail']" :key="mir.name">
|
||||
<td class="col-xs-4">Name</td>
|
||||
<td class="col-md-2 col-xs-8">{{mir.name}}{{mir.is_master ? '' : ' [slave]'}}</td>
|
||||
<td class="col-xs-4">Last Success</td>
|
||||
<td class="col-xs-8 hidden-md hidden-lg">{{mir.last_update}}, {{mir.last_update_ago}}</td>
|
||||
<template v-if="mir.last_ended_ts != mir.last_update_ts">
|
||||
<td class="col-xs-4">Last Attempt</td>
|
||||
<td class="col-xs-8 hidden-md hidden-lg">{{mir.last_ended}}, {{mir.last_ended_ago}}</td>
|
||||
</template>
|
||||
<td class="col-xs-4">Next Sync</td>
|
||||
<td class="col-xs-8 hidden-md hidden-lg" v-if="mir.status != 'syncing'">{{mir.next_schedule}}, {{mir.next_schedule_ago}}</td>
|
||||
<td class="col-xs-8 hidden-md hidden-lg" v-else>Syncing Now</td>
|
||||
<td class="hidden-xs hidden-sm hidden-md hidden-lg"></td>
|
||||
<td class="col-md-2 col-xs-3 rolling-3 text-left-md text-right-xs hidden-xs hidden-sm">
|
||||
<div class="tuna-roll">
|
||||
<template v-if="mir.last_ended_ts == mir.last_update_ts">
|
||||
<div data-tuna-roll-seq="0 1 2 3">Last Successful Sync</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div data-tuna-roll-seq="0">Last Successful Sync</div>
|
||||
<div data-tuna-roll-seq="1">Last Attempted Sync</div>
|
||||
<div data-tuna-roll-seq="2">Last Successful Sync</div>
|
||||
<div data-tuna-roll-seq="3">Last Attempted Sync</div>
|
||||
</template>
|
||||
<div data-tuna-roll-seq="4 5">Next Scheduled Sync</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="hidden-xs hidden-sm hidden-md hidden-lg"></td>
|
||||
<td class="col-md-2 col-xs-5 rolling-6 hidden-xs hidden-sm">
|
||||
<div class="tuna-roll">
|
||||
<template v-if="mir.last_ended_ts == mir.last_update_ts">
|
||||
<div data-tuna-roll-seq="0 1">{{mir.last_update}}</div>
|
||||
<div data-tuna-roll-seq="2 3">{{mir.last_update_ago}}</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div data-tuna-roll-seq="0">{{mir.last_update}}</div>
|
||||
<div data-tuna-roll-seq="1" v-if="mir.last_ended_ts != mir.last_update_ts">{{mir.last_ended}}</div>
|
||||
<div data-tuna-roll-seq="2">{{mir.last_update_ago}}</div>
|
||||
<div data-tuna-roll-seq="3" v-if="mir.last_ended_ts != mir.last_update_ts">{{mir.last_ended_ago}}</div>
|
||||
</template>
|
||||
<template v-if="mir.status == 'syncing'">
|
||||
<div data-tuna-roll-seq="4 5">Syncing Now</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div data-tuna-roll-seq="4">{{mir.next_schedule}}</div>
|
||||
<div data-tuna-roll-seq="5">{{mir.next_schedule_ago}}</div>
|
||||
</template>
|
||||
</div>
|
||||
</td>
|
||||
<td class="col-xs-4">Upstream</td>
|
||||
<td class="col-md-4 col-xs-8">{{mir.upstream}}</td>
|
||||
<td class="col-xs-4">Status</td>
|
||||
<td class="col-md-1 col-xs-8">{{mir.status}}</td>
|
||||
<td class="col-xs-4">Size</td>
|
||||
<td class="col-md-1 col-xs-8">{{mir.size}}</td>
|
||||
</tr>
|
||||
{% endraw %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div><!--/container -->
|
||||
</div><!--/status -->
|
||||
{% include footer.html %}
|
||||
{% raw %}
|
||||
<script id="template" type="x-tmpl-markup">
|
||||
{{mirrors}}
|
||||
<tr class="status-{{status}} row">
|
||||
<td class="col-xs-4">Name</td>
|
||||
<td class="col-md-2 col-xs-8">{{name}}{{if is_master|falsy}}[slave]{{/if}}</td>
|
||||
<td class="col-xs-4">Last Update</td>
|
||||
<td class="col-md-2 col-xs-8">{{last_update}}</td>
|
||||
<td class="col-xs-4">Upstream</td>
|
||||
<td class="col-md-5 col-xs-8">{{upstream}}</td>
|
||||
<td class="col-xs-4">Status</td>
|
||||
<td class="col-md-1 col-xs-8">{{status}}</td>
|
||||
<td class="col-xs-4">Size</td>
|
||||
<td class="col-md-2 col-xs-8">{{size}}</td>
|
||||
</tr>
|
||||
{{/mirrors}}
|
||||
</script>
|
||||
{% endraw %}
|
||||
<script src="/static/js/status.js"></script>
|
||||
<script src="/static/js/index.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
<!--
|
||||
|
|
|
|||
Loading…
Reference in New Issue