Initial commit

This commit is contained in:
2026-06-28 16:51:38 +00:00
commit 4cca794b0a
10 changed files with 1016 additions and 0 deletions
+186
View File
@@ -0,0 +1,186 @@
from flask import Flask, render_template, request, jsonify, session, redirect, url_for, flash
import mysql.connector
import os
import re
import requests
import random
import string
from datetime import datetime
from functools import wraps
app = Flask(__name__)
app.secret_key = os.environ.get('SECRET_KEY', 'skyevouchers-secret-key-2024')
# Config
DB_CONFIG = {
'host': os.environ.get('DB_HOST', 'localhost'),
'port': int(os.environ.get('DB_PORT', 3306)),
'user': os.environ.get('DB_USER', 'root'),
'password': os.environ.get('DB_PASSWORD', ''),
'database': os.environ.get('DB_NAME', 'skyevouchers'),
}
ADMIN_PASSWORD = os.environ.get('ADMIN_PASSWORD', 'admin123')
GOTIFY_URL = os.environ.get('GOTIFY_URL', 'http://localhost:8080')
GOTIFY_TOKEN = os.environ.get('GOTIFY_TOKEN', '')
LOW_VOUCHER_THRESHOLD = 2
def get_db():
return mysql.connector.connect(**DB_CONFIG)
def init_db():
conn = get_db()
c = conn.cursor()
c.execute('''CREATE TABLE IF NOT EXISTS vouchers (
id INT AUTO_INCREMENT PRIMARY KEY,
code VARCHAR(20) NOT NULL UNIQUE,
used BOOLEAN DEFAULT FALSE,
used_at DATETIME NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)''')
conn.commit()
conn.close()
def send_gotify_notification(remaining):
if not GOTIFY_TOKEN:
return
try:
requests.post(
f"{GOTIFY_URL}/message",
json={
"title": "⚠️ Skye Vouchers Niedriger Bestand",
"message": f"Nur noch {remaining} Voucher(s) verfügbar! Bitte neue Vouchers hinzufügen.",
"priority": 8
},
params={"token": GOTIFY_TOKEN},
timeout=5
)
except Exception:
pass
def login_required(f):
@wraps(f)
def decorated(*args, **kwargs):
if not session.get('admin_logged_in'):
return redirect(url_for('admin_login'))
return f(*args, **kwargs)
return decorated
# ── Frontend ──────────────────────────────────────────────────────────────────
@app.route('/')
def index():
return render_template('index.html')
@app.route('/api/get-voucher', methods=['POST'])
def get_voucher():
conn = get_db()
c = conn.cursor(dictionary=True)
c.execute('SELECT id, code FROM vouchers WHERE used = FALSE ORDER BY RAND() LIMIT 1')
voucher = c.fetchone()
if not voucher:
conn.close()
return jsonify({'error': 'Keine Vouchers mehr verfügbar'}), 404
c.execute('UPDATE vouchers SET used = TRUE, used_at = %s WHERE id = %s',
(datetime.now(), voucher['id']))
conn.commit()
# Check remaining
c.execute('SELECT COUNT(*) AS cnt FROM vouchers WHERE used = FALSE')
remaining = c.fetchone()['cnt']
conn.close()
if remaining <= LOW_VOUCHER_THRESHOLD:
send_gotify_notification(remaining)
return jsonify({'code': voucher['code']})
# ── Admin ─────────────────────────────────────────────────────────────────────
@app.route('/admin/login', methods=['GET', 'POST'])
def admin_login():
if request.method == 'POST':
if request.form.get('password') == ADMIN_PASSWORD:
session['admin_logged_in'] = True
return redirect(url_for('admin_dashboard'))
flash('Falsches Passwort', 'error')
return render_template('admin_login.html')
@app.route('/admin/logout')
def admin_logout():
session.clear()
return redirect(url_for('admin_login'))
@app.route('/admin')
@login_required
def admin_dashboard():
conn = get_db()
c = conn.cursor(dictionary=True)
c.execute('SELECT COUNT(*) AS cnt FROM vouchers WHERE used = FALSE')
available = c.fetchone()['cnt']
c.execute('SELECT COUNT(*) AS cnt FROM vouchers WHERE used = TRUE')
used_count = c.fetchone()['cnt']
c.execute('SELECT COUNT(*) AS cnt FROM vouchers')
total = c.fetchone()['cnt']
c.execute('SELECT code, used_at FROM vouchers WHERE used = TRUE ORDER BY used_at DESC LIMIT 20')
recent_used = c.fetchall()
c.execute('SELECT code, created_at FROM vouchers WHERE used = FALSE ORDER BY created_at DESC LIMIT 20')
available_vouchers = c.fetchall()
conn.close()
return render_template('admin_dashboard.html',
available=available,
used_count=used_count,
total=total,
recent_used=recent_used,
available_vouchers=available_vouchers)
@app.route('/admin/import', methods=['POST'])
@login_required
def admin_import():
text = request.form.get('voucher_text', '')
# Extract codes matching xxxxx-xxxxx (5 digits, dash, 5 digits)
codes = re.findall(r'\b(\d{5}-\d{5})\b', text)
if not codes:
flash('Keine gültigen Voucher-Codes gefunden.', 'error')
return redirect(url_for('admin_dashboard'))
conn = get_db()
c = conn.cursor()
imported = 0
skipped = 0
for code in codes:
try:
c.execute('INSERT INTO vouchers (code) VALUES (%s)', (code,))
imported += 1
except mysql.connector.IntegrityError:
skipped += 1
conn.commit()
conn.close()
flash(f'{imported} Voucher(s) importiert, {skipped} bereits vorhanden.', 'success')
return redirect(url_for('admin_dashboard'))
@app.route('/admin/delete/<int:vid>', methods=['POST'])
@login_required
def admin_delete(vid):
conn = get_db()
c = conn.cursor()
c.execute('DELETE FROM vouchers WHERE id = %s', (vid,))
conn.commit()
conn.close()
return jsonify({'ok': True})
if __name__ == '__main__':
init_db()
app.run(debug=True, host='0.0.0.0', port=5000)