ctucx.git: nixfiles

ctucx' nixfiles

commit 5d0a9bd66051e17c52d482145d41eea4c10b21bb
parent 602fa6d112f7c9e7ce281c82e3c6ffd477d54c5c
Author: Leah (ctucx) <leah@ctu.cx>
Date: Thu, 18 Aug 2022 11:07:12 +0200

machines/lollo/websites: add storage.home.ctu.cx
4 files changed, 362 insertions(+), 0 deletions(-)
M
machines/lollo/router/dnsmasq.nix
|
1
+
M
machines/lollo/websites/default.nix
|
1
+
A
machines/lollo/websites/storage.home.ctu.cx/default.nix
|
39
+++++++++++++++++++++++++++++++++++++++
A
machines/lollo/websites/storage.home.ctu.cx/web-root/index.html
|
321
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
diff --git a/machines/lollo/router/dnsmasq.nix b/machines/lollo/router/dnsmasq.nix
@@ -44,6 +44,7 @@ in {
         host-record=legacy.home.ctu.cx,                 195.39.246.41,      2a0f:4ac0:acab::1
         host-record=dnsmasq.home.ctu.cx,                195.39.246.41,      2a0f:4ac0:acab::1
         host-record=music.home.ctu.cx,                  195.39.246.41,      2a0f:4ac0:acab::1
+        host-record=storage.home.ctu.cx,                195.39.246.41,      2a0f:4ac0:acab::1
         host-record=influx.home.ctu.cx,                 195.39.246.41,      2a0f:4ac0:acab::1
         host-record=wiki.home.ctu.cx,                   195.39.246.41,      2a0f:4ac0:acab::1
         host-record=fedi.home.ctu.cx,                   195.39.246.41,      2a0f:4ac0:acab::1
diff --git a/machines/lollo/websites/default.nix b/machines/lollo/websites/default.nix
@@ -5,6 +5,7 @@
   imports = [
     ./wiki.home.ctu.cx.nix
     ./music.home.ctu.cx.nix
+    ./storage.home.ctu.cx
   ];
 
 }
