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

505
app.py
View File

@@ -1,505 +0,0 @@
import os
import io
import base64
import json
import uuid
from datetime import datetime
from PIL import Image, ImageDraw
import qrcode
from qrcode.image.styledpil import StyledPilImage
from qrcode.image.styles.moduledrawers import RoundedModuleDrawer, CircleModuleDrawer, SquareModuleDrawer
from flask import Flask, request, jsonify, send_file, render_template
from flask_cors import CORS
app = Flask(__name__)
CORS(app)
# Configuration
UPLOAD_FOLDER = 'static/qr_codes'
LOGOS_FOLDER = 'static/logos'
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
os.makedirs(LOGOS_FOLDER, exist_ok=True)
# In-memory storage for QR codes (in production, use a database)
qr_codes_db = {}
# In-memory storage for dynamic link pages
link_pages_db = {}
class QRCodeGenerator:
def __init__(self):
self.default_settings = {
'size': 10,
'border': 4,
'error_correction': qrcode.constants.ERROR_CORRECT_M,
'foreground_color': '#000000',
'background_color': '#FFFFFF',
'style': 'square'
}
def generate_qr_code(self, data, settings=None):
"""Generate QR code with custom settings"""
if settings is None:
settings = self.default_settings.copy()
else:
merged_settings = self.default_settings.copy()
merged_settings.update(settings)
settings = merged_settings
# Create QR code instance
qr = qrcode.QRCode(
version=1,
error_correction=settings['error_correction'],
box_size=settings['size'],
border=settings['border'],
)
qr.add_data(data)
qr.make(fit=True)
# For styled QR codes with custom module drawer
if settings['style'] != 'square':
# Choose module drawer based on style
module_drawer = None
if settings['style'] == 'rounded':
module_drawer = RoundedModuleDrawer()
elif settings['style'] == 'circle':
module_drawer = CircleModuleDrawer()
# Generate the styled image
img = qr.make_image(
image_factory=StyledPilImage,
module_drawer=module_drawer,
fill_color=settings['foreground_color'],
back_color=settings['background_color']
)
else:
# Generate standard image with custom colors
img = qr.make_image(
fill_color=settings['foreground_color'],
back_color=settings['background_color']
)
return img
def add_logo(self, qr_img, logo_path, logo_size_ratio=0.2):
"""Add logo to QR code"""
try:
logo = Image.open(logo_path)
# Calculate logo size
qr_width, qr_height = qr_img.size
logo_size = int(min(qr_width, qr_height) * logo_size_ratio)
# Resize logo
logo = logo.resize((logo_size, logo_size), Image.Resampling.LANCZOS)
# Create a white background for the logo
logo_bg = Image.new('RGB', (logo_size + 20, logo_size + 20), 'white')
logo_bg.paste(logo, (10, 10))
# Calculate position to center the logo
logo_pos = (
(qr_width - logo_bg.width) // 2,
(qr_height - logo_bg.height) // 2
)
# Paste logo onto QR code
qr_img.paste(logo_bg, logo_pos)
return qr_img
except Exception as e:
print(f"Error adding logo: {e}")
return qr_img
class LinkPageManager:
def __init__(self):
pass
def create_link_page(self, title="My Links", description="Collection of useful links"):
"""Create a new dynamic link page"""
page_id = str(uuid.uuid4())
page_data = {
'id': page_id,
'title': title,
'description': description,
'links': [],
'created_at': datetime.now().isoformat(),
'updated_at': datetime.now().isoformat(),
'view_count': 0
}
link_pages_db[page_id] = page_data
return page_id
def add_link(self, page_id, title, url, description=""):
"""Add a link to a page"""
if page_id not in link_pages_db:
return False
link_data = {
'id': str(uuid.uuid4()),
'title': title,
'url': url if url.startswith(('http://', 'https://')) else f'https://{url}',
'description': description,
'created_at': datetime.now().isoformat()
}
link_pages_db[page_id]['links'].append(link_data)
link_pages_db[page_id]['updated_at'] = datetime.now().isoformat()
return True
def update_link(self, page_id, link_id, title=None, url=None, description=None):
"""Update a specific link"""
if page_id not in link_pages_db:
return False
for link in link_pages_db[page_id]['links']:
if link['id'] == link_id:
if title is not None:
link['title'] = title
if url is not None:
link['url'] = url if url.startswith(('http://', 'https://')) else f'https://{url}'
if description is not None:
link['description'] = description
link_pages_db[page_id]['updated_at'] = datetime.now().isoformat()
return True
return False
def delete_link(self, page_id, link_id):
"""Delete a specific link"""
if page_id not in link_pages_db:
return False
links = link_pages_db[page_id]['links']
link_pages_db[page_id]['links'] = [link for link in links if link['id'] != link_id]
link_pages_db[page_id]['updated_at'] = datetime.now().isoformat()
return True
def increment_view_count(self, page_id):
"""Increment view count for a page"""
if page_id in link_pages_db:
link_pages_db[page_id]['view_count'] += 1
# Initialize managers
qr_generator = QRCodeGenerator()
link_manager = LinkPageManager()
@app.route('/')
def index():
"""Serve the main page"""
return render_template('index.html')
@app.route('/api/generate', methods=['POST'])
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 = str(uuid.uuid4())
qr_record = {
'id': qr_id,
'type': qr_type,
'content': qr_content,
'settings': settings,
'created_at': datetime.now().isoformat(),
'image_data': img_base64
}
qr_codes_db[qr_id] = qr_record
# 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
@app.route('/api/download/<qr_id>')
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
@app.route('/api/qr_codes')
def list_qr_codes():
"""List all generated QR codes"""
qr_list = []
for qr_id, qr_data in qr_codes_db.items():
qr_list.append({
'id': qr_id,
'type': qr_data['type'],
'created_at': qr_data['created_at'],
'preview': f'data:image/png;base64,{qr_data["image_data"]}'
})
return jsonify(qr_list)
@app.route('/api/qr_codes/<qr_id>')
def get_qr_code(qr_id):
"""Get specific QR code details"""
if qr_id in qr_codes_db:
return jsonify(qr_codes_db[qr_id])
else:
return jsonify({'error': 'QR code not found'}), 404
@app.route('/api/qr_codes/<qr_id>', methods=['DELETE'])
def delete_qr_code(qr_id):
"""Delete QR code"""
try:
if qr_id in qr_codes_db:
# Remove from database
del qr_codes_db[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
@app.route('/api/upload_logo', methods=['POST'])
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
@app.route('/api/create_link_page', methods=['POST'])
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 = str(uuid.uuid4())
qr_record = {
'id': qr_id,
'type': 'link_page',
'content': page_url,
'page_id': page_id,
'settings': settings,
'created_at': datetime.now().isoformat(),
'image_data': img_base64
}
qr_codes_db[qr_id] = qr_record
# 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
@app.route('/api/link_pages/<page_id>/links', methods=['POST'])
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
@app.route('/api/link_pages/<page_id>/links/<link_id>', methods=['PUT'])
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
@app.route('/api/link_pages/<page_id>/links/<link_id>', methods=['DELETE'])
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
@app.route('/api/link_pages/<page_id>')
def get_link_page(page_id):
"""Get link page data"""
if page_id in link_pages_db:
return jsonify(link_pages_db[page_id])
else:
return jsonify({'error': 'Page not found'}), 404
@app.route('/links/<page_id>')
def view_link_page(page_id):
"""Display the public link page"""
if page_id not in link_pages_db:
return "Page not found", 404
link_manager.increment_view_count(page_id)
page_data = link_pages_db[page_id]
return render_template('link_page.html', page=page_data)
@app.route('/edit/<page_id>')
def edit_link_page(page_id):
"""Display the edit interface for the link page"""
if page_id not in link_pages_db:
return "Page not found", 404
page_data = link_pages_db[page_id]
return render_template('edit_links.html', page=page_data)
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=5000)