initial commit

This commit is contained in:
Denis Lehmann 2022-04-10 23:39:47 +02:00
commit 7e4df5c34c
8 changed files with 232 additions and 0 deletions

43
flake.lock generated Normal file
View file

@ -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
}

27
flake.nix Normal file
View file

@ -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
];
};
}
);
}

126
raincloud.py Executable file
View file

@ -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("/<directory>", methods=["GET", "POST"])
@app.route("/<directory>/<path:filename>", 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)

BIN
static/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

3
static/style.css Normal file
View file

@ -0,0 +1,3 @@
body {
font-family: sans-serif;
}

View file

@ -0,0 +1,7 @@
{% extends "base.html" %}
{% block content %}
<form action="/{{ config["directory"] }}" method="post">
<input type="password" name="password">
<input type="submit">
</form>
{% endblock %}

7
templates/base.html Normal file
View file

@ -0,0 +1,7 @@
<!doctype html>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.png') }}">
<title>{{ config["directory"] }} - {{ cloud_name }}</title>
<h1>{{ config["directory"] }}</h1>
{% block content %}{% endblock %}

19
templates/files.html Normal file
View file

@ -0,0 +1,19 @@
{% extends "base.html" %}
{% block content %}
{% if config["upload"] %}
<form action="/{{ config["directory"] }}" enctype="multipart/form-data" method="post">
<input type="file" name="file">
<input type="submit">
</form>
{% endif %}
<ul>
{% for f in files %}
<li>
<span>{{ f["name"] }} </span>
{% if config["download"] %}
<a href="{{ config["directory"] }}/{{ f["name"] }}">Download</a>
{% endif %}
</li>
{% endfor %}
</ul>
{% endblock %}