initial commit
This commit is contained in:
commit
7e4df5c34c
8 changed files with 232 additions and 0 deletions
43
flake.lock
generated
Normal file
43
flake.lock
generated
Normal 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
27
flake.nix
Normal 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
126
raincloud.py
Executable 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
BIN
static/favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.2 KiB |
3
static/style.css
Normal file
3
static/style.css
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
body {
|
||||||
|
font-family: sans-serif;
|
||||||
|
}
|
||||||
7
templates/authenticate.html
Normal file
7
templates/authenticate.html
Normal 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
7
templates/base.html
Normal 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
19
templates/files.html
Normal 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 %}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue