Revert back to old script

This commit is contained in:
Morgan 2023-12-01 15:13:24 +09:00
parent 4c2bb9b812
commit 60ec3df42e
27 changed files with 667 additions and 1179 deletions

0
pub/.~tmp~ Normal file
View File

Binary file not shown.

12
scripts/all.sh Executable file
View File

@ -0,0 +1,12 @@
/srv/mirror/scripts/sync.sh archlinux &
/srv/mirror/scripts/sync.sh fedora &
/srv/mirror/scripts/sync.sh debian &
/srv/mirror/scripts/sync.sh ubuntu &
/srv/mirror/scripts/sync.sh kali &
/srv/mirror/scripts/sync.sh manjaro &
/srv/mirror/scripts/sync.sh raspbian &
/srv/mirror/scripts/sync.sh ubuntu_cd &
/srv/mirror/scripts/sync.sh kali_images &
/srv/mirror/scripts/sync.sh debian_cd &
/srv/mirror/scripts/sync.sh linux &
/srv/mirror/scripts/sync.sh gnu &

214
scripts/base.html Normal file
View File

@ -0,0 +1,214 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Morgan's Mirror</title>
<link rel="icon" type="image/x-icon" href="static/favicon.ico">
<script src="static/tailwind.css"></script>
<script>
tailwind.config = {
darkMode: 'class'
}
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
// document.documentElement.classList.add('dark')
// if (localStorage.theme === 'dark') {
document.documentElement.classList.add('dark')
} else {
document.documentElement.classList.remove('dark')
}
</script>
<style>
@font-face {
font-family: 'Source Sans s3';
src: url("static/SourceSans3-Regular.woff2");
}
@font-face {
font-family: 'Source Sans 3';
src: url("static/SourceSans3-Bold.woff2");
font-weight: 700;
}
@font-face {
font-family: 'Source Sans 3';
src: url("static/SourceSans3-Semibold.woff2");
font-weight: 600;
}
@font-face {
font-family: 'Source Sans 3';
src: url("static/SourceSans3-Italic.woff2");
font-style: italic;
}
body {
font-family: 'Source Sans 3';
}
.url {
font-style: italic;
color: rgb(86, 105, 151);
}
</style>
</head>
<body class="text-gray-900 bg-gray-100 py-16 px-6 mx-auto max-w-5xl dark:bg-gray-900">
<!--
<div class="bg-red-500 text-white text-xl font-bold text-center mb-2 w-full fixed top-0 left-0">
MIRROR NOT MANAGED. SYNC JOB IS STILL RUNNING BUT MAY SHUT DOWN SOON.
</div>
-->
<!-- <div class="bg-white text-black border border-black rounded text-lg font-light p-6 pt-7 m-6 w-80 fixed bottom-0 left-0 break-all" id="stopalert">
ISP's blocking/interruption of the network for high-bandwidth uploaders has caused frequent problems, disrupting the entire residential network.<br/>Rather than providing a slow and unreliable mirror, I thought it would be better to shut it down.<br/> I apologize for any confusion, and thank you for using my mirror.<br/>This mirror will be in sync as long as this page is alive.
<button onclick="document.getElementById('stopalert').remove();" class="absolute top-1 right-2">[X]</button>
</div> -->
<header class="mb-10 ml-2">
<h1 class="text-3xl font-bold mb-2 text-black dark:text-white">Welcome to Morgan's mirror archive!</h1>
<h2 class="text-xl text-gray-600 dark:text-gray-300 mb-1">Simple archiving server.</h2>
<h2 class="text-xl text-gray-600 dark:text-gray-300">This mirror is for archiving and NOT for public serving.</h2>
</header>
<main>
<section class="mb-12">
<h2 class="dark:text-gray-200 text-2xl font-semibold mb-5 ml-2">Archive List</h2>
<div id="mirrorList" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-2 gap-4">
<div class="bg-white dark:bg-gray-800 col-span-1 md:col-span-2 shadow-lg rounded-lg p-5">
<h3 class="dark:text-white text-lg font-semibold mb-1"><a href="/archlinux"
class="text-blue-900 hover:text-blue-600 dark:text-blue-300 dark:hover:text-blue-400">ArchLinux (x86_64)</a></h3>
<p class="text-gray-600 dark:text-gray-300">Last Updated: @@archlinux@@</p>
<p class="text-gray-600 dark:text-gray-300 mt-1">Source: <span
class="url">rsync://mirrors.xtom.de/archlinux/</span></p>
</div>
<div class="bg-white dark:bg-gray-800 col-span-1 shadow-lg rounded-lg p-5">
<h3 class="dark:text-white text-lg font-semibold mb-1"><a href="/ubuntu"
class="text-blue-900 hover:text-blue-600 dark:text-blue-300 dark:hover:text-blue-400">Ubuntu</a></h3>
<p class="text-gray-600 dark:text-gray-300">Last Updated: @@ubuntu@@</p>
<p class="text-gray-600 dark:text-gray-300 mt-1">Source: <span
class="url">rsync://rsync.archive.ubuntu.com/ubuntu/</span></p>
</div>
<div class="bg-white dark:bg-gray-800 col-span-1 shadow-lg rounded-lg p-5">
<h3 class="dark:text-white text-lg font-semibold mb-1"><a href="/ubuntu-cd"
class="text-blue-900 hover:text-blue-600 dark:text-blue-300 dark:hover:text-blue-400">Ubuntu Releases</a></h3>
<p class="text-gray-600 dark:text-gray-300">Last Updated: @@ubuntu_cd@@</p>
<p class="text-gray-600 dark:text-gray-300 mt-1">Source: <span
class="url">rsync://releases.ubuntu.com/releases/</span></p>
</div>
<div class="bg-white dark:bg-gray-800 col-span-1 shadow-lg rounded-lg p-5">
<h3 class="dark:text-white text-lg font-semibold mb-1"><a href="/debian"
class="text-blue-900 hover:text-blue-600 dark:text-blue-300 dark:hover:text-blue-400">Debian</a></h3>
<p class="text-gray-600 dark:text-gray-300">Last Updated: @@debian@@</p>
<p class="text-gray-600 dark:text-gray-300 mt-1">Source: <span class="url">rsync://ftp.halifax.rwth-aachen.de/debian/</span></p>
</div>
<div class="bg-white dark:bg-gray-800 col-span-1 shadow-lg rounded-lg p-5">
<h3 class="dark:text-white text-lg font-semibold mb-1"><a href="/debian-cd"
class="text-blue-900 hover:text-blue-600 dark:text-blue-300 dark:hover:text-blue-400">Debian Releases</a></h3>
<p class="text-gray-600 dark:text-gray-300">Last Updated: @@debian_cd@@</p>
<p class="text-gray-600 dark:text-gray-300 mt-1">Source: <span class="url">rsync://ftp.lanet.kr/debian-cd/</span></p>
</div>
<div class="bg-white dark:bg-gray-800 col-span-1 shadow-lg rounded-lg p-5">
<h3 class="dark:text-white text-lg font-semibold mb-1"><a href="/kali"
class="text-blue-900 hover:text-blue-600 dark:text-blue-300 dark:hover:text-blue-400">Kali Linux</a></h3>
<p class="text-gray-600 dark:text-gray-300">Last Updated: @@kali@@</p>
<p class="text-gray-600 dark:text-gray-300 mt-1">Source: <span class="url">rsync://archive.kali.org/</span></p>
</div>
<div class="bg-white dark:bg-gray-800 col-span-1 shadow-lg rounded-lg p-5">
<h3 class="dark:text-white text-lg font-semibold mb-1"><a href="/kali-images"
class="text-blue-900 hover:text-blue-600 dark:text-blue-300 dark:hover:text-blue-400">Kali Linux Images</a></h3>
<p class="text-gray-600 dark:text-gray-300">Last Updated: @@kali_images@@</p>
<p class="text-gray-600 dark:text-gray-300 mt-1">Source: <span class="url">rsync://archive.kali.org/</span></p>
</div>
<div class="bg-white dark:bg-gray-800 col-span-1 md:col-span-2 shadow-lg rounded-lg p-5">
<h3 class="dark:text-white text-lg font-semibold mb-1"><a href="/manjaro"
class="text-blue-900 hover:text-blue-600 dark:text-blue-300 dark:hover:text-blue-400">Manjaro</a></h3>
<p class="text-gray-600 dark:text-gray-300">Last Updated: @@manjaro@@</p>
<p class="text-gray-600 dark:text-gray-300 mt-1">Source: <span
class="url">rsync://ftp.riken.jp/manjaro/</span></p>
</div>
<div class="bg-white dark:bg-gray-800 col-span-1 md:col-span-2 shadow-lg rounded-lg p-5">
<h3 class="dark:text-white text-lg font-semibold mb-1"><a href="/raspbian"
class="text-blue-900 hover:text-blue-600 dark:text-blue-300 dark:hover:text-blue-400">Raspbian</a></h3>
<p class="text-gray-600 dark:text-gray-300">Last Updated: @@raspbian@@</p>
<p class="text-gray-600 dark:text-gray-300 mt-1">Source: <span
class="url">rsync://archive.raspbian.org/archive/</span></p>
</div>
<div class="bg-white dark:bg-gray-800 col-span-1 md:col-span-2 shadow-lg rounded-lg p-5">
<h3 class="dark:text-white text-lg font-semibold mb-1"><a href="/fedora"
class="text-blue-900 hover:text-blue-600 dark:text-blue-300 dark:hover:text-blue-400">Fedora</a></h3>
<p class="text-gray-600 dark:text-gray-300">Last Updated: @@fedora@@</p>
<p class="text-gray-600 dark:text-gray-300 mt-1">Source: <span
class="url">rsync://dl.fedoraproject.org/fedora-enchilada/linux/</span></p>
</div>
<div class="bg-white dark:bg-gray-800 col-span-1 md:col-span-2 shadow-lg rounded-lg p-5">
<h3 class="dark:text-white text-lg font-semibold mb-1"><a href="/ubuntu-old"
class="text-blue-900 hover:text-blue-600 dark:text-blue-300 dark:hover:text-blue-400">Ubuntu Releases (Old)</a></h3>
<p class="text-gray-600 dark:text-gray-300">Last Updated: @@ubuntu_cd_old@@</p>
<p class="text-gray-600 dark:text-gray-300 mt-1">Source: <span
class="url">rsync://old-releases.ubuntu.com/releases/</span></p>
</div>
<div class="mb-2"></div>
<div class="bg-white dark:bg-gray-800 col-span-1 md:col-span-2 shadow-lg rounded-lg p-5">
<h3 class="dark:text-white text-lg font-semibold mb-1"><a href="/gnu"
class="text-blue-900 hover:text-blue-600 dark:text-blue-300 dark:hover:text-blue-400">GNU</a></h3>
<p class="text-gray-600 dark:text-gray-300">Last Updated: @@gnu@@</p>
<p class="text-gray-600 dark:text-gray-300 mt-1">Source: <span
class="url">rsync://ftp.gnu.org/gnu/</span></p>
</div>
<div class="bg-white dark:bg-gray-800 col-span-1 md:col-span-2 shadow-lg rounded-lg p-5">
<h3 class="dark:text-white text-lg font-semibold mb-1"><a href="/linux"
class="text-blue-900 hover:text-blue-600 dark:text-blue-300 dark:hover:text-blue-400">Linux Kernel</a></h3>
<p class="text-gray-600 dark:text-gray-300">Last Updated: @@linux@@</p>
<p class="text-gray-600 dark:text-gray-300 mt-1">Source: <span
class="url">rsync://kernel.org</span></p>
</div>
<div class="bg-white dark:bg-gray-800 col-span-1 md:col-span-2 shadow-lg rounded-lg p-5">
<h3 class="dark:text-white text-lg font-semibold mb-1"><a href="/cd-image"
class="text-blue-900 hover:text-blue-600 dark:text-blue-300 dark:hover:text-blue-400">Image Files</a></h3>
</div>
</div>
</section>
<section class="ml-2">
<h2 class="dark:text-gray-200 text-2xl font-semibold mb-4">Server Details</h2>
<p class="mb-2 text-gray-800 dark:text-gray-500"><strong>Location:</strong> Seoul, Korea</p>
<p class="mb-2 text-gray-800 dark:text-gray-500"><strong>Bandwidth:</strong> 100Mbps</p>
<p class="mb-2 text-gray-800 dark:text-gray-500"><strong>Storage size:</strong> 16TiB</p>
<p class="mb-2 text-gray-800 dark:text-gray-500"><strong>Sync delay:</strong> 4hrs </p>
<p class="mb-2 text-gray-800 dark:text-gray-500"><strong>Contact:</strong> <a href="mailto:mirror@morgan.kr">mirror@morgan.kr</a></p>
</section>
<button
class="fixed bottom-6 right-6 bg-blue-200 hover:bg-blue-300 text-blue-700 dark:bg-blue-800 dark:hover:bg-blue-600 dark:text-white w-10 h-10 rounded-full flex items-center justify-center"
onclick="toggleDarkMode()">
+
</button>
</main>
<footer class="mt-10 text-center text-gray-600">
<a href="mailto:mirror@morgan.kr">&copy;MORGAN.KR</a>
</footer>
<script>
function toggleView(elementId) {
const element = document.getElementById(elementId);
element.style.display = element.style.display === "none" ? "block" : "none";
}
function toggleDarkMode() {
if (document.documentElement.classList.contains('dark')) {
document.documentElement.classList.remove('dark');
localStorage.theme = 'light';
} else {
document.documentElement.classList.add('dark');
localStorage.theme = 'dark';
}
}
</script>
</body>
</html>

