chore: automatic commit 2025-04-30 12:48
This commit is contained in:
41
utils/__init__.py
Normal file
41
utils/__init__.py
Normal file
@@ -0,0 +1,41 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Utility functions for the website application.
|
||||
This package contains various utilities for database management,
|
||||
user management, and server administration.
|
||||
"""
|
||||
|
||||
# Import utilities that don't depend on app.py first
|
||||
from .db_check import check_db_connection, initialize_db_if_needed
|
||||
|
||||
# Define the list of all available utilities
|
||||
__all__ = [
|
||||
# Database utilities
|
||||
'check_db_connection',
|
||||
'initialize_db_if_needed',
|
||||
'fix_database_schema',
|
||||
'rebuild_database',
|
||||
'test_database_connection',
|
||||
'test_models',
|
||||
'print_database_stats',
|
||||
'run_all_tests',
|
||||
|
||||
# User management
|
||||
'list_users',
|
||||
'create_user',
|
||||
'reset_password',
|
||||
'delete_user',
|
||||
'create_admin_user',
|
||||
|
||||
# Server management
|
||||
'run_development_server',
|
||||
]
|
||||
|
||||
# Import remaining modules that might depend on app
|
||||
from .db_fix import fix_database_schema
|
||||
from .db_rebuild import rebuild_database
|
||||
from .db_test import test_database_connection, test_models, print_database_stats, run_all_tests
|
||||
from .user_manager import list_users, create_user, reset_password, delete_user, create_admin_user
|
||||
from .server import run_development_server
|
||||
BIN
utils/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
utils/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
BIN
utils/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
utils/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
BIN
utils/__pycache__/db_check.cpython-311.pyc
Normal file
BIN
utils/__pycache__/db_check.cpython-311.pyc
Normal file
Binary file not shown.
BIN
utils/__pycache__/db_check.cpython-313.pyc
Normal file
BIN
utils/__pycache__/db_check.cpython-313.pyc
Normal file
Binary file not shown.
BIN
utils/__pycache__/db_fix.cpython-311.pyc
Normal file
BIN
utils/__pycache__/db_fix.cpython-311.pyc
Normal file
Binary file not shown.
BIN
utils/__pycache__/db_fix.cpython-313.pyc
Normal file
BIN
utils/__pycache__/db_fix.cpython-313.pyc
Normal file
Binary file not shown.
BIN
utils/__pycache__/db_rebuild.cpython-311.pyc
Normal file
BIN
utils/__pycache__/db_rebuild.cpython-311.pyc
Normal file
Binary file not shown.
BIN
utils/__pycache__/db_rebuild.cpython-313.pyc
Normal file
BIN
utils/__pycache__/db_rebuild.cpython-313.pyc
Normal file
Binary file not shown.
BIN
utils/__pycache__/db_test.cpython-311.pyc
Normal file
BIN
utils/__pycache__/db_test.cpython-311.pyc
Normal file
Binary file not shown.
BIN
utils/__pycache__/db_test.cpython-313.pyc
Normal file
BIN
utils/__pycache__/db_test.cpython-313.pyc
Normal file
Binary file not shown.
BIN
utils/__pycache__/server.cpython-311.pyc
Normal file
BIN
utils/__pycache__/server.cpython-311.pyc
Normal file
Binary file not shown.
BIN
utils/__pycache__/server.cpython-313.pyc
Normal file
BIN
utils/__pycache__/server.cpython-313.pyc
Normal file
Binary file not shown.
BIN
utils/__pycache__/user_manager.cpython-311.pyc
Normal file
BIN
utils/__pycache__/user_manager.cpython-311.pyc
Normal file
Binary file not shown.
BIN
utils/__pycache__/user_manager.cpython-313.pyc
Normal file
BIN
utils/__pycache__/user_manager.cpython-313.pyc
Normal file
Binary file not shown.
79
utils/db_check.py
Normal file
79
utils/db_check.py
Normal file
@@ -0,0 +1,79 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from flask import current_app
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
from sqlalchemy import text
|
||||
import time
|
||||
|
||||
def check_db_connection(db):
|
||||
"""
|
||||
Überprüft die Datenbankverbindung und versucht ggf. die Verbindung wiederherzustellen
|
||||
|
||||
Args:
|
||||
db: SQLAlchemy-Instanz
|
||||
|
||||
Returns:
|
||||
bool: True, wenn die Verbindung erfolgreich ist, sonst False
|
||||
"""
|
||||
max_retries = 3
|
||||
retry_count = 0
|
||||
|
||||
while retry_count < max_retries:
|
||||
try:
|
||||
# Führe eine einfache Abfrage durch, um die Verbindung zu testen
|
||||
with current_app.app_context():
|
||||
db.session.execute(text('SELECT 1'))
|
||||
return True
|
||||
except SQLAlchemyError as e:
|
||||
retry_count += 1
|
||||
print(f"DB-Verbindungsfehler (Versuch {retry_count}/{max_retries}): {str(e)}")
|
||||
|
||||
if retry_count < max_retries:
|
||||
# Warte vor dem nächsten Versuch
|
||||
time.sleep(1)
|
||||
|
||||
# Versuche die Verbindung zurückzusetzen
|
||||
try:
|
||||
db.session.rollback()
|
||||
except:
|
||||
pass
|
||||
|
||||
return False
|
||||
|
||||
def initialize_db_if_needed(db, initialize_function=None):
|
||||
"""
|
||||
Initialisiert die Datenbank, falls erforderlich
|
||||
|
||||
Args:
|
||||
db: SQLAlchemy-Instanz
|
||||
initialize_function: Funktion, die aufgerufen wird, um die Datenbank zu initialisieren
|
||||
|
||||
Returns:
|
||||
bool: True, wenn die Datenbank bereit ist, sonst False
|
||||
"""
|
||||
# Prüfe die Verbindung
|
||||
if not check_db_connection(db):
|
||||
return False
|
||||
|
||||
# Prüfe, ob die Tabellen existieren
|
||||
try:
|
||||
with current_app.app_context():
|
||||
# Führe eine Testabfrage auf einer Tabelle durch
|
||||
db.session.execute(text('SELECT COUNT(*) FROM user'))
|
||||
except SQLAlchemyError:
|
||||
# Tabellen existieren nicht, erstelle sie
|
||||
try:
|
||||
with current_app.app_context():
|
||||
db.create_all()
|
||||
|
||||
# Rufe die Initialisierungsfunktion auf, falls vorhanden
|
||||
if initialize_function and callable(initialize_function):
|
||||
initialize_function()
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"Fehler bei DB-Initialisierung: {str(e)}")
|
||||
return False
|
||||
|
||||
return True
|
||||
78
utils/db_fix.py
Normal file
78
utils/db_fix.py
Normal file
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
import sqlite3
|
||||
from datetime import datetime
|
||||
import sys
|
||||
import importlib.util
|
||||
|
||||
# Add the parent directory to path so we can import the app
|
||||
parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
sys.path.insert(0, parent_dir)
|
||||
|
||||
from app import app, db_path
|
||||
from models import db
|
||||
|
||||
def ensure_db_dir():
|
||||
"""Make sure the database directory exists."""
|
||||
os.makedirs(os.path.dirname(db_path), exist_ok=True)
|
||||
|
||||
def fix_database_schema():
|
||||
"""Fix the database schema by adding missing columns."""
|
||||
with app.app_context():
|
||||
# Ensure directory exists
|
||||
ensure_db_dir()
|
||||
|
||||
# Check if database exists, create tables if needed
|
||||
if not os.path.exists(db_path):
|
||||
print("Database doesn't exist. Creating all tables from scratch...")
|
||||
db.create_all()
|
||||
print("Database tables created successfully!")
|
||||
return
|
||||
|
||||
# Connect to existing database
|
||||
print(f"Connecting to database: {db_path}")
|
||||
conn = sqlite3.connect(db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
# Check if User table exists
|
||||
cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='user'")
|
||||
if not cursor.fetchone():
|
||||
print("User table doesn't exist. Creating all tables from scratch...")
|
||||
conn.close()
|
||||
db.create_all()
|
||||
print("Database tables created successfully!")
|
||||
return
|
||||
|
||||
# Check existing columns
|
||||
cursor.execute("PRAGMA table_info(user)")
|
||||
columns = cursor.fetchall()
|
||||
column_names = [col[1] for col in columns]
|
||||
print("Existing columns in User table:", column_names)
|
||||
|
||||
# Add missing columns
|
||||
if 'created_at' not in column_names:
|
||||
print("Adding 'created_at' column to User table...")
|
||||
cursor.execute("ALTER TABLE user ADD COLUMN created_at TIMESTAMP")
|
||||
|
||||
if 'last_login' not in column_names:
|
||||
print("Adding 'last_login' column to User table...")
|
||||
cursor.execute("ALTER TABLE user ADD COLUMN last_login TIMESTAMP")
|
||||
|
||||
if 'avatar' not in column_names:
|
||||
print("Adding 'avatar' column to User table...")
|
||||
cursor.execute("ALTER TABLE user ADD COLUMN avatar VARCHAR(200)")
|
||||
|
||||
if 'bio' not in column_names:
|
||||
print("Adding 'bio' column to User table...")
|
||||
cursor.execute("ALTER TABLE user ADD COLUMN bio TEXT")
|
||||
|
||||
# Commit the changes
|
||||
conn.commit()
|
||||
conn.close()
|
||||
print("Database schema updated successfully!")
|
||||
return True
|
||||
|
||||
if __name__ == "__main__":
|
||||
fix_database_schema()
|
||||
91
utils/db_rebuild.py
Normal file
91
utils/db_rebuild.py
Normal file
@@ -0,0 +1,91 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
import sqlite3
|
||||
from datetime import datetime
|
||||
import sys
|
||||
import importlib.util
|
||||
|
||||
# Add the parent directory to path so we can import the app
|
||||
parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
sys.path.insert(0, parent_dir)
|
||||
|
||||
# Import models directly to avoid circular import
|
||||
from models import db, User, Category
|
||||
|
||||
def rebuild_database(app_instance=None):
|
||||
"""Completely rebuilds the database by dropping and recreating all tables."""
|
||||
if app_instance is None:
|
||||
# Only import app if it's not provided as a parameter
|
||||
from app import app as app_instance
|
||||
from app import db_path
|
||||
else:
|
||||
# Get db_path from app_instance config
|
||||
db_path = app_instance.config['SQLALCHEMY_DATABASE_URI'].replace('sqlite:///', '')
|
||||
|
||||
with app_instance.app_context():
|
||||
print(f"Database path: {db_path}")
|
||||
|
||||
# Back up existing database if it exists
|
||||
if os.path.exists(db_path):
|
||||
backup_path = db_path + '.backup'
|
||||
try:
|
||||
import shutil
|
||||
shutil.copy2(db_path, backup_path)
|
||||
print(f"Backed up existing database to {backup_path}")
|
||||
except Exception as e:
|
||||
print(f"Warning: Could not create backup - {str(e)}")
|
||||
|
||||
# Ensure directory exists
|
||||
os.makedirs(os.path.dirname(db_path), exist_ok=True)
|
||||
|
||||
# Drop all tables and recreate them
|
||||
print("Dropping all tables...")
|
||||
db.drop_all()
|
||||
|
||||
print("Creating all tables...")
|
||||
db.create_all()
|
||||
|
||||
# Create admin user
|
||||
print("Creating admin user...")
|
||||
admin = User(
|
||||
username='admin',
|
||||
email='admin@example.com',
|
||||
is_admin=True,
|
||||
created_at=datetime.utcnow()
|
||||
)
|
||||
admin.set_password('admin')
|
||||
db.session.add(admin)
|
||||
|
||||
# Create regular user
|
||||
print("Creating regular user...")
|
||||
user = User(
|
||||
username='user',
|
||||
email='user@example.com',
|
||||
is_admin=False,
|
||||
created_at=datetime.utcnow()
|
||||
)
|
||||
user.set_password('user')
|
||||
db.session.add(user)
|
||||
|
||||
try:
|
||||
# Commit to generate user IDs
|
||||
db.session.commit()
|
||||
print("Users created successfully!")
|
||||
|
||||
# Create default categories
|
||||
print("Creating default categories...")
|
||||
# Instead of directly importing create_default_categories, call it through app_instance
|
||||
create_default_categories_func = getattr(sys.modules['app'], 'create_default_categories')
|
||||
create_default_categories_func()
|
||||
|
||||
print("Database rebuild completed successfully!")
|
||||
return True
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
print(f"Error during database rebuild: {str(e)}")
|
||||
raise
|
||||
|
||||
if __name__ == "__main__":
|
||||
rebuild_database()
|
||||
120
utils/db_test.py
Normal file
120
utils/db_test.py
Normal file
@@ -0,0 +1,120 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
import sys
|
||||
import sqlite3
|
||||
|
||||
# Add the parent directory to path so we can import the app
|
||||
parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
sys.path.insert(0, parent_dir)
|
||||
|
||||
from app import app, db_path
|
||||
from models import db, User, Thought, MindMapNode, Category
|
||||
|
||||
def test_database_connection():
|
||||
"""Test if the database exists and can be connected to."""
|
||||
try:
|
||||
if not os.path.exists(db_path):
|
||||
print(f"Database file does not exist: {db_path}")
|
||||
return False
|
||||
|
||||
conn = sqlite3.connect(db_path)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("PRAGMA integrity_check")
|
||||
result = cursor.fetchone()
|
||||
conn.close()
|
||||
|
||||
if result and result[0] == "ok":
|
||||
print(f"Database integrity check passed: {db_path}")
|
||||
return True
|
||||
else:
|
||||
print(f"Database integrity check failed: {result}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"Error testing database connection: {e}")
|
||||
return False
|
||||
|
||||
def test_models():
|
||||
"""Test if all models are properly defined and can be queried."""
|
||||
with app.app_context():
|
||||
try:
|
||||
print("\nTesting User model...")
|
||||
user_count = User.query.count()
|
||||
print(f" Found {user_count} users")
|
||||
|
||||
print("\nTesting Category model...")
|
||||
category_count = Category.query.count()
|
||||
print(f" Found {category_count} categories")
|
||||
|
||||
print("\nTesting MindMapNode model...")
|
||||
node_count = MindMapNode.query.count()
|
||||
print(f" Found {node_count} mindmap nodes")
|
||||
|
||||
print("\nTesting Thought model...")
|
||||
thought_count = Thought.query.count()
|
||||
print(f" Found {thought_count} thoughts")
|
||||
|
||||
if user_count == 0:
|
||||
print("\nWARNING: No users found in the database. You might need to create an admin user.")
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"Error testing models: {e}")
|
||||
return False
|
||||
|
||||
def print_database_stats():
|
||||
"""Print database statistics."""
|
||||
with app.app_context():
|
||||
try:
|
||||
stats = []
|
||||
stats.append(("Users", User.query.count()))
|
||||
stats.append(("Categories", Category.query.count()))
|
||||
stats.append(("Mindmap Nodes", MindMapNode.query.count()))
|
||||
stats.append(("Thoughts", Thought.query.count()))
|
||||
|
||||
print("\nDatabase Statistics:")
|
||||
print("-" * 40)
|
||||
for name, count in stats:
|
||||
print(f"{name:<20} : {count}")
|
||||
print("-" * 40)
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"Error generating database statistics: {e}")
|
||||
return False
|
||||
|
||||
def run_all_tests():
|
||||
"""Run all database tests."""
|
||||
success = True
|
||||
|
||||
print("=" * 60)
|
||||
print("STARTING DATABASE TESTS")
|
||||
print("=" * 60)
|
||||
|
||||
# Test database connection
|
||||
print("\n1. Testing database connection...")
|
||||
if not test_database_connection():
|
||||
success = False
|
||||
|
||||
# Test models
|
||||
print("\n2. Testing database models...")
|
||||
if not test_models():
|
||||
success = False
|
||||
|
||||
# Print statistics
|
||||
print("\n3. Database statistics:")
|
||||
if not print_database_stats():
|
||||
success = False
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
if success:
|
||||
print("All database tests completed successfully!")
|
||||
else:
|
||||
print("Some database tests failed. Check the output above for details.")
|
||||
print("=" * 60)
|
||||
|
||||
return success
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_all_tests()
|
||||
225
utils/download_resources.py
Normal file
225
utils/download_resources.py
Normal file
@@ -0,0 +1,225 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Dieses Skript lädt externe Ressourcen wie Font Awesome, Tailwind CSS und Alpine.js herunter
|
||||
und installiert sie lokal, um die Abhängigkeit von externen CDNs zu vermeiden und
|
||||
die Content Security Policy zu erfüllen.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import requests
|
||||
import zipfile
|
||||
import io
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
# URLs für die Ressourcen
|
||||
RESOURCES = {
|
||||
'alpine.js': 'https://cdn.jsdelivr.net/npm/alpinejs@3.12.3/dist/cdn.min.js',
|
||||
'font_awesome': 'https://use.fontawesome.com/releases/v6.4.0/fontawesome-free-6.4.0-web.zip',
|
||||
'inter_font_300': 'https://fonts.gstatic.com/s/inter/v18/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1ZL7.woff2',
|
||||
'inter_font_400': 'https://fonts.gstatic.com/s/inter/v18/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2ZL7SUc.woff2',
|
||||
'inter_font_500': 'https://fonts.gstatic.com/s/inter/v18/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa1pL7SUc.woff2',
|
||||
'inter_font_600': 'https://fonts.gstatic.com/s/inter/v18/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa2pL7SUc.woff2',
|
||||
'inter_font_700': 'https://fonts.gstatic.com/s/inter/v18/UcC73FwrK3iLTeHuS_nVMrMxCp50SjIa25L7SUc.woff2',
|
||||
'jetbrains_font_400': 'https://fonts.gstatic.com/s/jetbrainsmono/v20/tDbv2o-flEEny0FZhsfKu5WU4zr3E_BX0PnT8RD8yKwBNntkaToggR7BYRbKPxDcwg.woff2',
|
||||
'jetbrains_font_500': 'https://fonts.gstatic.com/s/jetbrainsmono/v20/tDbv2o-flEEny0FZhsfKu5WU4zr3E_BX0PnT8RD8yKwBNntkaToggR7BYRbKPx3cwhsk.woff2',
|
||||
'jetbrains_font_700': 'https://fonts.gstatic.com/s/jetbrainsmono/v20/tDbv2o-flEEny0FZhsfKu5WU4zr3E_BX0PnT8RD8yKwBNntkaToggR7BYRbKPxTcwhsk.woff2',
|
||||
}
|
||||
|
||||
# Zielverzeichnisse
|
||||
DIRECTORIES = {
|
||||
'js': os.path.join(BASE_DIR, 'static', 'js'),
|
||||
'css': os.path.join(BASE_DIR, 'static', 'css'),
|
||||
'fonts': os.path.join(BASE_DIR, 'static', 'fonts'),
|
||||
'webfonts': os.path.join(BASE_DIR, 'static', 'webfonts'),
|
||||
}
|
||||
|
||||
def download_file(url, filepath):
|
||||
"""Lädt eine Datei von einer URL herunter und speichert sie lokal."""
|
||||
response = requests.get(url, stream=True)
|
||||
if response.status_code == 200:
|
||||
with open(filepath, 'wb') as f:
|
||||
for chunk in response.iter_content(chunk_size=8192):
|
||||
f.write(chunk)
|
||||
print(f"✅ Heruntergeladen: {os.path.basename(filepath)}")
|
||||
return True
|
||||
else:
|
||||
print(f"❌ Fehler beim Herunterladen von {url}: {response.status_code}")
|
||||
return False
|
||||
|
||||
def extract_zip(zip_data, extract_to):
|
||||
"""Extrahiert eine ZIP-Datei in das angegebene Verzeichnis."""
|
||||
with zipfile.ZipFile(io.BytesIO(zip_data)) as zip_ref:
|
||||
zip_ref.extractall(extract_to)
|
||||
|
||||
def setup_directories():
|
||||
"""Erstellt die benötigten Verzeichnisse, falls sie nicht existieren."""
|
||||
for directory in DIRECTORIES.values():
|
||||
os.makedirs(directory, exist_ok=True)
|
||||
print(f"📁 Verzeichnis erstellt/überprüft: {directory}")
|
||||
|
||||
def download_alpine():
|
||||
"""Lädt Alpine.js herunter."""
|
||||
url = RESOURCES['alpine.js']
|
||||
filepath = os.path.join(DIRECTORIES['js'], 'alpine.min.js')
|
||||
download_file(url, filepath)
|
||||
|
||||
def download_font_awesome():
|
||||
"""Lädt Font Awesome herunter und extrahiert die Dateien."""
|
||||
url = RESOURCES['font_awesome']
|
||||
response = requests.get(url)
|
||||
if response.status_code == 200:
|
||||
# Temporäres Verzeichnis für die Extraktion
|
||||
temp_dir = os.path.join(BASE_DIR, 'temp_fontawesome')
|
||||
os.makedirs(temp_dir, exist_ok=True)
|
||||
|
||||
# ZIP-Datei extrahieren
|
||||
extract_zip(response.content, temp_dir)
|
||||
|
||||
# CSS-Datei kopieren
|
||||
fa_dir = os.path.join(temp_dir, 'fontawesome-free-6.4.0-web')
|
||||
css_source = os.path.join(fa_dir, 'css', 'all.min.css')
|
||||
css_dest = os.path.join(DIRECTORIES['css'], 'all.min.css')
|
||||
shutil.copyfile(css_source, css_dest)
|
||||
print(f"✅ Font Awesome CSS kopiert nach {css_dest}")
|
||||
|
||||
# Webfonts-Verzeichnis kopieren
|
||||
webfonts_source = os.path.join(fa_dir, 'webfonts')
|
||||
shutil.rmtree(DIRECTORIES['webfonts'], ignore_errors=True)
|
||||
shutil.copytree(webfonts_source, DIRECTORIES['webfonts'])
|
||||
print(f"✅ Font Awesome Webfonts kopiert nach {DIRECTORIES['webfonts']}")
|
||||
|
||||
# Temporäres Verzeichnis löschen
|
||||
shutil.rmtree(temp_dir)
|
||||
|
||||
return True
|
||||
else:
|
||||
print(f"❌ Fehler beim Herunterladen von Font Awesome: {response.status_code}")
|
||||
return False
|
||||
|
||||
def download_google_fonts():
|
||||
"""Lädt die Google Fonts (Inter und JetBrains Mono) herunter."""
|
||||
font_files = {
|
||||
'inter-light.woff2': RESOURCES['inter_font_300'],
|
||||
'inter-regular.woff2': RESOURCES['inter_font_400'],
|
||||
'inter-medium.woff2': RESOURCES['inter_font_500'],
|
||||
'inter-semibold.woff2': RESOURCES['inter_font_600'],
|
||||
'inter-bold.woff2': RESOURCES['inter_font_700'],
|
||||
'jetbrainsmono-regular.woff2': RESOURCES['jetbrains_font_400'],
|
||||
'jetbrainsmono-medium.woff2': RESOURCES['jetbrains_font_500'],
|
||||
'jetbrainsmono-bold.woff2': RESOURCES['jetbrains_font_700'],
|
||||
}
|
||||
|
||||
for filename, url in font_files.items():
|
||||
filepath = os.path.join(DIRECTORIES['fonts'], filename)
|
||||
download_file(url, filepath)
|
||||
|
||||
def setup_tailwind():
|
||||
"""Richtet Tailwind CSS ein."""
|
||||
# Tailwind-Konfiguration erstellen, falls sie nicht existiert
|
||||
tailwind_config = os.path.join(BASE_DIR, 'tailwind.config.js')
|
||||
if not os.path.exists(tailwind_config):
|
||||
with open(tailwind_config, 'w') as f:
|
||||
f.write("""/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
darkMode: 'class',
|
||||
content: [
|
||||
"./templates/**/*.html",
|
||||
"./static/**/*.js",
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
fontFamily: {
|
||||
sans: ['Inter', 'ui-sans-serif', 'system-ui', '-apple-system', 'sans-serif'],
|
||||
mono: ['JetBrains Mono', 'ui-monospace', 'monospace']
|
||||
},
|
||||
colors: {
|
||||
primary: {
|
||||
50: '#f5f3ff',
|
||||
100: '#ede9fe',
|
||||
200: '#ddd6fe',
|
||||
300: '#c4b5fd',
|
||||
400: '#a78bfa',
|
||||
500: '#8b5cf6',
|
||||
600: '#7c3aed',
|
||||
700: '#6d28d9',
|
||||
800: '#5b21b6',
|
||||
900: '#4c1d95'
|
||||
},
|
||||
secondary: {
|
||||
50: '#ecfdf5',
|
||||
100: '#d1fae5',
|
||||
200: '#a7f3d0',
|
||||
300: '#6ee7b7',
|
||||
400: '#34d399',
|
||||
500: '#10b981',
|
||||
600: '#059669',
|
||||
700: '#047857',
|
||||
800: '#065f46',
|
||||
900: '#064e3b'
|
||||
},
|
||||
dark: {
|
||||
500: '#374151',
|
||||
600: '#1f2937',
|
||||
700: '#111827',
|
||||
800: '#0e1220',
|
||||
900: '#0a0e19'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
""")
|
||||
print(f"✅ Tailwind-Konfiguration erstellt: {tailwind_config}")
|
||||
|
||||
# Input-CSS-Datei erstellen
|
||||
input_css_dir = os.path.join(DIRECTORIES['css'], 'src')
|
||||
os.makedirs(input_css_dir, exist_ok=True)
|
||||
|
||||
input_css = os.path.join(input_css_dir, 'input.css')
|
||||
if not os.path.exists(input_css):
|
||||
with open(input_css, 'w') as f:
|
||||
f.write("""@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
""")
|
||||
print(f"✅ Tailwind Input-CSS erstellt: {input_css}")
|
||||
|
||||
# Hinweis zur Kompilierung anzeigen
|
||||
print("\n📋 Um Tailwind CSS zu kompilieren, führe folgenden Befehl aus:")
|
||||
print("npm install -D tailwindcss")
|
||||
print(f"npx tailwindcss -i {input_css} -o {os.path.join(DIRECTORIES['css'], 'tailwind.min.css')} --minify")
|
||||
|
||||
def main():
|
||||
"""Hauptfunktion: Lädt alle benötigten Ressourcen herunter."""
|
||||
print("🚀 Starte den Download externer Ressourcen...")
|
||||
setup_directories()
|
||||
|
||||
# Alpine.js herunterladen
|
||||
print("\n📦 Lade Alpine.js herunter...")
|
||||
download_alpine()
|
||||
|
||||
# Font Awesome herunterladen
|
||||
print("\n📦 Lade Font Awesome herunter...")
|
||||
download_font_awesome()
|
||||
|
||||
# Google Fonts herunterladen
|
||||
print("\n📦 Lade Google Fonts herunter...")
|
||||
download_google_fonts()
|
||||
|
||||
# Tailwind CSS einrichten
|
||||
print("\n📦 Richte Tailwind CSS ein...")
|
||||
setup_tailwind()
|
||||
|
||||
print("\n✅ Alle Ressourcen wurden erfolgreich heruntergeladen und eingerichtet!")
|
||||
print("🔒 Die Webseite sollte nun ohne externe CDNs funktionieren und die Content Security Policy erfüllen.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
34
utils/server.py
Normal file
34
utils/server.py
Normal file
@@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Add the parent directory to path so we can import the app
|
||||
parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
sys.path.insert(0, parent_dir)
|
||||
|
||||
from app import app
|
||||
|
||||
def run_development_server(host='127.0.0.1', port=5000, debug=True):
|
||||
"""Run the Flask development server."""
|
||||
try:
|
||||
print(f"Starting development server on http://{host}:{port}")
|
||||
print("Press CTRL+C to stop the server")
|
||||
app.run(host=host, port=port, debug=debug)
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"Error starting development server: {e}")
|
||||
return False
|
||||
|
||||
if __name__ == "__main__":
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description='Run the development server')
|
||||
parser.add_argument('--host', default='127.0.0.1', help='Host to bind to')
|
||||
parser.add_argument('--port', type=int, default=5000, help='Port to bind to')
|
||||
parser.add_argument('--debug', action='store_true', default=True, help='Enable debug mode')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
run_development_server(host=args.host, port=args.port, debug=args.debug)
|
||||
159
utils/user_manager.py
Normal file
159
utils/user_manager.py
Normal file
@@ -0,0 +1,159 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
import sys
|
||||
from datetime import datetime
|
||||
|
||||
# Add the parent directory to path so we can import the app
|
||||
parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
sys.path.insert(0, parent_dir)
|
||||
|
||||
from app import app
|
||||
from models import db, User
|
||||
|
||||
def list_users():
|
||||
"""List all users in the database."""
|
||||
with app.app_context():
|
||||
try:
|
||||
users = User.query.all()
|
||||
if not users:
|
||||
print("No users found in the database.")
|
||||
return []
|
||||
|
||||
print("Found {} users:".format(len(users)))
|
||||
print("-" * 60)
|
||||
print("{:<5} {:<20} {:<30} {:<10}".format("ID", "Username", "Email", "Admin"))
|
||||
print("-" * 60)
|
||||
|
||||
for user in users:
|
||||
print("{:<5} {:<20} {:<30} {:<10}".format(
|
||||
user.id, user.username, user.email, "Yes" if user.is_admin else "No"
|
||||
))
|
||||
return users
|
||||
except Exception as e:
|
||||
print(f"Error listing users: {e}")
|
||||
return []
|
||||
|
||||
def create_user(username, email, password, is_admin=False):
|
||||
"""Create a new user in the database."""
|
||||
with app.app_context():
|
||||
try:
|
||||
# Check if user already exists
|
||||
existing_user = User.query.filter_by(username=username).first()
|
||||
if existing_user:
|
||||
print(f"User with username '{username}' already exists.")
|
||||
return False
|
||||
|
||||
# Check if email already exists
|
||||
existing_email = User.query.filter_by(email=email).first()
|
||||
if existing_email:
|
||||
print(f"User with email '{email}' already exists.")
|
||||
return False
|
||||
|
||||
# Create new user
|
||||
user = User(
|
||||
username=username,
|
||||
email=email,
|
||||
is_admin=is_admin,
|
||||
created_at=datetime.utcnow()
|
||||
)
|
||||
user.set_password(password)
|
||||
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
|
||||
print(f"User '{username}' created successfully!")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
print(f"Error creating user: {e}")
|
||||
return False
|
||||
|
||||
def reset_password(username, new_password):
|
||||
"""Reset password for a user."""
|
||||
with app.app_context():
|
||||
try:
|
||||
user = User.query.filter_by(username=username).first()
|
||||
if not user:
|
||||
print(f"User '{username}' not found.")
|
||||
return False
|
||||
|
||||
user.set_password(new_password)
|
||||
db.session.commit()
|
||||
|
||||
print(f"Password for user '{username}' reset successfully!")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
print(f"Error resetting password: {e}")
|
||||
return False
|
||||
|
||||
def delete_user(username):
|
||||
"""Delete a user from the database."""
|
||||
with app.app_context():
|
||||
try:
|
||||
user = User.query.filter_by(username=username).first()
|
||||
if not user:
|
||||
print(f"User '{username}' not found.")
|
||||
return False
|
||||
|
||||
db.session.delete(user)
|
||||
db.session.commit()
|
||||
|
||||
print(f"User '{username}' deleted successfully!")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
print(f"Error deleting user: {e}")
|
||||
return False
|
||||
|
||||
def create_admin_user():
|
||||
"""Create an admin user in the database."""
|
||||
return create_user('admin', 'admin@example.com', 'admin', is_admin=True)
|
||||
|
||||
if __name__ == "__main__":
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description='User management utility')
|
||||
subparsers = parser.add_subparsers(dest='command', help='Command to execute')
|
||||
|
||||
# List users command
|
||||
list_parser = subparsers.add_parser('list', help='List all users')
|
||||
|
||||
# Create user command
|
||||
create_parser = subparsers.add_parser('create', help='Create a new user')
|
||||
create_parser.add_argument('--username', '-u', required=True, help='Username')
|
||||
create_parser.add_argument('--email', '-e', required=True, help='Email address')
|
||||
create_parser.add_argument('--password', '-p', required=True, help='Password')
|
||||
create_parser.add_argument('--admin', '-a', action='store_true', help='Make user an admin')
|
||||
|
||||
# Reset password command
|
||||
reset_parser = subparsers.add_parser('reset-password', help='Reset a user password')
|
||||
reset_parser.add_argument('--username', '-u', required=True, help='Username')
|
||||
reset_parser.add_argument('--password', '-p', required=True, help='New password')
|
||||
|
||||
# Delete user command
|
||||
delete_parser = subparsers.add_parser('delete', help='Delete a user')
|
||||
delete_parser.add_argument('--username', '-u', required=True, help='Username to delete')
|
||||
|
||||
# Create admin command (shortcut)
|
||||
admin_parser = subparsers.add_parser('create-admin', help='Create the default admin user')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.command == 'list':
|
||||
list_users()
|
||||
elif args.command == 'create':
|
||||
create_user(args.username, args.email, args.password, args.admin)
|
||||
elif args.command == 'reset-password':
|
||||
reset_password(args.username, args.password)
|
||||
elif args.command == 'delete':
|
||||
delete_user(args.username)
|
||||
elif args.command == 'create-admin':
|
||||
create_admin_user()
|
||||
else:
|
||||
parser.print_help()
|
||||
Reference in New Issue
Block a user