update
This commit is contained in:
parent
395dfab938
commit
da20329942
15
allowed_secrets.json
Normal file
15
allowed_secrets.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"test": {
|
||||||
|
"expiry": "31.03.2025",
|
||||||
|
"file_root": "/mp3_root"
|
||||||
|
},
|
||||||
|
"fh48750p353": {
|
||||||
|
"expiry": "20.04.2025",
|
||||||
|
"file_root": "/mp3_root"
|
||||||
|
},
|
||||||
|
"another_secret": {
|
||||||
|
"expiry": "30.04.2025",
|
||||||
|
"file_root": "/mp3_root/another"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
67
app.py
67
app.py
@ -4,45 +4,63 @@ from PIL import Image
|
|||||||
import io
|
import io
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
import mimetypes
|
import mimetypes
|
||||||
from datetime import datetime
|
from datetime import datetime, date
|
||||||
from urllib.parse import unquote
|
from urllib.parse import unquote
|
||||||
import diskcache
|
import diskcache
|
||||||
|
import json
|
||||||
cache = diskcache.Cache('./filecache', size_limit= 32 * 1024**3) # 32 GB limit
|
cache = diskcache.Cache('./filecache', size_limit= 32 * 1024**3) # 32 GB limit
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
# Use a raw string for the UNC path.
|
# Use a raw string for the default FILE_ROOT path.
|
||||||
app.config['MP3_ROOT'] = r'/mp3_root'
|
app.config['FILE_ROOT'] = r'/mp3_root'
|
||||||
# app.config['MP3_ROOT'] = r'\\192.168.10.10\docker2\sync-bethaus\syncfiles\folders'
|
app.config['SECRET_KEY'] = '85c1117eb3a5f2c79f0ff395bada8ff8d9a257b99ef5e143'
|
||||||
app.config['SECRET_KEY'] = os.urandom(24)
|
|
||||||
app.config['ALLOWED_SECRETS'] = {
|
def load_allowed_secrets(filename='allowed_secrets.json'):
|
||||||
'test': datetime(2026, 3, 31, 23, 59, 59),
|
with open(filename) as f:
|
||||||
'another_secret': datetime(2024, 4, 30, 23, 59, 59)
|
secrets = json.load(f)
|
||||||
}
|
for key, value in secrets.items():
|
||||||
|
if 'expiry' in value:
|
||||||
|
value['expiry'] = datetime.strptime(value['expiry'], '%d.%m.%Y').date()
|
||||||
|
return secrets
|
||||||
|
|
||||||
|
app.config['ALLOWED_SECRETS'] = load_allowed_secrets()
|
||||||
|
|
||||||
def require_secret(f):
|
def require_secret(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def decorated_function(*args, **kwargs):
|
def decorated_function(*args, **kwargs):
|
||||||
allowed_secrets = app.config['ALLOWED_SECRETS']
|
allowed_secrets = app.config['ALLOWED_SECRETS']
|
||||||
current_secret = session.get('secret')
|
current_secret = session.get('secret')
|
||||||
now = datetime.now()
|
today = date.today()
|
||||||
|
# be nice if somebody hit you without a secret (no error)
|
||||||
|
if current_secret is None:
|
||||||
|
return render_template('index.html')
|
||||||
|
|
||||||
def is_valid(secret):
|
|
||||||
expiry_date = allowed_secrets.get(secret)
|
|
||||||
return expiry_date and now <= expiry_date
|
|
||||||
|
|
||||||
# Check if secret from session is still valid
|
def is_valid(secret_data):
|
||||||
if current_secret and is_valid(current_secret):
|
expiry_date = secret_data.get('expiry')
|
||||||
return f(*args, **kwargs)
|
return expiry_date and today <= expiry_date
|
||||||
|
|
||||||
|
# Check if the secret stored in session is still valid
|
||||||
|
if current_secret:
|
||||||
|
secret_data = allowed_secrets.get(current_secret)
|
||||||
|
if secret_data and is_valid(secret_data):
|
||||||
|
# Update FILE_ROOT based on the secret's configuration
|
||||||
|
app.config['FILE_ROOT'] = secret_data.get('file_root')
|
||||||
|
return f(*args, **kwargs)
|
||||||
|
|
||||||
# Check secret from GET parameter
|
# Check secret from GET parameter
|
||||||
secret = request.args.get('secret')
|
secret = request.args.get('secret')
|
||||||
if secret and is_valid(secret):
|
if secret:
|
||||||
session['secret'] = secret
|
secret_data = allowed_secrets.get(secret)
|
||||||
return f(*args, **kwargs)
|
if secret_data and is_valid(secret_data):
|
||||||
|
session['secret'] = secret
|
||||||
|
app.config['FILE_ROOT'] = secret_data.get('file_root')
|
||||||
|
return f(*args, **kwargs)
|
||||||
|
|
||||||
# If secret invalid or expired
|
# If the secret is invalid or expired, show an error
|
||||||
return render_template('error.html', message="Invalid or expired secret."), 403
|
return render_template('error.html', message="Invalid or expired secret."), 403
|
||||||
|
|
||||||
return decorated_function
|
return decorated_function
|
||||||
|
|
||||||
@app.route('/static/icons/<string:size>.png')
|
@app.route('/static/icons/<string:size>.png')
|
||||||
@ -156,8 +174,8 @@ def generate_breadcrumbs(subpath):
|
|||||||
@app.route('/api/path/<path:subpath>')
|
@app.route('/api/path/<path:subpath>')
|
||||||
@require_secret
|
@require_secret
|
||||||
def api_browse(subpath):
|
def api_browse(subpath):
|
||||||
mp3_root = app.config['MP3_ROOT']
|
file_root = app.config['FILE_ROOT']
|
||||||
directory = os.path.join(mp3_root, subpath.replace('/', os.sep))
|
directory = os.path.join(file_root, subpath.replace('/', os.sep))
|
||||||
|
|
||||||
if not os.path.isdir(directory):
|
if not os.path.isdir(directory):
|
||||||
return jsonify({'error': 'Directory not found'}), 404
|
return jsonify({'error': 'Directory not found'}), 404
|
||||||
@ -175,7 +193,7 @@ def api_browse(subpath):
|
|||||||
@require_secret
|
@require_secret
|
||||||
def serve_file(filename):
|
def serve_file(filename):
|
||||||
decoded_filename = unquote(filename).replace('/', os.sep)
|
decoded_filename = unquote(filename).replace('/', os.sep)
|
||||||
full_path = os.path.normpath(os.path.join(app.config['MP3_ROOT'], decoded_filename))
|
full_path = os.path.normpath(os.path.join(app.config['FILE_ROOT'], decoded_filename))
|
||||||
|
|
||||||
if not os.path.isfile(full_path):
|
if not os.path.isfile(full_path):
|
||||||
app.logger.error(f"File not found: {full_path}")
|
app.logger.error(f"File not found: {full_path}")
|
||||||
@ -228,7 +246,7 @@ def serve_file(filename):
|
|||||||
@require_secret
|
@require_secret
|
||||||
def get_transcript(filename):
|
def get_transcript(filename):
|
||||||
fs_filename = filename.replace('/', os.sep)
|
fs_filename = filename.replace('/', os.sep)
|
||||||
full_path = os.path.join(app.config['MP3_ROOT'], fs_filename)
|
full_path = os.path.join(app.config['FILE_ROOT'], fs_filename)
|
||||||
|
|
||||||
if not os.path.isfile(full_path):
|
if not os.path.isfile(full_path):
|
||||||
return "Transcription not found", 404
|
return "Transcription not found", 404
|
||||||
@ -242,7 +260,6 @@ def get_transcript(filename):
|
|||||||
@app.route('/<path:path>')
|
@app.route('/<path:path>')
|
||||||
@require_secret
|
@require_secret
|
||||||
def index(path):
|
def index(path):
|
||||||
# The SPA template (browse.html) will read the current URL and load the correct directory.
|
|
||||||
return render_template("browse.html")
|
return render_template("browse.html")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
version: "3"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
flask-app:
|
flask-app:
|
||||||
image: python:3.11-slim
|
image: python:3.11-slim
|
||||||
@ -14,7 +16,38 @@ services:
|
|||||||
- FLASK_RUN_HOST=0.0.0.0
|
- FLASK_RUN_HOST=0.0.0.0
|
||||||
- FLASK_ENV=production
|
- FLASK_ENV=production
|
||||||
- MP3_ROOT=/mp3_root
|
- MP3_ROOT=/mp3_root
|
||||||
ports:
|
networks:
|
||||||
- "5000:5000"
|
- traefik
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
|
||||||
|
# ----------------------------------------------------
|
||||||
|
# HTTP router: Listen on entrypoint "web" (port 80)
|
||||||
|
# and apply a middleware to force redirect to HTTPS
|
||||||
|
# ----------------------------------------------------
|
||||||
|
- "traefik.http.routers.bethaus-app.rule=Host(`app.bethaus-speyer.de`)"
|
||||||
|
- "traefik.http.routers.bethaus-app.entrypoints=web"
|
||||||
|
- "traefik.http.routers.bethaus-app.middlewares=redirect-to-https"
|
||||||
|
|
||||||
|
# This is the "redirect-to-https" middleware definition
|
||||||
|
- "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
|
||||||
|
|
||||||
|
# -----------------------------------------------------
|
||||||
|
# HTTPS router: Listen on entrypoint "websecure"
|
||||||
|
# using TLS via your ACME (Let's Encrypt) resolver
|
||||||
|
# -----------------------------------------------------
|
||||||
|
- "traefik.http.routers.bethaus-app-secure.rule=Host(`app.bethaus-speyer.de`)"
|
||||||
|
- "traefik.http.routers.bethaus-app-secure.entrypoints=websecure"
|
||||||
|
- "traefik.http.routers.bethaus-app-secure.tls=true"
|
||||||
|
- "traefik.http.routers.bethaus-app-secure.tls.certresolver=myresolver"
|
||||||
|
|
||||||
|
# The service’s internal port
|
||||||
|
- "traefik.http.services.bethaus-app.loadbalancer.server.port=5000"
|
||||||
|
|
||||||
command: >
|
command: >
|
||||||
sh -c "pip install -r requirements.txt && flask run"
|
sh -c "pip install -r requirements.txt && flask run"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
traefik:
|
||||||
|
external: true
|
||||||
|
|
||||||
|
|||||||
@ -62,7 +62,7 @@ function renderContent(data) {
|
|||||||
// Add a data-index attribute for music files.
|
// Add a data-index attribute for music files.
|
||||||
const indexAttr = file.file_type === 'music' ? ` data-index="${currentMusicFiles.length - 1}"` : '';
|
const indexAttr = file.file_type === 'music' ? ` data-index="${currentMusicFiles.length - 1}"` : '';
|
||||||
contentHTML += `<li class="file-item">
|
contentHTML += `<li class="file-item">
|
||||||
<a href="#" class="play-file"${indexAttr} data-url="${file.path}" data-file-type="${file.file_type}">${symbol} ${file.name}</a>`;
|
<a href="#" class="play-file"${indexAttr} data-url="${file.path}" data-file-type="${file.file_type}">${symbol} ${file.name.replace('.mp3', '')}</a>`;
|
||||||
if (file.has_transcript) {
|
if (file.has_transcript) {
|
||||||
contentHTML += `<a href="#" class="show-transcript" data-url="${file.transcript_url}" title="Show Transcript">📄</a>`;
|
contentHTML += `<a href="#" class="show-transcript" data-url="${file.transcript_url}" title="Show Transcript">📄</a>`;
|
||||||
}
|
}
|
||||||
@ -187,7 +187,7 @@ document.querySelectorAll('.play-file').forEach(link => {
|
|||||||
const pathParts = relUrl.split('/');
|
const pathParts = relUrl.split('/');
|
||||||
const fileName = pathParts.pop();
|
const fileName = pathParts.pop();
|
||||||
const pathStr = pathParts.join('/');
|
const pathStr = pathParts.join('/');
|
||||||
nowPlayingInfo.innerHTML = pathStr + '<br><span style="font-size: larger; font-weight: bold;">' + fileName + '</span>';
|
nowPlayingInfo.innerHTML = pathStr.replace(/\//g, ' > ') + '<br><span style="font-size: larger; font-weight: bold;">' + fileName.replace('.mp3', '') + '</span>';
|
||||||
preload_audio();
|
preload_audio();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@ -17,9 +17,9 @@ body {
|
|||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
}
|
}
|
||||||
.container {
|
.container {
|
||||||
|
width: 90%;
|
||||||
max-width: 900px;
|
max-width: 900px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 10px;
|
|
||||||
padding-bottom: 200px;
|
padding-bottom: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,7 +69,7 @@ li {
|
|||||||
}
|
}
|
||||||
.directories-grid .directory-item {
|
.directories-grid .directory-item {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
padding: 15px;
|
padding: 15px 10px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
||||||
|
|||||||
@ -2,6 +2,12 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
|
|
||||||
|
<meta property="og:title" content="Gottesdienste Speyer und Schwegenheim" />
|
||||||
|
<meta property="og:description" content="... uns aber, die wir gerettet werden, ist es eine Gotteskraft." />
|
||||||
|
<meta property="og:image" content="https://app.bethaus-speyer.de/static/icons/logo-200x200.png" />
|
||||||
|
<meta property="og:url" content="https://app.bethaus-speyer.de" />
|
||||||
|
|
||||||
<title>Gottesdienste</title>
|
<title>Gottesdienste</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link rel="icon" href="/static/icons/logo-192x192.png" type="image/png" sizes="192x192">
|
<link rel="icon" href="/static/icons/logo-192x192.png" type="image/png" sizes="192x192">
|
||||||
@ -23,11 +29,35 @@
|
|||||||
<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
|
<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='gallery.css') }}">
|
<link rel="stylesheet" href="{{ url_for('static', filename='gallery.css') }}">
|
||||||
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
||||||
|
<style>
|
||||||
|
/* Header styles */
|
||||||
|
.site-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px 20px;
|
||||||
|
background-color: #9bbbff; /* Using the theme color */
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
.site-header img.logo {
|
||||||
|
height: 50px;
|
||||||
|
margin-right: 15px;
|
||||||
|
}
|
||||||
|
.site-header h1 {
|
||||||
|
font-size: 1.5em;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<header class="site-header">
|
||||||
|
<img src="https://app.bethaus-speyer.de/static/icons/logo-300x300.png" alt="Logo" class="logo">
|
||||||
|
<h1>Gottesdienste Speyer und Schwegenheim</h1>
|
||||||
|
</header>
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h2>Gottesdienste Speyer und Schwegenheim</h2>
|
|
||||||
<div id="breadcrumbs" class="breadcrumb"></div>
|
<div id="breadcrumbs" class="breadcrumb"></div>
|
||||||
<div id="content"></div>
|
<div id="content"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,16 +1,48 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>Keine Berechtigung</title>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta property="og:title" content="Gottesdienste Speyer und Schwegenheim" />
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
|
<meta property="og:description" content="... uns aber, die wir gerettet werden, ist es eine Gotteskraft." />
|
||||||
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
<meta property="og:image" content="https://app.bethaus-speyer.de/static/icons/logo-300x300.png" />
|
||||||
|
<meta property="og:url" content="https://app.bethaus-speyer.de" />
|
||||||
|
<meta property="og:type" content="website" />
|
||||||
|
|
||||||
|
<title>Gottesdienste Speyer und Schwegenheim</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
||||||
|
<style>
|
||||||
|
/* Header styles */
|
||||||
|
.site-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px 20px;
|
||||||
|
background-color: #9bbbff; /* Using the theme color */
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
.site-header img.logo {
|
||||||
|
height: 50px;
|
||||||
|
margin-right: 15px;
|
||||||
|
}
|
||||||
|
.site-header h1 {
|
||||||
|
font-size: 1.5em;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<header class="site-header">
|
||||||
|
<img src="https://app.bethaus-speyer.de/static/icons/logo-300x300.png" alt="Logo" class="logo">
|
||||||
|
<h1>Gottesdienste Speyer und Schwegenheim</h1>
|
||||||
|
</header>
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h2>Keine Berechtigung</h2>
|
<div id="content">Bitte den Link aus der Telegram-Gruppe erneut anklicken.</div>
|
||||||
<div id="content">Bitte mit Link aus Telegram-Gruppe erneut anklicken.</div>
|
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
47
templates/index.html
Normal file
47
templates/index.html
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
|
||||||
|
<meta property="og:title" content="Gottesdienste Speyer und Schwegenheim" />
|
||||||
|
<meta property="og:description" content="... uns aber, die wir gerettet werden, ist es eine Gotteskraft." />
|
||||||
|
<meta property="og:image" content="https://app.bethaus-speyer.de/static/icons/logo-300x300.png" />
|
||||||
|
<meta property="og:url" content="https://app.bethaus-speyer.de" />
|
||||||
|
<meta property="og:type" content="website" />
|
||||||
|
|
||||||
|
<title>Gottesdienste Speyer und Schwegenheim</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
||||||
|
<style>
|
||||||
|
.site-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px 20px;
|
||||||
|
background-color: #9bbbff;
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
.site-header img.logo {
|
||||||
|
height: 50px;
|
||||||
|
margin-right: 15px;
|
||||||
|
}
|
||||||
|
.site-header h1 {
|
||||||
|
font-size: 1.5em;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header class="site-header">
|
||||||
|
<img src="https://app.bethaus-speyer.de/static/icons/logo-300x300.png" alt="Logo" class="logo">
|
||||||
|
<h1>Gottesdienste Speyer und Schwegenheim</h1>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div id="content">Bitte den Link aus der Telegram-Gruppe erneut anklicken.</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Loading…
x
Reference in New Issue
Block a user