View File

@ -1,131 +0,0 @@
BASE_DIR: /srv/mirror
# Any path under this must be in relative of BASE_DIR.
# Script doesnt do any kind of outbound check, so make sure to check all paths.
global:
scripts:
pre:
post:
log: logs/sync.log
log_dir: logs/
data_dir: pub/
sync:
- ALL
defaults:
type: rsync
scripts:
pre:
fail:
- scripts/alert.sh
post:
- scripts/index.py ${config}
log: logs/${path}.log
delay: 4
rsync:
options: "-rtlHpv --chmod=D0755,F0644 --partial --hard-links --safe-links --stats --delete --delete-after --delay-updates --max-delete=70000"
exclude:
- ".*.??????"
- ".~tmp~/"
- "Packages*"
- "Sources*"
- "Release*"
- "*.links.tar.gz*"
- "/other"
- "/sources"
repos:
ARCHLINUX:
url: rsync://mirrors.xtom.de/archlinux/
name: "ArchLinux (x86_64)"
path: archlinux
UBUNTU:
url: rsync://rsync.archive.ubuntu.com/ubuntu/
name: "Ubuntu"
path: ubuntu
UBUNTU_CD:
url: rsync://releases.ubuntu.com/releases/
name: "Ubuntu Releases"
delay: 6
path: ubuntu_cd
UBUNTU_CD_OLD:
url: rsync://old-releases.ubuntu.com/releases/
name: "Ubuntu Releases (Old)"
delay: 12
path: ubuntu_cd_old
DEBIAN:
url: rsync://mirrors.xtom.jp/
type: ftpsync
name: Debian
path: debian
scripts:
# pre: scripts/pre_debian.sh
last_sync: "2023-10-12T10:58Z"
DEBIAN_CD:
url: rsync://ftp.lanet.kr/debian-cd/
delay: 6
name: "Debian CD"
path: debian_cd
FEDORA:
url: rsync://dl.fedoraproject.org/fedora-enchilada/linux/
name: "Fedora"
path: fedora
RASPBIAN:
url: rsync://archive.raspbian.org/archive/
delay: 6
name: "Raspbian"
path: raspbian
MANJARO:
url: rsync://ftp.riken.jp/manjaro/
name: Manjaro
path: manjaro
ARCHLINUXARM:
name: "ArchLinux (ARM)"
path: archlinuxarm
url: http://jp.mirror.archlinuxarm.org/
type: http
delay: 12
ASAHILINUX:
name: "AsahiLinux"
path: asahilinux
url: https://cdn.asahilinux.org/
type: http
delay: 12
GNU:
name: "GNU"
path: gnu
url: rsync://ftp.gnu.org/gnu/
delay: 12
LINUX:
name: "Linux Kernel"
path: linux
url: rsync://rsync.kernel.org/pub/
delay: 12
index:
- ARCHLINUX
- UBUNTU UBUNTU_CD
- DEBIAN DEBIAN_CD
- FEDORA
- MANJARO
- RASPBIAN
- DIVIDER
- ARCHLINUXARM
- ASAHILINUX
- GNU
- LINUX
- UBUNTU_CD_OLD

