Initial commit
This commit is contained in:
@@ -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)
|
||||
Reference in New Issue
Block a user