Add login functionality with password protection and create login page

This commit is contained in:
zwitschi committed 2026-07-24 23:59:03 +02:00
1 parent 443ed74093
commit c237564f61
4 files changed
+205 -2

No files matched your search

+45 -1
View File
@@ -12,15 +12,26 @@ play videos from different camera views.
import os
import re
import time
import secrets
from functools import wraps
from dotenv import load_dotenv
from flask import Flask, render_template, send_from_directory, abort, jsonify
from flask import (
Flask, render_template, send_from_directory, abort, jsonify,
request, session, redirect, url_for
)
# Load variables from a .env file if present (does not override real env vars)
load_dotenv()
app = Flask(__name__)
# Session secret key — MUST be set in production. Generate with: python -c "import secrets; print(secrets.token_hex(32))"
app.secret_key = os.environ.get('SECRET_KEY', secrets.token_hex(32))
# Password for web login. Default: 'patreon'. Override via LOGIN_PASSWORD env var.
LOGIN_PASSWORD = os.environ.get('LOGIN_PASSWORD', 'patreon')
# Root TeslaCam directory containing RecentClips, SavedClips, SentryClips, etc.
# Override via the TESLACAM_DIRECTORY environment variable (see .env.example).
TESLACAM_DIRECTORY = os.environ.get('TESLACAM_DIRECTORY', '/mnt/TeslaCam')
@@ -113,7 +124,37 @@ def get_camera_display_name(camera_view):
return CAMERA_VIEW_MAPPING.get(camera_view, camera_view.replace('_', ' ').title())
def login_required(f):
"""Decorator: redirect to /login if not authenticated."""
@wraps(f)
def decorated_function(*args, **kwargs):
if not session.get('logged_in'):
return redirect(url_for('login'))
return f(*args, **kwargs)
return decorated_function
@app.route('/login', methods=['GET', 'POST'])
def login():
"""Simple password-based login page."""
error = None
if request.method == 'POST':
if request.form.get('password') == LOGIN_PASSWORD:
session['logged_in'] = True
return redirect(url_for('index'))
error = 'Invalid password. Please try again.'
return render_template('login.html', error=error)
@app.route('/logout')
def logout():
"""Clear session and return to login page."""
session.pop('logged_in', None)
return redirect(url_for('login'))
@app.route('/')
@login_required
def index():
"""
Main page listing all videos categorized by human-readable camera view.
@@ -133,6 +174,7 @@ def index():
@app.route('/camera/<camera_view>')
@login_required
def camera_view(camera_view):
"""
Displays all available videos for a single camera view.
@@ -150,6 +192,7 @@ def camera_view(camera_view):
@app.route('/video/<path:filepath>')
@login_required
def serve_video(filepath):
"""
Safely streams video files from the TeslaCam directory structure.
@@ -162,6 +205,7 @@ def serve_video(filepath):
@app.route('/refresh')
@login_required
def refresh():
"""
Forces a re-scan of the TeslaCam directory, bypassing the cache TTL.