Revert back to old script
This commit is contained in:
		
							parent
							
								
									4c2bb9b812
								
							
						
					
					
						commit
						60ec3df42e
					
				
										
											Binary file not shown.
										
									
								
							| 
						 | 
				
			
			@ -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 &
 | 
			
		||||
| 
						 | 
				
			
			@ -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">©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>
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			@ -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"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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}.")
 | 
			
		||||
| 
						 | 
				
			
			@ -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())
 | 
			
		||||
							
								
								
									
										121
									
								
								scripts/index.py
								
								
								
								
							
							
						
						
									
										121
									
								
								scripts/index.py
								
								
								
								
							| 
						 | 
				
			
			@ -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
 | 
			
		||||
for dist in dists:
 | 
			
		||||
    stat, tt = stats[dist]
 | 
			
		||||
    if stat == "ERROR":
 | 
			
		||||
        stat = f" (Error @ {tt})"
 | 
			
		||||
    elif stat == "STARTED":
 | 
			
		||||
        stat = f" (Running @ {tt})"
 | 
			
		||||
    else:
 | 
			
		||||
        stat = ""
 | 
			
		||||
    
 | 
			
		||||
            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))
 | 
			
		||||
            else:
 | 
			
		||||
                (main_repos if not DIV else additional_repos).append(full_template.render(**context))
 | 
			
		||||
    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}")
 | 
			
		||||
 | 
			
		||||
    html_output = base_template.render(
 | 
			
		||||
        repos="\n".join(main_repos),
 | 
			
		||||
        repos_more="\n".join(additional_repos)
 | 
			
		||||
    )
 | 
			
		||||
with open(index, 'w') as f:
 | 
			
		||||
    f.write(html_file)
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        with open(OUTPUT_PATH, 'w') as f:
 | 
			
		||||
            f.write(html_output)
 | 
			
		||||
 | 
			
		||||
    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")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
#	}
 | 
			
		||||
#}
 | 
			
		||||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
    
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
							
								
								
									
										377
									
								
								scripts/sync.sh
								
								
								
								
							
							
						
						
									
										377
									
								
								scripts/sync.sh
								
								
								
								
							| 
						 | 
				
			
			@ -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
 | 
			
		||||
}
 | 
			
		||||
if [[ "$USER" == "root" ]]; then
 | 
			
		||||
	echo "Dont run as root."
 | 
			
		||||
	su user $0 $@
 | 
			
		||||
	exit
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
function error() {
 | 
			
		||||
    echo $0: "$1"
 | 
			
		||||
    exit
 | 
			
		||||
}
 | 
			
		||||
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 debug() {
 | 
			
		||||
    if [[ -v DEBUG ]]; then
 | 
			
		||||
        echo debug: "$1"
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
function execute() {
 | 
			
		||||
    debug "Executing script: \"$@\""
 | 
			
		||||
    if [[ ! -v DRY_RUN ]]; then
 | 
			
		||||
        $@
 | 
			
		||||
    fi
 | 
			
		||||
}
 | 
			
		||||
ubuntu_cd_name="ubuntu-cd"
 | 
			
		||||
debian_cd_name="debian-cd"
 | 
			
		||||
ubuntu_cd_old_name="ubuntu-old"
 | 
			
		||||
kali_images_name="kali-images"
 | 
			
		||||
 | 
			
		||||
parse_yaml() {
 | 
			
		||||
    echo $($YAML eval ".$1" "${CONFIG}")
 | 
			
		||||
}
 | 
			
		||||
dist=$1
 | 
			
		||||
echo Syncing $1...
 | 
			
		||||
set -o pipefail
 | 
			
		||||
LASTLOG=`head -1 ${BASE_DIR}/logs/${dist}.log`
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
    # 1 if global 0 if local
 | 
			
		||||
}
 | 
			
		||||
if [[ -v ${dist}_name ]]; then
 | 
			
		||||
	dist_name_var="${dist}_name"
 | 
			
		||||
	dist_dir=${!dist_name_var}
 | 
			
		||||
else
 | 
			
		||||
	dist_dir=$dist
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
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};
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
    if [[ "$type" == "null" || "$url" == "null" || "$path" == "null" || "$log" == "null" ]]; then
 | 
			
		||||
        echo "Error config wrong."
 | 
			
		||||
        return
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    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")
 | 
			
		||||
                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
 | 
			
		||||
TRY=3
 | 
			
		||||
while [ $TRY -ne 0 ]; do
 | 
			
		||||
  echo Try $TRY...
 | 
			
		||||
  if [ "$dist" == "debian" ];
 | 
			
		||||
  then
 | 
			
		||||
  	cd ${BASE_DIR}/scripts
 | 
			
		||||
  	export BASE_DIR=${BASE_DIR}
 | 
			
		||||
  	export DATA_DIR=${DATA_DIR}
 | 
			
		||||
  	./ftpsync
 | 
			
		||||
  else
 | 
			
		||||
	unset RSYNC_CONNECT_PROG
 | 
			
		||||
	if [ "$dist" == "kali_images" ];
 | 
			
		||||
	then
 | 
			
		||||
 		# export RSYNC_CONNECT_PROG='ssh zhr0 nc %H 873'
 | 
			
		||||
		echo Connecting to RSYNC PROG
 | 
			
		||||
	fi
 | 
			
		||||
	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
 | 
			
		||||
 | 
			
		||||
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 [ $EXIT -ne 0 ];
 | 
			
		||||
then
 | 
			
		||||
	MSG="${dist} failed at ${TIMENOW}"
 | 
			
		||||
	if [ -f "$ALERT" ];
 | 
			
		||||
	then
 | 
			
		||||
	        ${ALERT} "${MSG}"
 | 
			
		||||
	fi
 | 
			
		||||
	
 | 
			
		||||
if [[ ! -f ${CONFIG} ]]; then
 | 
			
		||||
    error "config not found."
 | 
			
		||||
	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
 | 
			
		||||
 | 
			
		||||
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."
 | 
			
		||||
        else
 | 
			
		||||
 | 
			
		||||
            global_log $repo
 | 
			
		||||
            $YAML eval ".repos.${repo}.last_sync = \"$TIMESTAMP\"" -i "${CONFIG}"
 | 
			
		||||
            echo "Successfully synced $repo."
 | 
			
		||||
        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
 | 
			
		||||
    fi
 | 
			
		||||
done
 | 
			
		||||
 | 
			
		||||
# POST
 | 
			
		||||
global_post_scripts=($(parse_yaml 'global.scripts.post[]'))
 | 
			
		||||
for script in "${global_post_scripts[@]}"; do
 | 
			
		||||
    execute $BASE_DIR/$script
 | 
			
		||||
done
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
echo Ended job $TIMESTAMP..
 | 
			
		||||
| 
						 | 
				
			
			@ -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 & 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">© 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>
 | 
			
		||||
| 
						 | 
				
			
			@ -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>
 | 
			
		||||
| 
						 | 
				
			
			@ -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>
 | 
			
		||||
| 
						 | 
				
			
			@ -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 & 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">© 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>
 | 
			
		||||
| 
						 | 
				
			
			@ -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>
 | 
			
		||||
| 
						 | 
				
			
			@ -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>
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								scripts/yq
								
								
								
								
							
							
						
						
									
										
											BIN
										
									
								
								scripts/yq
								
								
								
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 40 KiB  | 
| 
						 | 
				
			
			@ -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">  Only Rsync & 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>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1 +0,0 @@
 | 
			
		|||
../index.html
 | 
			
		||||
| 
						 | 
				
			
			@ -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>
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue