From 7e4df5c34c130403d34e45d8867e3a65b86986f2 Mon Sep 17 00:00:00 2001 From: Denis Lehmann Date: Sun, 10 Apr 2022 23:39:47 +0200 Subject: [PATCH] initial commit --- flake.lock | 43 ++++++++++++ flake.nix | 27 ++++++++ raincloud.py | 126 ++++++++++++++++++++++++++++++++++++ static/favicon.png | Bin 0 -> 2242 bytes static/style.css | 3 + templates/authenticate.html | 7 ++ templates/base.html | 7 ++ templates/files.html | 19 ++++++ 8 files changed, 232 insertions(+) create mode 100644 flake.lock create mode 100644 flake.nix create mode 100755 raincloud.py create mode 100644 static/favicon.png create mode 100644 static/style.css create mode 100644 templates/authenticate.html create mode 100644 templates/base.html create mode 100644 templates/files.html diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..d8c1ef6 --- /dev/null +++ b/flake.lock @@ -0,0 +1,43 @@ +{ + "nodes": { + "flake-utils": { + "locked": { + "lastModified": 1648297722, + "narHash": "sha256-W+qlPsiZd8F3XkzXOzAoR+mpFqzm3ekQkJNa+PIh1BQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "0f8662f1319ad6abf89b3380dd2722369fc51ade", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1649225869, + "narHash": "sha256-u1zLtPmQzhT9mNXyM8Ey9pk7orDrIKdwooeGDEXm5xM=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "b6966d911da89e5a7301aaef8b4f0a44c77e103c", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..49d1596 --- /dev/null +++ b/flake.nix @@ -0,0 +1,27 @@ +{ + description = "Stream media files over SSH"; + nixConfig.bash-prompt = "\[\\e[1mmincloud-dev\\e[0m:\\w\]$ "; + inputs = { + nixpkgs.url = github:nixos/nixpkgs/nixos-unstable; + flake-utils.url = github:numtide/flake-utils; + }; + + outputs = { self, nixpkgs, flake-utils }: + + flake-utils.lib.eachDefaultSystem + (system: + let + pkgs = nixpkgs.legacyPackages.${system}; + in + { + # Development shell + devShell = pkgs.mkShell { + buildInputs = with pkgs; [ + python3 + python3Packages.flask + python3Packages.toml + ]; + }; + } + ); +} diff --git a/raincloud.py b/raincloud.py new file mode 100755 index 0000000..4be21c8 --- /dev/null +++ b/raincloud.py @@ -0,0 +1,126 @@ +#!/usr/bin/env python + +import toml +import werkzeug +from flask import ( + Flask, + render_template, + redirect, + request, + send_from_directory, + session, + url_for, +) +from pathlib import Path +from werkzeug.utils import secure_filename + +base_path = Path("public") +cloud_name = "raincloud" + + +class MincloudIOException(Exception): + pass + + +def get_config(directory): + """Load a 'mincloud.conf' file from given directory. + + Parameters: + directory - basepath of 'mincloud.conf' + + Returns: Dictionary of config parameters + """ + path = base_path / directory / "rc.toml" + + if path.exists(): + config = {} + config["directory"] = directory + + parsed_config = toml.load(path) + config["password"] = ( + parsed_config["password"] if "password" in parsed_config else None + ) + config["download"] = ( + parsed_config["download"] if "download" in parsed_config else False + ) + config["upload"] = ( + parsed_config["upload"] if "upload" in parsed_config else False + ) + return config + + else: + raise MincloudIOException("No raincloud directory") + + +def get_files(directory): + path = base_path / directory + file_paths = [f for f in path.glob("*") if f.is_file()] + files = [] + for p in file_paths: + if p.name != "rc.toml": + files.append({"name": p.name}) + return files + + +app = Flask(__name__) + + +@app.route("/", methods=["GET", "POST"]) +@app.route("//", methods=["GET"]) +def files(directory, filename=None): + + try: + config = get_config(directory) + if config["password"]: + authenticated = False + if directory in session and session[directory] == config["password"]: + authenticated = True + + if not authenticated: + if request.method == "POST": + if request.form["password"] == config["password"]: + session[directory] = config["password"] + return redirect(url_for("files", directory=directory)) + else: + return render_template( + "authenticate.html", cloud_name=cloud_name, config=config + ) + + if request.method == "GET": + # List + if not filename: + files = get_files(directory) + return render_template( + "files.html", cloud_name=cloud_name, config=config, files=files + ) + + # Download + else: + if config["download"] and filename != "rc.toml": + return send_from_directory(base_path / directory, filename) + else: + return "Not allowed" + + # Upload + elif request.method == "POST": + if config["upload"]: + f = request.files["file"] + filename = secure_filename(f.filename) + if filename != "rc.toml": + f.save(base_path / directory / filename) + + # Return new file list + files = get_files(directory) + return render_template( + "files.html", cloud_name=cloud_name, config=config, files=files + ) + else: + return "No upload allowed" + + except MincloudIOException as e: + print(e) + return "No 404 file" + + +app.secret_key = "raincloud" +app.run(host="0.0.0.0", debug=True) diff --git a/static/favicon.png b/static/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..e5d3ba18ebeaa2c7a48d937a9d56c88eeab74653 GIT binary patch literal 2242 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 + + + +{% endblock %} diff --git a/templates/base.html b/templates/base.html new file mode 100644 index 0000000..10ce608 --- /dev/null +++ b/templates/base.html @@ -0,0 +1,7 @@ + + + + +{{ config["directory"] }} - {{ cloud_name }} +

{{ config["directory"] }}

+{% block content %}{% endblock %} diff --git a/templates/files.html b/templates/files.html new file mode 100644 index 0000000..1a423f1 --- /dev/null +++ b/templates/files.html @@ -0,0 +1,19 @@ +{% extends "base.html" %} +{% block content %} + {% if config["upload"] %} +
+ + +
+ {% endif %} +
    + {% for f in files %} +
  • + {{ f["name"] }} + {% if config["download"] %} + Download + {% endif %} +
  • + {% endfor %} +
+{% endblock %}