View File

@ -1,12 +1,50 @@
MIRRORNAME="${DEB_HOST}"
TO="${DEB_PATH}"
########################################################################
########################################################################
## This is a sample configuration file for the ftpsync mirror script. ##
## Only options most users may need are included. For documentation ##
## and all available options see ftpsync.conf(5). ##
########################################################################
########################################################################
echo ${BASE_DIR}
echo ${DATA_DIR}
MIRRORNAME="mirror.morgan.kr"
TO="${DATA_DIR}/debian"
# MAILTO="$LOGNAME"
HUB=false
RSYNC_HOST="${DEB_UPSTREAM}"
########################################################################
## Connection options
########################################################################
RSYNC_HOST="mirrors.xtom.jp"
RSYNC_PATH="debian"
INFO_MAINTAINER="${DEB_ADMIN}"
INFO_COUNTRY="${DEB_COUNTRY}"
INFO_LOCATION="${DEB_LOCATION}"
INFO_THROUGHPUT=1Gbps
LOGDIR="${DEB_LOG}"
# RSYNC_USER=
# RSYNC_PASSWORD=
########################################################################
## Mirror information options
########################################################################
INFO_MAINTAINER="Morgan <mirror@morgan.kr>"
# INFO_SPONSOR="Example <https://example.com>"
INFO_COUNTRY=KR
INFO_LOCATION="Seoul, Korea"
INFO_THROUGHPUT=1Gb
########################################################################
## Include and exclude options
########################################################################
# ARCH_INCLUDE=
# ARCH_EXCLUDE=
########################################################################
## Log option
########################################################################
LOGDIR="${BASE_DIR}/logs"
UIPRETRIES=9
TRACEHOST="${DEB_HOST}"
TRACEHOST="mirror.morgan.kr"

View File

@ -1,81 +0,0 @@
import requests, json, sys
from bs4 import BeautifulSoup as bs4
def tree(idx: int, flag = False) -> str:
idx += 1
if idx == 1:
if flag :
return "├────"
return "├──"
elif idx == 2:
if flag :
return "│ ├────"
return "│ ├──"
else:
if flag :
return "" * (idx - 2) + "├────"
return "" * (idx - 2) + "├──"
def parseIndex(url, base = "", idx = 0, dirs = [], ret = []):
print(f"D {(tree(idx)+url):<40} {''.join(dirs):>80}")
html = bs4(requests.get(base + url).text, features="html.parser")
hrefs = [a["href"] for a in html.find_all('a')]
hrefs = [i for i in hrefs if i[0] != '?']
fls = []
for href in hrefs:
if href[-1] == "/": # if dir
if href[0] != "/" and href[0] != ".":
parseIndex(href, base + url, idx + 1, dirs + [href], ret)
else:
if href[0:2] == "./" and "/" not in href[2:]:
href = href[2:]
assert "/" not in href
print(f"F {(tree(idx, 1)+href):<80}") # if file
fls.append(href)
while len(ret) <= idx:
ret.append({})
if ''.join(dirs) not in ret[idx].keys():
ret[idx][''.join(dirs)] = []
for fl in fls:
ret[idx][''.join(dirs)].append({fl: base + url + fl})
return ret
if __name__ == "__main__":
if len(sys.argv) != 3 and len(sys.argv) != 4:
print("Usage: createFetch.py [URL] [Path] (fetch)")
sys.exit()
if "http" in sys.argv[1]:
if input(f"[*] Download from {sys.argv[1]}? ") in "Yy":
url = sys.argv[1]
else:
sys.exit()
else:
print("[*] Not supported")
sys.exit()
bpath = sys.argv[2]
assert(bpath[-1] == '/')
print()
print(f"[*] Downloading File list from {url} with base path {bpath}")
files = parseIndex('', base = url, dirs = [bpath])
filename = url.split('/')[2]+'.fetch'
if len(sys.argv) == 4:
filename = sys.argv[3]
with open(filename, 'w') as f:
f.write(json.dumps(files, indent=4))
print()
print(f"[*] Saved to {filename}.")

View File

@ -1,114 +0,0 @@
# import asyncio, aiohttp, aiofiles
import json, os, sys
import requests, time
# from tqdm import tqdm
def byteSize(size):
pr = ""
if size > 1024:
size = round(size / 1024, 1)
pr = "K"
if size > 1024:
size = round(size / 1024, 1)
pr = "M"
if size > 1024:
size = round(size / 1024, 1)
pr = "G"
return f"{size}{pr}"
if len(sys.argv) != 2:
print("Usage: getFiles.py [fetch file]")
sys.exit()
listf = sys.argv[1]
if not os.path.exists(listf):
print("There is no such file..")
sys.exit()
print(f"Downloading from fetchFile {listf}")
def spchr(string, num = 25, pad = 0):
if len(string) > num:
string = string[0:20] + ".." + string[-4:]
if pad > num:
string = string + ' '*(pad-len(string))
return string
with open(listf, 'r') as f:
jstr = json.loads(f.read())
for stage in jstr:
for redirs in stage.keys():
der = redirs.split('/')
for i in range(len(der)):
dpath = '/'.join(der[0:i])
if not os.path.exists(dpath) and dpath:
print(f"[*] Making new directory {dpath}")
os.mkdir(dpath)
for stage in jstr:
for dosta in stage:
print(f"[*] Downloading path {dosta}")
fpaths = []
furls = []
for fls in stage[dosta]:
fna = list(fls.keys())[0]
fpa = fls[fna]
fpaths.append(dosta + fna)
furls.append(fpa)
assert len(fpaths) == len(furls)
# pbar = tqdm(total=len(fpaths))
# print(furls)
dlist = []
for url, path in zip(furls, fpaths):
if os.path.exists(path):
flen = os.path.getsize(path)
wlen = requests.get(url, stream=True).headers['Content-length']
if int(flen) != int(wlen):
dlist.append((path, url))
else:
dlist.append((path, url))
for path, url in dlist:
print(f"[*] Fetch {url} ", end = "")
wfil = requests.get(url, stream=True)
wlen = wfil.headers['Content-length']
print(byteSize(int(wlen)))
with open(path, 'wb') as f:
if wlen is None: # no content length header
f.write(wfil.content)
else:
# pbar = tqdm(total=int(wlen), desc=spchr(' '+path, pad = 30))
for data in wfil.iter_content(chunk_size=4096):
f.write(data)
# print(".", end = "")
# pbar.update(len(data))
# print()
# async def getHTTP(session, url):
# async with session.get(url) as resp:
# try:
# reqa = await resp.read()
# # pbar.update(1)
# return reqa
# except:
# print(url)
# return
# async def agetReq():
# async with aiohttp.ClientSession() as session:
# tasks = []
# for url in furls:
# tasks.append(asyncio.ensure_future(getHTTP(session, url)))
# getReqs = await asyncio.gather(*tasks)
# for i, getReq in enumerate(getReqs):
# async with aiofiles.open(fpaths[i], mode='wb') as handle:
# await handle.write(getReq)
# asyncio.run(agetReq())

View File

