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"
|
||||
}
|
||||
}
|
||||
|
||||
61
app.py
61
app.py
@ -4,45 +4,63 @@ from PIL import Image
|
||||
import io
|
||||
from functools import wraps
|
||||
import mimetypes
|
||||
from datetime import datetime
|
||||
from datetime import datetime, date
|
||||
from urllib.parse import unquote
|
||||
import diskcache
|
||||
import json
|
||||
cache = diskcache.Cache('./filecache', size_limit= 32 * 1024**3) # 32 GB limit
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
# Use a raw string for the UNC path.
|
||||
app.config['MP3_ROOT'] = r'/mp3_root'
|
||||
# app.config['MP3_ROOT'] = r'\\192.168.10.10\docker2\sync-bethaus\syncfiles\folders'
|
||||
app.config['SECRET_KEY'] = os.urandom(24)
|
||||
app.config['ALLOWED_SECRETS'] = {
|
||||
'test': datetime(2026, 3, 31, 23, 59, 59),
|
||||
'another_secret': datetime(2024, 4, 30, 23, 59, 59)
|
||||
}
|
||||
# Use a raw string for the default FILE_ROOT path.
|
||||
app.config['FILE_ROOT'] = r'/mp3_root'
|
||||
app.config['SECRET_KEY'] = '85c1117eb3a5f2c79f0ff395bada8ff8d9a257b99ef5e143'
|
||||
|
||||
def load_allowed_secrets(filename='allowed_secrets.json'):
|
||||
with open(filename) as f:
|
||||
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):
|
||||
@wraps(f)
|
||||
def decorated_function(*args, **kwargs):
|
||||
allowed_secrets = app.config['ALLOWED_SECRETS']
|
||||
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
|
||||
if current_secret and is_valid(current_secret):
|
||||
def is_valid(secret_data):
|
||||
expiry_date = secret_data.get('expiry')
|
||||
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
|
||||
secret = request.args.get('secret')
|
||||
if secret and is_valid(secret):
|
||||
if secret:
|
||||
secret_data = allowed_secrets.get(secret)
|
||||
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 decorated_function
|
||||
|
||||
@app.route('/static/icons/<string:size>.png')
|
||||
@ -156,8 +174,8 @@ def generate_breadcrumbs(subpath):
|
||||
@app.route('/api/path/<path:subpath>')
|
||||
@require_secret
|
||||
def api_browse(subpath):
|
||||
mp3_root = app.config['MP3_ROOT']
|
||||
directory = os.path.join(mp3_root, subpath.replace('/', os.sep))
|
||||
file_root = app.config['FILE_ROOT']
|
||||
directory = os.path.join(file_root, subpath.replace('/', os.sep))
|
||||
|
||||
if not os.path.isdir(directory):
|
||||
return jsonify({'error': 'Directory not found'}), 404
|
||||
@ -175,7 +193,7 @@ def api_browse(subpath):
|
||||
@require_secret
|
||||
def serve_file(filename):
|
||||
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):
|
||||
app.logger.error(f"File not found: {full_path}")
|
||||
@ -228,7 +246,7 @@ def serve_file(filename):
|
||||
@require_secret
|
||||
def get_transcript(filename):
|
||||
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):
|
||||
return "Transcription not found", 404
|
||||
@ -242,7 +260,6 @@ def get_transcript(filename):
|
||||
@app.route('/<path:path>')
|
||||
@require_secret
|
||||
def index(path):
|
||||
# The SPA template (browse.html) will read the current URL and load the correct directory.
|
||||
return render_template("browse.html")
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
flask-app:
|
||||
image: python:3.11-slim
|
||||
@ -14,7 +16,38 @@ services:
|
||||
- FLASK_RUN_HOST=0.0.0.0
|
||||
- FLASK_ENV=production
|
||||
- MP3_ROOT=/mp3_root
|
||||
ports:
|
||||
- "5000:5000"
|
||||
networks:
|
||||
- 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: >
|
||||
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.
|
||||
const indexAttr = file.file_type === 'music' ? ` data-index="${currentMusicFiles.length - 1}"` : '';
|
||||
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) {
|
||||
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 fileName = pathParts.pop();
|
||||
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();
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
@ -17,9 +17,9 @@ body {
|
||||
min-height: 100%;
|
||||
}
|
||||
.container {
|
||||
width: 90%;
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
padding: 10px;
|
||||
padding-bottom: 200px;
|
||||
}
|
||||
|
||||
@ -69,7 +69,7 @@ li {
|
||||
}
|
||||
.directories-grid .directory-item {
|
||||
background-color: #fff;
|
||||
padding: 15px;
|
||||
padding: 15px 10px;
|
||||
border-radius: 5px;
|
||||
text-align: center;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
||||
|
||||
@ -2,6 +2,12 @@
|
||||
<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-200x200.png" />
|
||||
<meta property="og:url" content="https://app.bethaus-speyer.de" />
|
||||
|
||||
<title>Gottesdienste</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<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='gallery.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>
|
||||
<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="container">
|
||||
<h2>Gottesdienste Speyer und Schwegenheim</h2>
|
||||
<div id="breadcrumbs" class="breadcrumb"></div>
|
||||
<div id="content"></div>
|
||||
</div>
|
||||
|
||||
@ -2,15 +2,47 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Keine Berechtigung</title>
|
||||
|
||||
<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>
|
||||
/* 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>
|
||||
<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">
|
||||
<h2>Keine Berechtigung</h2>
|
||||
<div id="content">Bitte mit Link aus Telegram-Gruppe erneut anklicken.</div>
|
||||
<div id="content">Bitte den Link aus der Telegram-Gruppe erneut anklicken.</div>
|
||||
</div>
|
||||
</body>
|
||||
</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