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