@ -1,86 +1,67 @@
#!/usr/bin/python3
#!/usr/bin/python
import os, sys
import yaml
import jinja2
import os, sys, re
import datetime
# def get_last_sync(repo_name):
# if os.path.exists(SYNC_LOG):
# with open(SYNC_LOG, 'r') as log_file:
# log_all = reversed(log_file.readlines())
# for logline in log_all:
# dist, time, stat = logline.split()
# if repo_name == dist and stat =="SUCCESS":
# return datetime.datetime.strptime(time, '%Y-%m-%dT%H:%MZ').strftime("%Y-%m-%d %H:%M")
# return "Not Synced"
base_path = sys.argv[1]
out_path = sys.argv[2]
assert os.path.exists(base_path)
# main()
log_path = os.path.join(base_path, "logs/all.log")
assert os.path.exists(log_path)
if __name__=="__main__":
html_path = os.path.join(base_path, "scripts/base.html")
index = os.path.join(out_path, "index.html")
assert os.path.exists(html_path)
if len(sys,argv) == 3:
print(sys.argv)
with open(log_path, 'r') as f:
log_file = f.read().splitlines()
log_file.reverse()
CONFIG_PATH = sys.argv[1]
with open(html_path, 'r') as f:
html_file = f.read()
dists = re.findall("@@([^@@]+)@@", html_file)
with open(CONFIG_PATH, 'r') as f:
config = yaml.safe_load(f)
print(dists)
logs = {}
for dist in dists:
logs[dist] = []
BASE_DIR = config.get("BASE_DIR", ".")
THEME = "new"
TEMPLATES_DIR = os.path.join(os.path.join(BASE_DIR, "scripts/templates"), THEME)
LOG_DIR = os.path.join(BASE_DIR, config['global'].get("log_dir", "logs"))
OUTPUT_PATH = os.path.join(os.path.join(BASE_DIR, config['global'].get("data_dir", ".")), "index.html")
for dist in logs:
for logline in log_file:
time, stat, disk = logline.split(" ")
if stat == "DONE" and dist == disk:
logs[dist] = datetime.datetime.strptime(time, '%Y%m%d_%H%M').strftime("%Y-%m-%d %H:%M")
break
logs[dist] = "Not Synced"
env = jinja2.Environment(loader=jinja2.FileSystemLoader(TEMPLATES_DIR))
base_template = env.get_template('base.html')
full_template = env.get_template('full.html')
half_template = env.get_template('half.html')
stats = {}
for dist in dists:
for logline in log_file:
time, stat, disk = logline.split(" ")
if dist == disk:
stats[dist] = stat, datetime.datetime.strptime(time, '%Y%m%d_%H%M').strftime("%Y-%m-%d %H:%M")
break
stats[dist] = (-1, "Not Synced")
main_repos = []
additional_repos = []
print(logs)
print(stats)
DIV = 0
for line in config['index']:
if line == "DIVIDER":
DIV = 1
continue
repos_line = line.split()
for repo_name in repos_line:
repo_data = config['repos'].get(repo_name)
if not repo_data:
continue
context = {
'path': repo_data['path'],
'name': repo_data['name'],
# 'lastsync': get_last_sync(repo_name),
'lastsync': repo_data.get('lastsync', "Not Synced"),
'upstream': repo_data['url']
}
print(context)
if len(repos_line) > 1:
(main_repos if not DIV else additional_repos).append(half_template.render(**context))
for dist in dists:
stat, tt = stats[dist]
if stat == "ERROR":
stat = f" (Error @ {tt})"
elif stat == "STARTED":
stat = f" (Running @ {tt})"
else:
(main_repos if not DIV else additional_repos).append(full_template.render(**context))
stat = ""
html_output = base_template.render(
repos="\n".join(main_repos),
repos_more="\n".join(additional_repos)
)
if stat:
stat = f"</p><p class=\"text-gray-500 dark:text-gray-400\">{stat}</p>"
html_file = html_file.replace(f"@@{dist}@@", f"{logs[dist]}{stat}")
try:
with open(OUTPUT_PATH, 'w') as f:
f.write(html_output)
with open(index, 'w') as f:
f.write(html_file)
except:
if len(sys.argv) == 2:
if os.path.exists(sys.argv[1]):
if not os.path.isdir(sys.argv[1]):
print(f"Writing to {sys.argv[1]}")
with open(sys.argv[1], 'w') as f:
f.write(html_output)
else:
print(html_output)
print("Written to index.html")

View File

