final stage of the app

This commit is contained in:
2025-07-15 14:32:57 +03:00
parent 94f006d458
commit b94d2ebbd6
29 changed files with 1498 additions and 1124 deletions

7
app/routes/__init__.py Normal file
View File

@@ -0,0 +1,7 @@
"""
Route modules for QR Code Manager
"""
from . import main, api, auth
__all__ = ['main', 'api', 'auth']

304
app/routes/api.py Normal file
View File

@@ -0,0 +1,304 @@
"""
API routes for QR Code Manager
"""
import os
import io
import base64
import uuid
from datetime import datetime
from flask import Blueprint, request, jsonify, send_file
from app.utils.auth import login_required
from app.utils.qr_generator import QRCodeGenerator
from app.utils.link_manager import LinkPageManager
from app.utils.data_manager import QRDataManager
bp = Blueprint('api', __name__)
# Initialize managers
qr_generator = QRCodeGenerator()
link_manager = LinkPageManager()
data_manager = QRDataManager()
# Configuration for file uploads
UPLOAD_FOLDER = 'app/static/qr_codes'
LOGOS_FOLDER = 'app/static/logos'
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
os.makedirs(LOGOS_FOLDER, exist_ok=True)
@bp.route('/generate', methods=['POST'])
@login_required
def generate_qr():
"""Generate QR code API endpoint"""
try:
data = request.json
# Extract QR code content
qr_type = data.get('type', 'text')
content = data.get('content', '')
# Process content based on type
if qr_type == 'url':
qr_content = content if content.startswith(('http://', 'https://')) else f'https://{content}'
elif qr_type == 'wifi':
wifi_data = data.get('wifi', {})
qr_content = f"WIFI:T:{wifi_data.get('security', 'WPA')};S:{wifi_data.get('ssid', '')};P:{wifi_data.get('password', '')};H:{wifi_data.get('hidden', 'false')};;"
elif qr_type == 'email':
email_data = data.get('email', {})
qr_content = f"mailto:{email_data.get('email', '')}?subject={email_data.get('subject', '')}&body={email_data.get('body', '')}"
elif qr_type == 'phone':
qr_content = f"tel:{content}"
elif qr_type == 'sms':
sms_data = data.get('sms', {})
qr_content = f"smsto:{sms_data.get('phone', '')}:{sms_data.get('message', '')}"
elif qr_type == 'vcard':
vcard_data = data.get('vcard', {})
qr_content = f"""BEGIN:VCARD
VERSION:3.0
FN:{vcard_data.get('name', '')}
ORG:{vcard_data.get('organization', '')}
TEL:{vcard_data.get('phone', '')}
EMAIL:{vcard_data.get('email', '')}
URL:{vcard_data.get('website', '')}
END:VCARD"""
else: # text
qr_content = content
# Extract styling options
settings = {
'size': data.get('size', 10),
'border': data.get('border', 4),
'foreground_color': data.get('foreground_color', '#000000'),
'background_color': data.get('background_color', '#FFFFFF'),
'style': data.get('style', 'square')
}
# Generate QR code
qr_img = qr_generator.generate_qr_code(qr_content, settings)
# Add logo if provided
logo_path = data.get('logo_path')
if logo_path and os.path.exists(logo_path):
qr_img = qr_generator.add_logo(qr_img, logo_path)
# Convert to base64
img_buffer = io.BytesIO()
qr_img.save(img_buffer, format='PNG')
img_buffer.seek(0)
img_base64 = base64.b64encode(img_buffer.getvalue()).decode()
# Save QR code record
qr_id = data_manager.save_qr_record(qr_type, qr_content, settings, img_base64)
# Save image file
img_path = os.path.join(UPLOAD_FOLDER, f'{qr_id}.png')
qr_img.save(img_path)
return jsonify({
'success': True,
'qr_id': qr_id,
'image_data': f'data:image/png;base64,{img_base64}',
'download_url': f'/api/download/{qr_id}'
})
except Exception as e:
return jsonify({'success': False, 'error': str(e)}), 500
@bp.route('/download/<qr_id>')
@login_required
def download_qr(qr_id):
"""Download QR code"""
try:
img_path = os.path.join(UPLOAD_FOLDER, f'{qr_id}.png')
if os.path.exists(img_path):
return send_file(img_path, as_attachment=True, download_name=f'qr_code_{qr_id}.png')
else:
return jsonify({'error': 'QR code not found'}), 404
except Exception as e:
return jsonify({'error': str(e)}), 500
@bp.route('/qr_codes')
@login_required
def list_qr_codes():
"""List all generated QR codes"""
return jsonify(data_manager.list_qr_codes())
@bp.route('/qr_codes/<qr_id>')
@login_required
def get_qr_code(qr_id):
"""Get specific QR code details"""
qr_data = data_manager.get_qr_record(qr_id)
if qr_data:
return jsonify(qr_data)
else:
return jsonify({'error': 'QR code not found'}), 404
@bp.route('/qr_codes/<qr_id>', methods=['DELETE'])
@login_required
def delete_qr_code(qr_id):
"""Delete QR code"""
try:
if data_manager.qr_exists(qr_id):
# Remove from database
data_manager.delete_qr_record(qr_id)
# Remove image file
img_path = os.path.join(UPLOAD_FOLDER, f'{qr_id}.png')
if os.path.exists(img_path):
os.remove(img_path)
return jsonify({'success': True})
else:
return jsonify({'error': 'QR code not found'}), 404
except Exception as e:
return jsonify({'error': str(e)}), 500
@bp.route('/upload_logo', methods=['POST'])
@login_required
def upload_logo():
"""Upload logo for QR code"""
try:
if 'logo' not in request.files:
return jsonify({'error': 'No logo file provided'}), 400
file = request.files['logo']
if file.filename == '':
return jsonify({'error': 'No file selected'}), 400
# Save logo
logo_id = str(uuid.uuid4())
logo_extension = file.filename.rsplit('.', 1)[1].lower()
logo_filename = f'{logo_id}.{logo_extension}'
logo_path = os.path.join(LOGOS_FOLDER, logo_filename)
file.save(logo_path)
return jsonify({
'success': True,
'logo_path': logo_path,
'logo_id': logo_id
})
except Exception as e:
return jsonify({'error': str(e)}), 500
# Dynamic Link Pages API Routes
@bp.route('/create_link_page', methods=['POST'])
@login_required
def create_link_page():
"""Create a new dynamic link page and QR code"""
try:
data = request.json
title = data.get('title', 'My Links')
description = data.get('description', 'Collection of useful links')
# Create the link page
page_id = link_manager.create_link_page(title, description)
# Create QR code pointing to the link page
page_url = f"{request.url_root}links/{page_id}"
settings = {
'size': data.get('size', 10),
'border': data.get('border', 4),
'foreground_color': data.get('foreground_color', '#000000'),
'background_color': data.get('background_color', '#FFFFFF'),
'style': data.get('style', 'square')
}
# Generate QR code
qr_img = qr_generator.generate_qr_code(page_url, settings)
# Convert to base64
img_buffer = io.BytesIO()
qr_img.save(img_buffer, format='PNG')
img_buffer.seek(0)
img_base64 = base64.b64encode(img_buffer.getvalue()).decode()
# Save QR code record
qr_id = data_manager.save_qr_record('link_page', page_url, settings, img_base64, page_id)
# Save image file
img_path = os.path.join(UPLOAD_FOLDER, f'{qr_id}.png')
qr_img.save(img_path)
return jsonify({
'success': True,
'qr_id': qr_id,
'page_id': page_id,
'page_url': page_url,
'edit_url': f"{request.url_root}edit/{page_id}",
'image_data': f'data:image/png;base64,{img_base64}',
'download_url': f'/api/download/{qr_id}'
})
except Exception as e:
return jsonify({'success': False, 'error': str(e)}), 500
@bp.route('/link_pages/<page_id>/links', methods=['POST'])
@login_required
def add_link_to_page(page_id):
"""Add a link to a page"""
try:
data = request.json
title = data.get('title', '')
url = data.get('url', '')
description = data.get('description', '')
if not title or not url:
return jsonify({'error': 'Title and URL are required'}), 400
success = link_manager.add_link(page_id, title, url, description)
if success:
return jsonify({'success': True})
else:
return jsonify({'error': 'Page not found'}), 404
except Exception as e:
return jsonify({'error': str(e)}), 500
@bp.route('/link_pages/<page_id>/links/<link_id>', methods=['PUT'])
@login_required
def update_link_in_page(page_id, link_id):
"""Update a link in a page"""
try:
data = request.json
title = data.get('title')
url = data.get('url')
description = data.get('description')
success = link_manager.update_link(page_id, link_id, title, url, description)
if success:
return jsonify({'success': True})
else:
return jsonify({'error': 'Page or link not found'}), 404
except Exception as e:
return jsonify({'error': str(e)}), 500
@bp.route('/link_pages/<page_id>/links/<link_id>', methods=['DELETE'])
@login_required
def delete_link_from_page(page_id, link_id):
"""Delete a link from a page"""
try:
success = link_manager.delete_link(page_id, link_id)
if success:
return jsonify({'success': True})
else:
return jsonify({'error': 'Page or link not found'}), 404
except Exception as e:
return jsonify({'error': str(e)}), 500
@bp.route('/link_pages/<page_id>')
@login_required
def get_link_page(page_id):
"""Get link page data"""
page_data = link_manager.get_page(page_id)
if page_data:
return jsonify(page_data)
else:
return jsonify({'error': 'Page not found'}), 404

