chore: automatic commit 2025-04-30 12:48
This commit is contained in:
@@ -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
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -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)
|
||||
@@ -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