@ -1,91 +0,0 @@
user user;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 768;
# multi_accept on;
}
http {
##
# Basic Settings
##
sendfile on;
tcp_nopush on;
types_hash_max_size 2048;
# server_tokens off;
# server_names_hash_bucket_size 64;
# server_name_in_redirect off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
##
# SSL Settings
##
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;
##
# Logging Settings
##
log_format main_ext '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'"$host" sn="$server_name" ' 'rt=$request_time '
'ua="$upstream_addr" us="$upstream_status" '
'ut="$upstream_response_time" ul="$upstream_response_length" '
'cs=$upstream_cache_status' ;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
##
# Gzip Settings
##
gzip on;
# gzip_vary on;
# gzip_proxied any;
# gzip_comp_level 6;
# gzip_buffers 16 8k;
# gzip_http_version 1.1;
# gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
##
# Virtual Host Configs
##
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
#mail {
# # See sample authentication script at:
# # http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript
#
# # auth_http localhost/auth.php;
# # pop3_capabilities "TOP" "USER";
# # imap_capabilities "IMAP4rev1" "UIDPLUS";
#
# server {
# listen localhost:110;
# protocol pop3;
# proxy on;
# }
#
# server {
# listen localhost:143;
# protocol imap;
# proxy on;
# }
#}

View File

@ -1,21 +0,0 @@
server {
listen 80 ;
listen [::]:80 ;
server_name repo.city;
access_log /var/log/nginx/repo-city-access.log main_ext;
error_log /var/log/nginx/repo-city-error.log warn;
root /srv/mirror;
location /scripts/ {
return 404;
}
location / {
autoindex on;
fancyindex on;
}
}

View File

@ -1,11 +0,0 @@
server {
listen 127.0.0.1:80;
server_name 127.0.0.1;
location /nginx_status {
stub_status on;
access_log off;
allow 127.0.0.1;
deny all;
}
}

230
scripts/ngparse Executable file
View File

@ -0,0 +1,230 @@
#!/usr/bin/python3
import re, math, os, sys, datetime
path_list = ["archlinux", "/archlinuxarm", "/asahilinux",
"/cd-image", "/debian", "/debian-cd", "/fedora",
"/gnu", "/index.html", "/kali", "/kali-images",
"/linux", "/manjaro", "/raspbian", "/static",
"/ubuntu", "/ubuntu-cd", "/ubuntu-old", "/"]
def byte_human(size_bytes):
if size_bytes == 0:
return "0B"
size_name = ("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB")
i = int(math.floor(math.log(size_bytes, 1024)))
p = math.pow(1024, i)
s = round(size_bytes / p, 2)
return "%s%s" % (s, size_name[i])
def parse_req_line(http_entry):
req = {}
http_parsed = http_entry.split()
if http_parsed:
if http_parsed[0] in ["HEAD", "POST", "GET", "OPTION"]:
req["method"] = http_parsed[0]
if http_parsed[1][0] == "/":
req["path"] = http_parsed[1]
return req
def get_path_parent(path):
path = path.split("?")[0].split("&")[0].split("/")
if len(path) == 1:
return "/" + path[0]
else:
return "/".join(path[:2])
def get_date_range(all_log):
dates = sorted([ i[2] for i in all_log ])
dates = [datetime.datetime.strptime(i, "%d/%b/%Y:%H:%M:%S %z").strftime("%m/%d %H:%M:%S") for i in dates]
print(f"Date: {dates[0]} ~ {dates[-1]}")
def parse_log_entry(entry):
pattern = r'([\d\.]+) - (\S+) \[(.*?)\] "(.*?)" (\d+) (\d+) "(.*?)" "(.*?)" "(.*?)" "(.*?)" sn="(.*?)" rt=([\d\.]+) [^\n]+'
match = re.match(pattern, entry)
if not match:
return None
lm = list(match.groups())
assert len(lm) == 12
if "HTTP" not in lm[3]:
return
log_entry = {}
log_entry["client"] = lm[0]
log_entry["user"] = lm[1]
log_entry["time"] = datetime.datetime.strptime(lm[2], "%d/%b/%Y:%H:%M:%S %z")
log_entry["req"] = lm[3]
log_entry["status"] = lm[4]
log_entry["bytes"] = lm[5]
log_entry["referer"] = lm[6]
log_entry["ua"] = lm[7]
log_entry["forward"] = lm[8]
log_entry["host"] = lm[9]
log_entry["server"] = lm[10]
log_entry["reqtime"] = lm[11]
return log_entry
def get_all_log_entry(log_file):
with open(log_file, "r") as f:
full_log = f.read().split("\n")
log_entries = [ i for i in [ parse_log_entry(log) for log in full_log[:-1] ] if i ]
return log_entries
def main(log_file, logs=[]):
all_logs = get_all_log_entry(log_file) if not logs else logs
log_by_date = {}
for entry in all_logs:
date_day = datetime.datetime.strftime(entry.get("time"), "%Y-%m-%d")
if date_day not in log_by_date:
log_by_date[date_day] = []
log_by_date[date_day].append(entry)
for day in log_by_date:
paths = {}
for entry in log_by_date[day]:
req = parse_req_line(entry.get("req"))
if path := req.get("path"):
path_parent = get_path_parent(path)
if path_parent not in path_list:
continue
if path_parent not in paths:
paths[path_parent] = [0, 0]
paths[path_parent][0] += int(entry.get("bytes"))
paths[path_parent][1] += 1
print(day)
print('-'*36)
print(f'{"Path":<14} {"Count":<10} Transfer')
print('-'*36)
for path in sorted(paths):
print(f"{path:<14} {paths[path][1]:<10} {byte_human(paths[path][0])}")
print()
def main_geo(log_file, logs = []):
import geoip2.database
reader = geoip2.database.Reader('GeoLite2-Country.mmdb')
all_logs = get_all_log_entry(log_file) if not logs else logs
c = len(all_logs)
# print(c)
geolocstat = {}
for n, log in enumerate(all_logs):
req = parse_req_line(log.get("req"))
if path := req.get("path"):
path_parent = get_path_parent(path)
try:
geoloc = reader.country(log['client']).country.iso_code
except:
geoloc = "XX"
print(f"[{int(100*n/c):>3}%] {geoloc} {log['client']:>15} {byte_human(int(log['bytes'])):>8} {path_parent}", file=sys.stderr)
if geoloc not in geolocstat:
geolocstat[geoloc] = 0
geolocstat[geoloc] += int(log['bytes'])
print(file=sys.stderr)
sortdict = lambda x : {k: v for k, v in sorted(x.items(), key=lambda item: item[1], reverse=True)}
for geo in sortdict(geolocstat):
print(geo, byte_human(geolocstat[geo]))
def main_date(log_file, logs=[]):
all_logs = get_all_log_entry(log_file) if not logs else logs
date_sorted = (sorted(all_logs, key=(lambda x: x['time'])))
st = datetime.datetime.strftime(date_sorted[0]['time'], "%Y-%m-%d %H:%M:%S")
ed = datetime.datetime.strftime(date_sorted[-1]['time'], "%Y-%m-%d %H:%M:%S")
total_bytes = sum([int(x['bytes']) for x in all_logs])
print(f"------- Log {log_file} -------\n Date: {st} ~ {ed}\n Entry count: {len(all_logs)}\n Total bytes: {byte_human(total_bytes)}")
def main_html(log_file):
all_logs = get_all_log_entry(log_file)
###
date_sorted = (sorted(all_logs, key=(lambda x: x['time'])))
st = datetime.datetime.strftime(date_sorted[0]['time'], "%Y-%m-%d %H:%M:%S")
ed = datetime.datetime.strftime(date_sorted[-1]['time'], "%Y-%m-%d %H:%M:%S")
total_bytes = sum([int(x['bytes']) for x in all_logs])
print(f"-------- Log Info --------\nDate: {st} ~ {ed}\nEntry count: {len(all_logs)}\nTotal bytes: {byte_human(total_bytes)}\n")
###
print("-------- By Path ---------")
paths = {}
for entry in all_logs:
req = parse_req_line(entry.get("req"))
if path := req.get("path"):
path_parent = get_path_parent(path)
if path_parent not in path_list:
continue
if path_parent not in paths:
paths[path_parent] = [0, 0]
paths[path_parent][0] += int(entry.get("bytes"))
paths[path_parent][1] += 1
for path in sorted(paths):
print(f"{path:<14} {paths[path][1]:<10} {byte_human(paths[path][0])}")
print()
###
print("------- By Country -------")
import geoip2.database
reader = geoip2.database.Reader('/srv/mirror/scripts/GeoLite2-Country.mmdb')
geolocstat = {}
for n, log in enumerate(all_logs):
req = parse_req_line(log.get("req"))
if path := req.get("path"):
path_parent = get_path_parent(path)
try:
geoloc = reader.country(log['client']).country.iso_code
except:
geoloc = "XX"
if geoloc not in geolocstat:
geolocstat[geoloc] = 0
geolocstat[geoloc] += int(log['bytes'])
geos = []
sortdict = lambda x : {k: v for k, v in sorted(x.items(), key=lambda item: item[1], reverse=True)}
for geo in sortdict(geolocstat):
if geo:
geos.append(f"{geo} {byte_human(geolocstat[geo])}")
print('\n'.join(geos[:min(len(geos),7)]))
print("--------------------------")
if __name__=="__main__":
if len(sys.argv) != 3:
print("Error: ngparse {stat,parse,geo} [ log file ]\n* log file must have extended format.")
exit()
if os.path.exists(sys.argv[2]):
logfile = sys.argv[2]
else:
print("Error: File doesnt exists.")
exit
if sys.argv[1] == "stat":
main(logfile)
elif sys.argv[1] == "geo":
main_geo(logfile)
elif sys.argv[1] == "date":
main_date(logfile)
elif sys.argv[1] == "html":
main_html(logfile)

18
scripts/stat.sh Executable file
View File

@ -0,0 +1,18 @@
#!/bin/bash
echo "<h3><bold>$(date +%Y%m%d)</bold></h3><h4><bold>Log</bold></h4>"> /srv/mirror/pub/stat/stat.html
echo "<pre>$(/srv/mirror/scripts/ngparse html /var/log/nginx/mirror/access.log)</pre>" >> /srv/mirror/pub/stat/stat.html
echo "<h4><bold>Traffic</bold></h4>" >> /srv/mirror/pub/stat/stat.html
echo "<pre>$(vnstat -i enp1s0 --days 10 | sed 's/ //' | tail -n +4)</pre>" >> /srv/mirror/pub/stat/stat.html
for i in /var/log/nginx/mirror/access.log-*;
do
DATE=${i#*-}
echo $DATE
if [ ! -f /srv/mirror/pub/stat/$DATE.html ];
then
echo $DATE.html;
echo "<date>$DATE</date>" > /srv/mirror/pub/stat/$DATE.html;
echo "<pre>$(/srv/mirror/scripts/ngparse html $i)</pre>" >> /srv/mirror/pub/stat/$DATE.html
echo "<br/><br/>" >> /srv/mirror/pub/stat/$DATE.html
fi
done

View File

@ -1,301 +1,110 @@
#!/bin/bash
# Default locations.
CONFIG="./scripts/config.yml"
YAML="./scripts/yq"
TIMENOW=$(date '+%Y%m%d_%H%M')
BASE_DIR="/srv/mirror"
ALERT="$BASE_DIR/scripts/alert.sh"
DATA_DIR="$BASE_DIR/pub"
function usage() {
cat <<EOF
Usage: $0 [-c config_path] [-y yq_path]
echo $TIMENOW $BASE_DIR $DATA_DIR $USER
Flags:
-c, --config_path: config file path
-y, --yq: yq binary path (>v4.35.2)
-v, --verbose
-> -b, --base-dir: overrides base_dir
-> -d, --dry-run: dry run
EOF
exit 1
}
function error() {
echo $0: "$1"
if [[ "$USER" == "root" ]]; then
echo "Dont run as root."
su user $0 $@
exit
}
fi
function debug() {
if [[ -v DEBUG ]]; then
echo debug: "$1"
fi
}
option="-rtlHpvi --chmod=D0755,F0644 --partial --hard-links --safe-links --stats --delete --delete-after --delay-updates --max-delete=70000"
exclude="--exclude=.*.?????? --exclude='.~tmp~/' --exclude='Packages*' --exclude='Sources*' --exclude='Release*' --exclude='*.links.tar.gz*' --exclude='/other' --exclude='/sources'"
function execute() {
debug "Executing script: \"$@\""
if [[ ! -v DRY_RUN ]]; then
$@
fi
}
ubuntu="rsync://rsync.archive.ubuntu.com/ubuntu/"
ubuntu_cd="rsync://releases.ubuntu.com/releases/"
ubuntu_cd_old="rsync://old-releases.ubuntu.com/releases/"
debian="rsync://mirrors.xtom.jp/debian/"
debian_cd="rsync://ftp.lanet.kr/debian-cd/"
fedora="rsync://dl.fedoraproject.org/fedora-enchilada/linux/"
epel="rsync://dl.fedoraproject.org/fedora-epel/"
fedora_cd=""
archlinux="rsync://mirrors.xtom.de/archlinux/"
raspbian="rsync://archive.raspbian.org/archive/"
manjaro="rsync://ftp.riken.jp/manjaro/"
gnu="rsync://ftp.gnu.org/gnu/"
kali_images="rsync://repo.jing.rocks/kali-images"
kali="rsync://repo.jing.rocks/kali"
linux="rsync://rsync.kernel.org/pub/"
failtest="rsync://aa"
if [[ ! -v $1 ]]; then
echo Not found.
exit
fi
parse_yaml() {
echo $($YAML eval ".$1" "${CONFIG}")
}
ubuntu_cd_name="ubuntu-cd"
debian_cd_name="debian-cd"
ubuntu_cd_old_name="ubuntu-old"
kali_images_name="kali-images"
get_repo_config() {
local LOCAL_CFG=$($YAML eval ".repos.$1.$2" "${CONFIG}")
if [[ "$LOCAL_CFG" == "null" || "$LOCAL_CFG" == "" ]]; then
echo $($YAML eval ".defaults.$2" "${CONFIG}");
return 1
fi
echo $LOCAL_CFG
return 0
dist=$1
echo Syncing $1...
set -o pipefail
LASTLOG=`head -1 ${BASE_DIR}/logs/${dist}.log`
# 1 if global 0 if local
}
mv ${BASE_DIR}/logs/${dist}.log ${BASE_DIR}/logs/previous/${dist}-${LASTLOG}.log
mv ${BASE_DIR}/logs/${dist}-error.log ${BASE_DIR}/logs/previous/${dist}-error-${LASTLOG}.log
sync_repo() {
local repo=$1
local name=$(get_repo_config ${repo} name)
local type=$(get_repo_config ${repo} type)
local url=$(get_repo_config ${repo} url)
local path=$(get_repo_config ${repo} path)
local log; log=$(get_repo_config ${repo} log) || log=${log/\${path\}/$path};
if [[ -v ${dist}_name ]]; then
dist_name_var="${dist}_name"
dist_dir=${!dist_name_var}
else
dist_dir=$dist
fi
if [[ "$type" == "null" || "$url" == "null" || "$path" == "null" || "$log" == "null" ]]; then
echo "Error config wrong."
return
fi
echo ${TIMENOW} >> ${BASE_DIR}/logs/${dist}.log
echo ${TIMENOW} >> ${BASE_DIR}/logs/${dist}-error.log
echo "${TIMENOW}: Mirroring ${dist} from ${!dist} to ${DATA_DIR}/${dist}"
echo "${TIMENOW} STARTED ${dist}" >> ${BASE_DIR}/logs/all.log
full_path="${BASE_DIR}/$(parse_yaml global.data_dir)/$path/"
log="${BASE_DIR}/$log"
echo -e "--------\nRepo: $name\nType: $type\nUpstream: $url\nPath: $full_path\nLog: $log\n--------"
if [[ ! -v DRY_RUN ]]; then
rotate_log $log
case $type in
"rsync")
local rsync_options=$(get_repo_config $repo 'rsync.options')
local exclude_list=($(get_repo_config $repo 'rsync.exclude[]'))
local exclude=""
for ex in "${exclude_list[@]}"; do
exclude="${exclude} --exclude='${ex}'"
done
echo rsync ${rsync_options} ${exclude} $url $full_path >> $log
rsync ${rsync_options} ${exclude} $url $full_path >> $log 2>> ${log}-error
;;
"ftpsync")
TRY=3
while [ $TRY -ne 0 ]; do
echo Try $TRY...
if [ "$dist" == "debian" ];
then
cd ${BASE_DIR}/scripts
export DEB_ADMIN="Morgan <mirror@devpg.net>"
export DEB_COUNTRY="Korea"
export DEB_LOCATION="Korea/Seoul"
export DEB_PATH="$full_path"
export DEB_HOST="$(parse_yaml global.hostname)"
export DEB_UPSTREAM="$url"
export DEB_LOG="${BASE_DIR}/$(parse_yaml global.log_dir)"
./ftpsync >> $log 2>> ${log}-error
cd ${BASE_DIR}
;;
"http")
echo ${repo} Fetch >> $log 2>> ${log}-error
python3 -u $BASE_DIR/scripts/getFetch.py "${url}" $full_path $BASE_DIR/scripts/${path}.fetch >> $log 2>> ${log}-error
echo ${repo} Download >> $log 2>> ${log}-error
python3 -u $BASE_DIR/scripts/getFile.py $BASE_DIR/scripts/${path}.fetch >> $log 2>> ${log}-error
;;
*)
echo "Unknown type $type for $repo." | tee ${log}-error
;;
esac
clean_log $log
fi
}
rotate_log() {
local log_file=$1
if [[ -f $log_file ]]; then
PREV_LOG=$(cat "$log_file" | head -n 1)
old_log_file="$(dirname $log_file)/old/$(basename $log_file)-$PREV_LOG"
mkdir -p "$(dirname $old_log_file)"
mv "$log_file" "$old_log_file"
fi
local error_file=$1-error
if [[ -f $error_file ]]; then
PREV_LOG=$(cat "$error_file" | head -n 1)
old_error_file="$(dirname $error_file)/old/$(basename $error_file)-$PREV_LOG"
mkdir -p "$(dirname $old_error_file)"
mv "$error_file" "$old_error_file"
fi
echo $TIMESTAMP >> $log_file
echo $TIMESTAMP >> $error_file
}
clean_log() {
local error_file=$1-error
nl=$(cat "$error_file" | wc -l)
if [ $nl -eq 1 ]; then
rm "$error_file"
fi
}
function global_log() {
GLOBAL_LOG_FILE="$(parse_yaml 'global.log')"
echo "$1 $TIMESTAMP SUCCESS" >> $GLOBAL_LOG_FILE
}
function global_log_error() {
GLOBAL_LOG_FILE="$(parse_yaml 'global.log')"
echo "$1 $TIMESTAMP ERROR" >> $GLOBAL_LOG_FILE
}
##########
# Main #
##########
while [ "$1" != "" ]; do
case $1 in
-c | --config)
shift
if [[ -z $1 ]]; then
error "option '-c' requires argument 'config_path'"
fi
CONFIG=$1
;;
-y | --yq)
shift
if [[ -z $1 ]]; then
error "option '-y' requires argument 'yq_path'"
fi
YAML=$1
;;
-b | --base-dir)
shift
if [[ -z $1 ]]; then
error "option '-b' requires argument 'base_dir'"
fi
BASE_DIR_OVERRIDE=$1
;;
-h | --help)
usage
;;
-v | --verbose)
DEBUG=1
;;
-d | --dry-run)
DRY_RUN=1
;;
*)
usage
exit 1
;;
esac
shift
done
if [[ -v DEBUG ]]; then
debug "DEBUG=1"
fi
if [[ -v DRY_RUN ]]; then
debug "DRY_RUN=1"
fi
debug CONFIG="\"${CONFIG}\""
debug YQ="\"${YAML}\""
if [[ ! -f ${CONFIG} ]]; then
error "config not found."
fi
if [[ ! -f ${YAML} ]]; then
error "yq not found."
fi
BASE_DIR=$($YAML eval ".BASE_DIR" "${CONFIG}")
if [[ -v DEBUG && -v BASE_DIR_OVERRIDE ]]; then
debug "Overriding $BASE_DIR to $BASE_DIR_OVERRIDE"
BASE_DIR="$BASE_DIR_OVERRIDE"
fi
TIMESTAMP=$(date '+%Y-%m-%dT%H:%MZ')
debug BASE_DIR="\"${BASE_DIR}\""
debug TIMESTAMP="\"${TIMESTAMP}\""
echo Started job $TIMESTAMP..
cd $BASE_DIR
# PRE
global_pre_scripts=($(parse_yaml 'global.scripts.pre[]'))
for script in "${global_pre_scripts[@]}"; do
execute $BASE_DIR/$script
done
#
repos=($(parse_yaml 'global.sync[]'))
if [[ "${repos[0]}" == "ALL" ]]; then
repos=($($YAML eval '.repos | keys | .[]' "${CONFIG}"))
fi
for repo in "${repos[@]}"; do
cd $BASE_DIR
debug "Checking $repo..."
delay=$(get_repo_config ${repo} "delay")
last_sync_timestamp=$(date -d "$(get_repo_config ${repo} "last_sync")" +%s)
next_sync_timestamp=$(( last_sync_timestamp + delay * 3600 ))
if [[ -v DEBUG ]]; then
next_sync_timestamp=1
# read -p "Continue? " choice
# case "$choice" in
# y) next_sync_timestamp=1;;
# *) continue;;
# esac
fi
if [ $next_sync_timestamp -le $(date +%s) ]; then
debug "Lastsync was $last_sync_timestamp."
echo "Syncing $repo..."
repo_pre_scripts=($(get_repo_config ${repo} "scripts.pre[]"))
for script in "${repo_pre_scripts[@]}"; do
execute $BASE_DIR/$script $repo
done
sync_repo $repo
if [ $? -ne 0 ]; then
repo_fail_scripts=($(get_repo_config ${repo} "scripts.fail[]"))
for script in "${repo_fail_scripts[@]}"; do
execute $BASE_DIR/$script $repo
done
global_log_error $repo
echo "Error during syncing $repo."
export BASE_DIR=${BASE_DIR}
export DATA_DIR=${DATA_DIR}
./ftpsync
else
global_log $repo
$YAML eval ".repos.${repo}.last_sync = \"$TIMESTAMP\"" -i "${CONFIG}"
echo "Successfully synced $repo."
unset RSYNC_CONNECT_PROG
if [ "$dist" == "kali_images" ];
then
# export RSYNC_CONNECT_PROG='ssh zhr0 nc %H 873'
echo Connecting to RSYNC PROG
fi
repo_post_scripts=($(get_repo_config ${repo} "scripts.post[]"))
for script in "${repo_post_scripts[@]}"; do
script=${script/\${config\}/$CONFIG}
execute $BASE_DIR/$script $repo
done
echo "rsync ${option} ${exclude} ${!dist} ${DATA_DIR}/${dist_dir}" | tee -a ${BASE_DIR}/logs/${dist}.log
rsync ${option} ${exclude} ${!dist} ${DATA_DIR}/${dist_dir} 2> >(tee -a ${BASE_DIR}/logs/${dist}-error.log) | tee -a ${BASE_DIR}/logs/${dist}.log
EXIT=$?
fi
if [[ $EXIT == 0 ]]; then break; fi
TRY=$(($TRY-1))
done
# POST
global_post_scripts=($(parse_yaml 'global.scripts.post[]'))
for script in "${global_post_scripts[@]}"; do
execute $BASE_DIR/$script
done
#
if [ $EXIT -ne 0 ];
then
MSG="${dist} failed at ${TIMENOW}"
if [ -f "$ALERT" ];
then
${ALERT} "${MSG}"
fi
echo Sync ${dist} Error
echo "${TIMENOW} ERROR ${dist}" >> ${BASE_DIR}/logs/all.log
else
echo Sync ${dist} Success
if [ `echo ${BASE_DIR}/logs/${dist}-error.log | wc -l` -eq 1 ];
then
rm ${BASE_DIR}/logs/${dist}-error.log
fi
echo "${TIMENOW} DONE ${dist}" >> ${BASE_DIR}/logs/all.log
cd $BASE_DIR
echo "Updating Index"
python3 -u ./scripts/index.py ${BASE_DIR} ${DATA_DIR}
fi
echo Ended job $TIMESTAMP..