34
app/routes/auth.py Normal file
View File

@@ -0,0 +1,34 @@
"""
Authentication routes for QR Code Manager
"""
from flask import Blueprint, request, render_template, session, redirect, url_for, flash
from app.utils.auth import get_admin_credentials, verify_password
bp = Blueprint('auth', __name__)
@bp.route('/login', methods=['GET', 'POST'])
def login():
"""Login page"""
if request.method == 'POST':
username = request.form.get('username')
password = request.form.get('password')
admin_username, admin_password_hash = get_admin_credentials()
if username == admin_username and verify_password(password, admin_password_hash):
session['logged_in'] = True
session['username'] = username
flash('Successfully logged in!', 'success')
return redirect(url_for('main.index'))
else:
flash('Invalid username or password!', 'error')
return render_template('login.html')
@bp.route('/logout')
def logout():
"""Logout and clear session"""
session.clear()
flash('You have been logged out!', 'info')
return redirect(url_for('auth.login'))

46
app/routes/main.py Normal file
View File

@@ -0,0 +1,46 @@
"""
Main routes for QR Code Manager
"""
from flask import Blueprint, render_template
from app.utils.auth import login_required
from app.utils.link_manager import LinkPageManager
bp = Blueprint('main', __name__)
# Initialize manager
link_manager = LinkPageManager()
@bp.route('/')
@login_required
def index():
"""Serve the main page"""
return render_template('index.html')
@bp.route('/links/<page_id>')
def view_link_page(page_id):
"""Display the public link page"""
if not link_manager.page_exists(page_id):
return "Page not found", 404
link_manager.increment_view_count(page_id)
page_data = link_manager.get_page(page_id)
return render_template('link_page.html', page=page_data)
@bp.route('/edit/<page_id>')
@login_required
def edit_link_page(page_id):
"""Display the edit interface for the link page"""
if not link_manager.page_exists(page_id):
return "Page not found", 404
page_data = link_manager.get_page(page_id)
return render_template('edit_links.html', page=page_data)
@bp.route('/health')
def health_check():
"""Health check endpoint for Docker"""
from datetime import datetime
from flask import jsonify
return jsonify({'status': 'healthy', 'timestamp': datetime.now().isoformat()})