diff --git a/machines/lollo/websites/storage.home.ctu.cx/default.nix b/machines/lollo/websites/storage.home.ctu.cx/default.nix
@@ -0,0 +1,39 @@
+
+{ ... }:
+
+{
+  services.nginx.virtualHosts = {
+    "storage.home.ctu.cx" = {
+      enableACME = true;
+      forceSSL = true;
+
+      basicAuthFile = "/var/src/secrets/nginx/storage.htpasswd";
+
+      locations."= /" = {
+        alias = "${./web-root}/";
+        extraConfig = ''
+          try_files index.html =404;
+        '';
+      };
+
+      locations."/_/" = {
+        alias = "/data/";
+        extraConfig = ''
+          autoindex on;
+          autoindex_format json;
+
+          client_body_temp_path /data;
+          dav_methods PUT DELETE MKCOL COPY MOVE;
+          create_full_put_path on;
+          dav_access group:rw all:r;
+          client_max_body_size 1G;
+        '';
+      };
+    };
+  };
+
+  systemd.services.nginx.serviceConfig = {
+    ReadWritePaths = "/data";
+  };
+}
+
diff --git a/machines/lollo/websites/storage.home.ctu.cx/web-root/index.html b/machines/lollo/websites/storage.home.ctu.cx/web-root/index.html
@@ -0,0 +1,321 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+    <title>storage.home.ctu.cx</title>
+
+    <style>
+      * {
+        margin: 0;
+        padding: 0;
+      }
+
+      body {
+        font-family: Roboto;
+        color: #222222;
+      }
+
+      header {
+      }
+
+      header > .title {
+        padding: 3em;
+        min-height: 3em;
+      }
+
+      header > .title > h1 {
+        color: #222222;
+      }
+
+      header > .menu {
+        padding: 1em 3em;
+      }
+
+      header > .menu > button,
+      header > .menu > input {
+        height: 3em;
+        min-width: 3em;
+      }
+
+      main {
+      }
+
+      .listing {
+        display: flex;
+        width: 100%;
+        flex-direction: column;
+      }
+
+      .listing-item {
+        padding: 1em 3em;
+        display: flex;
+        flex-direction: row;
+        flex-wrap: wrap;
+        min-height: 3em;
+
+        border-top-style: solid;
+        border-width: 1px;
+        border-color: #F0F0F0;
+      }
+
+      .listing-item:hover {
+        background-color: rgb(245, 245, 245);
+      }
+
+      .listing-item > .detail {
+        flex: auto;
+      }
+
+      .listing-item > .detail > a {
+        display: block;
+        height: 100%;
+        width: 100%;
+      }
+
+      .listing-item button {
+        height: 3em;
+        min-width: 3em;
+      }
+
+      button, input[type="file"] {
+        border-style: none;
+        background-color: transparent;
+        min-width: 3em;
+        height: 3em;
+        border-radius: 0.2em;
+      }
+
+      button:hover {
+        background-color: #DDDDDD;
+      }
+
+      .loading {
+        margin: auto;
+      }
+      @keyframes spinner {
+        0% {
+          transform: translate3d(-50%, -50%, 0) rotate(0deg);
+        }
+        100% {
+          transform: translate3d(-50%, -50%, 0) rotate(360deg);
+        }
+      }
+      .loading::before {
+        display: block;
+        animation: 1.5s linear infinite spinner;
+        animation-play-state: inherit;
+        border: solid 5px #cfd0d1;
+        border-bottom-color: #1c87c9;
+        border-radius: 50%;
+        content: "";
+        height: 40px;
+        width: 40px;
+        transform: translate3d(-50%, -50%, 0);
+        will-change: transform;
+      }
+    </style>
+  </head>
+  <body>
+    <header>
+      <div class="title">
+        <h1 id="path"></h1>
+      </div>
+      <div class="menu">
+        <button onclick="createDirectoryButton();">📁 +</button>
+        <input id="files" type="file" multiple> <button onclick="uploadFilesButton();">⭫</button>
+      </div>
+    </header>
+    <main>
+      <div id="listing"></div>
+    </main>
+    <script>
+      let path_field = document.getElementById("path");
+      let listing_field = document.getElementById("listing");
+
+      let base_path = "/_";
+
+      if (window.location.hash === "") {
+        window.location.hash = "#/";
+      }
+
+      let current_path = window.location.hash.substring(1);
+
+      function deleteFile(path) {
+        fetch(base_path + path, {
+          method: 'DELETE',
+        })
+        .then((response) => {
+          console.log(response);
+          if (!response.ok) {
+            throw new Error("Removing file failed");
+          }
+        })
+        .then(() => {
+          listFiles();
+        });
+      }
+
+      function deleteFileButton(path) {
+        if (confirm("Are you sure you wanna delete " + path + "?")) {
+          deleteFile(path);
+        }
+      }
+
+      function createDirectory(path) {
+        fetch(base_path + path, {
+          method: 'MKCOL',
+        })
+        .then((response) => {
+          console.log(response);
+          if (!response.ok) {
+            throw new Error("Creating directory failed");
+          }
+        })
+        .then(() => {
+          listFiles();
+        });
+      }
+
+      function createDirectoryButton() {
+        let dir_name = prompt("Directory name");
+        if (dir_name !== null) {
+          createDirectory(current_path + dir_name + "/");
+        }
+      }
+
+      function uploadFile(path, file) {
+
+        fetch(base_path + path, {
+          method: 'PUT',
+          body: file,
+        })
+        .then((response) => {
+          console.log(response);
+          if (!response.ok) {
+            throw new Error("Upload failed");
+          }
+        })
+        .then(() => {
+          listFiles();
+        });
+      }
+
+      function uploadFilesButton() {
+        let files = document.getElementById("files").files;
+        for (let i = 0; i < files.length; i++) {
+          uploadFile(current_path + files[i].name, files[i]);
+        }
+        document.getElementById("files").value = "";
+      }
+
+      function moveFile(old_path, new_path) {
+        fetch(base_path + old_path, {
+          method: 'MOVE',
+          headers: {
+            'Destination': base_path + new_path,
+          },
+        })
+        .then((response) => {
+          console.log(response);
+          if (!response.ok) {   
+            throw new Error("Rename file failed");
+          }
+        })
+        .then(() => {
+          listFiles();
+        });
+      }
+
+      function moveFileButton(path) {
+        let new_path = prompt("New file path", path);
+        moveFile(path, new_path);
+      }
+
+      function renameFileButton(path) {
+        let new_name = prompt("New file name");
+        moveFile(path, current_path + new_name);
+      }
+
+      function copyFile(path, new_path) {
+        fetch(base_path + path, {
+          method: 'COPY',
+          headers: {
+            'Destination': base_path + new_path,
+          },
+        })
+        .then((response) => {
+          console.log(response);
+          if (!response.ok) {
+            throw new Error("Copy file failed");
+          }
+        })
+        .then(() => {
+          listFiles();
+        });
+      }
+
+      function duplicateFileButton(path) {
+        let new_path = prompt("New file path", path);
+        copyFile(path, new_path);
+      }
+
+      function listFiles() {
+        listing_field.innerHTML = '<div class="loading"></div>';
+
+        path_field.innerHTML = '<a href="#/">⌂</a>';
+
+        let path_blocks = current_path.split('/').slice(1);
+        for (let i = 0; i < path_blocks.length; i++) {
+          path_field.innerHTML += ' / <a href="#/' + path_blocks.slice(0, i+1).join('/') + '/">' + path_blocks[i] + '</a>';
+        }
+
+        fetch(base_path + current_path)
+        .then((response) => {
+          console.log(response);
+          if (!response.ok) {
+            throw new Error("Can't fetch directory");
+          }
+          return response.json();
+        })
+        .then((listing) => {
+          console.log(listing);
+          let out = '<div class="listing">';
+          for (let i = 0; i < listing.length; i++) {
+            let listing_path = current_path + encodeURIComponent(listing[i]["name"]);
+            let listing_link = "#";
+            let listing_icon = "";
+            if (listing[i]["type"] === "directory") {
+              listing_path += '/';
+              listing_link = '#' + listing_path;
+              listing_icon = "📁";
+            }
+            else {
+              listing_link = base_path + listing_path;
+            }
+            out += '<div class="listing-item">'
+                      + '<div class="detail">'
+                        + '<a href="' + listing_link + '">' + listing_icon + " " + listing[i]["name"] + '</a>'
+                      + '</div>'
+                      + '<div class="options">'
+                        + '<button onclick="renameFileButton(\'' + listing_path + '\');" alt="rename">✎</button>'
+                        + '<button onclick="duplicateFileButton(\'' + listing_path + '\');">●●</button>'
+                        + '<button onclick="moveFileButton(\'' + listing_path + '\');">⇨</button>'
+                        + '<button onclick="deleteFileButton(\'' + listing_path + '\');">❌</button>'
+                      + '</div>'
+                    + '</div>';
+          }
+          out += '</div>';
+          listing_field.innerHTML = out;
+        });
+      }
+
+      listFiles();
+
+      window.onhashchange = () => {
+        current_path = window.location.hash.substring(1);
+        listFiles();
+      }
+    </script>
+  </body>
+</html>