View File

@ -1,94 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PAWE.ME</title>
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans:wght@300;400;500;600;700;800;900&display=swap"
rel="stylesheet">
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
darkMode: 'class'
}
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.documentElement.classList.add('dark')
} else {
document.documentElement.classList.remove('dark')
}
</script>
<style>
.url {
font-style: italic;
color: #5d7abf;
} </style>
</head>
<body class="font-sans text-gray-900 bg-gray-100 py-12 px-6 mx-auto max-w-5xl dark:bg-gray-900">
<header class="mb-10">
<h1 class="text-4xl font-bold mb-2"><a href="http://PAWE.ME/"
class="text-blue-500 hover:text-blue-700">PAWE.ME</a></h1>
<h2 class="text-lg text-gray-600 dark:text-gray-300">Simple mirroring &amp; archiving server.</h2>
<!-- <h2 class="text-lg text-blue-400 dark:text-blue-200 italic"><a href="#">Hide my Email</a>, <a href="#">Invidious</a>, <a href="#">Proxy</a>.</h2> -->
</header>
<main>
<section class="mb-12">
<h2 class="dark:text-gray-200 text-2xl font-semibold mb-5">Mirroring List</h2>
<div id="mirrorList" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-2 gap-4">
{{ repos }}
</div>
</section>
<section class="mb-12">
<h2 class="dark:text-gray-200 text-2xl font-semibold mb-5">Additional Mirror</h2>
<!-- <button onclick="toggleView('httpView')" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded mb-4">View More</button> -->
<div id="httpView" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-2 gap-4">
{{ repos_more }}
<div class="bg-white dark:bg-gray-800 col-span-1 md:col-span-2 shadow-lg rounded-lg p-5">
<h3 class="dark:text-white text-lg font-semibold mb-1"><a href="/cd-image"
class="text-blue-500 hover:text-blue-700">Image Files</a></h3>
</div>
</div>
</section>
<section>
<h2 class="dark:text-gray-200 text-2xl font-semibold mb-5">Server Info</h2>
<ul class="space-y-2 mb-5">
<li class="text-gray-600 dark:text-gray-500">Xeon CPU</li>
<li class="text-gray-600 dark:text-gray-500">64GiB Memory</li>
<li class="text-gray-600 dark:text-gray-500">1Gbps Network, Seoul, Korea</li>
<li class="text-gray-600 dark:text-gray-500">32TB Storage</li>
</ul>
<p class="text-gray-700 dark:text-gray-400 url">Fill out <a href="/form.html" class="text-blue-600 dark:text-blue-700">this</a> form to request additional mirroring.</p>
</section>
<button
class="fixed bottom-6 right-6 bg-blue-200 hover:bg-blue-300 text-blue-700 dark:bg-blue-800 dark:hover:bg-blue-600 dark:text-white w-10 h-10 rounded-full flex items-center justify-center"
onclick="toggleDarkMode()">
+
</button>
</main>
<footer class="mt-10 text-center text-gray-600">
<a href="mailto:mirror@devpg.net">&copy; PAWE.ME</a>
<br/>
<a href="https://github.com/morgan9e/pawe.me"><i>source</i></a>
</footer>
<script>
function toggleView(elementId) {
const element = document.getElementById(elementId);
element.style.display = element.style.display === "none" ? "block" : "none";
}
function toggleDarkMode() {
if (document.documentElement.classList.contains('dark')) {
document.documentElement.classList.remove('dark');
localStorage.theme = 'light';
} else {
document.documentElement.classList.add('dark');
localStorage.theme = 'dark';
}
}
</script>
</body>
</html>

View File

@ -1,6 +0,0 @@
<div class="bg-white dark:bg-gray-800 col-span-1 md:col-span-2 shadow-lg rounded-lg p-5">
<h3 class="dark:text-white text-lg font-semibold mb-1"><a href="/{{path}}"
class="text-blue-500 hover:text-blue-700">{{name}}</a></h3>
<p class="text-gray-600 dark:text-gray-300">Last Updated: {{lastsync}}</p>
<p class="text-gray-600 dark:text-gray-300">Upstream: <span class="url">{{upstream}}</span></p>
</div>

View File

@ -1,6 +0,0 @@
<div class="bg-white dark:bg-gray-800 col-span-1 shadow-lg rounded-lg p-5">
<h3 class="dark:text-white text-lg font-semibold mb-1"><a href="/{{path}}"
class="text-blue-500 hover:text-blue-700">{{name}}</a></h3>
<p class="text-gray-600 dark:text-gray-300">Last Updated: {{lastsync}}</p>
<p class="text-gray-600 dark:text-gray-300">Upstream: <span class="url">{{upstream}}</span></p>
</div>

View File

@ -1,99 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>repo.city</title>
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans:wght@300;400;500;600;700;800;900&display=swap"
rel="stylesheet">
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
darkMode: 'class'
}
// if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
// document.documentElement.classList.add('dark')
if (localStorage.theme === 'dark') {
document.documentElement.classList.add('dark')
} else {
document.documentElement.classList.remove('dark')
}
</script>
<style>
.url {
font-style: italic;
color: rgb(86, 105, 151);
}
</style>
</head>
<body class="font-sans text-gray-900 bg-gray-100 py-12 px-6 mx-auto max-w-5xl dark:bg-gray-900">
<header class="mb-10 ml-2">
<h1 class="text-3xl font-bold mb-2 text-black dark:text-white">Welcome to <a href="https://repo.city"
class="text-blue-900 dark:text-blue-300 font-bold">REPO.CITY</a>!</h1>
<h2 class="text-xl text-gray-600 dark:text-gray-300">Simple mirroring &amp; archiving server.</h2>
</header>
<main>
<section class="mb-12">
<h2 class="dark:text-gray-200 text-2xl font-semibold mb-5 ml-2">Mirroring List</h2>
<div id="mirrorList" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-2 gap-4">
{{ repos }}
</div>
</section>
<section class="mb-12">
<h2 class="dark:text-gray-200 text-2xl font-semibold mb-5 ml-2">Additional Mirror</h2>
<!-- <button onclick="toggleView('httpView')" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded mb-4">View More</button> -->
<div id="httpView" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-2 gap-4">
{{ repos_more }}
<div class="bg-white dark:bg-gray-800 col-span-1 md:col-span-2 shadow-lg rounded-lg p-5">
<h3 class="dark:text-white text-lg font-semibold mb-1"><a href="/cd-image"
class="text-blue-900 hover:text-blue-600 dark:text-blue-600 dark:hover:text-blue-400">Image Files</a></h3>
</div>
</div>
</section>
<section class="ml-2">
<h2 class="dark:text-gray-200 text-2xl font-semibold mb-4">Server Details</h2>
<p class="mb-2 text-gray-800 dark:text-gray-500"><strong>Location:</strong> Seoul, Korea</p>
<p class="mb-2 text-gray-800 dark:text-gray-500"><strong>Bandwidth:</strong> ~1Gbps</p>
<p class="mb-2 text-gray-800 dark:text-gray-500"><strong>Storage size:</strong> 32TiB</p>
<p class="mb-2 text-gray-800 dark:text-gray-500"><strong>Sync delay:</strong> 4hrs</p>
<p class="mb-2 text-gray-800 dark:text-gray-500"><strong>Contact:</strong> <a href="mailto:mirror@devpg.net">mirror@devpg.net</a></p>
<p class="mb-2 text-gray-800 dark:text-gray-500"><strong>Maintainer:</strong> <a href="https://github.com/morgan9e">@morgan9e</a></p>
<p class="text-gray-500 dark:text-gray-600 url">This mirror is based on <a href="https://github.com/morgan9e/reposync" class="text-blue-500 dark:text-blue-800">reposync</a>.</p>
<p class="text-gray-500 dark:text-gray-600 url">For additional mirroring requests, please contact me at email above.</p>
</section>
<button
class="fixed bottom-6 right-6 bg-blue-200 hover:bg-blue-300 text-blue-700 dark:bg-blue-800 dark:hover:bg-blue-600 dark:text-white w-10 h-10 rounded-full flex items-center justify-center"
onclick="toggleDarkMode()">
+
</button>
</main>
<footer class="mt-10 text-center text-gray-600">
<a href="mailto:mirror@devpg.net">&copy; REPO.CITY</a>
</footer>
<script>
function toggleView(elementId) {
const element = document.getElementById(elementId);
element.style.display = element.style.display === "none" ? "block" : "none";
}
function toggleDarkMode() {
if (document.documentElement.classList.contains('dark')) {
document.documentElement.classList.remove('dark');
localStorage.theme = 'light';
} else {
document.documentElement.classList.add('dark');
localStorage.theme = 'dark';
}
}
</script>
</body>
</html>

View File

@ -1,6 +0,0 @@
<div class="bg-white dark:bg-gray-800 col-span-1 md:col-span-2 shadow-lg rounded-lg p-5">
<h3 class="dark:text-white text-lg font-semibold mb-1"><a href="/{{path}}"
class="text-blue-900 hover:text-blue-600 dark:text-blue-600 dark:hover:text-blue-400">{{name}}</a></h3>
<p class="text-gray-600 dark:text-gray-300">Last Updated: {{lastsync}}</p>
<p class="text-gray-600 dark:text-gray-300">Upstream: <span class="url">{{upstream}}</span></p>
</div>

View File

@ -1,6 +0,0 @@
<div class="bg-white dark:bg-gray-800 col-span-1 shadow-lg rounded-lg p-5">
<h3 class="dark:text-white text-lg font-semibold mb-1"><a href="/{{path}}"
class="text-blue-900 hover:text-blue-600 dark:text-blue-600 dark:hover:text-blue-400">{{name}}</a></h3>
<p class="text-gray-600 dark:text-gray-300">Last Updated: {{lastsync}}</p>
<p class="text-gray-600 dark:text-gray-300">Upstream: <span class="url">{{upstream}}</span></p>
</div>

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

View File

@ -1,77 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PAWE.ME</title>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans:wght@300;400;500;600;700;800;900&display=swap"
rel="stylesheet">
<script src="https://cdn.tailwindcss.com"></script>
<style>
.url {
font-style: italic;
color: rgb(86, 105, 151);
}
</style>
</head>
<body class="font-sans text-gray-900 bg-gray-100 py-12 px-6 mx-auto max-w-5xl">
<header class="mb-10">
<h1 class="text-4xl font-bold mb-2"><a href="http://PAWE.ME" class="text-blue-500 hover:text-blue-700">PAWE.ME</a></h1>
<h2 class="text-lg text-gray-600"></h2>
</header>
<form id="contactForm" class="bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4">
<h2 class="text-2xl font-semibold mb-5">Request Form</h2>
<div class="mb-4">
<label class="block text-gray-700 text-sm font-bold mb-2" for="email">
Email
</label>
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" id="email" type="email" placeholder="Email">
</div>
<div class="mb-4 hidden">
<label class="block text-gray-700 text-sm font-bold mb-2" for="title">
Title
</label>
<input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" id="title" type="text" placeholder="Title" value="New Request">
</div>
<div class="mb-6">
<label class="block text-gray-700 text-sm font-bold mb-2" for="content">
Content
</label>
<textarea class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" id="content" placeholder="Content" rows="5"></textarea>
</div>
<div class="flex items-center justify-between">
<button id="sendButton" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline" type="button">
Send Message
</button>
</div>
</form>
<p class="text-gray-800 text-xs italic">&nbsp;&nbsp;Only Rsync &amp; HTTP File server is available. HTTP mirroring cycle is much slower than Rsync. Feel free to request!</p>
<script>
document.getElementById('sendButton').addEventListener('click', function() {
const email = document.getElementById('email').value;
const title = document.getElementById('title').value;
const content = document.getElementById('content').value;
let formData = new URLSearchParams();
formData.append('email', email);
formData.append('title', title);
formData.append('content', content);
fetch(PUSHAPIADDRESS, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: formData.toString(),
})
.then(response => response.json())
.then((data) => {
console.log(data);
alert("Sent to Administrator. Reply may take 2-3 days.");
})
});
</script>
</body>
</html>

View File

@ -1 +0,0 @@
../index.html

View File

@ -1,70 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Log Viewer</title>
</head>
<body>
<input type="text" id="logPath" placeholder="Enter log file path or URL">
<select id="intervalSelector">
<option value="1000">1 sec</option>
<option value="2000">2 sec</option>
<option value="3000">3 sec</option>
</select>
<button onclick="startFetchingLog()">Start Fetching</button>
<button onclick="toggleFetching()">Toggle Fetching</button>
<div id="logContent" style="white-space: pre-wrap;"></div>
<script>
let interval;
let isFetching = false;
function fetchLog(path) {
fetch(path)
.then(response => response.text())
.then(data => {
const reversedData = data.split("\n").reverse().join("\n");
document.getElementById('logContent').textContent = reversedData;
})
.catch(error => {
console.error('There was an error fetching the log:', error);
});
}
function startFetchingLog() {
stopFetching(); // Stop any existing fetching
const path = document.getElementById('logPath').value;
const fetchInterval = parseInt(document.getElementById('intervalSelector').value);
if (path) {
fetchLog(path); // Initial fetch
interval = setInterval(() => fetchLog(path), fetchInterval); // Fetch the log file at the selected interval
isFetching = true;
}
}
function stopFetching() {
if (interval) {
clearInterval(interval);
isFetching = false;
}
}
function toggleFetching() {
if (isFetching) {
stopFetching();
} else {
startFetchingLog();
}
}
</script>
</body>
</html>