vpn-btcpay-provisioner/app/__init__.py

163 lines
5.9 KiB
Python
Raw Permalink Normal View History

2024-12-13 09:57:12 +00:00
from flask import Flask, request, jsonify, render_template
2024-12-11 09:05:26 +00:00
import logging
2025-01-10 08:20:21 +00:00
import os
from pathlib import Path
2024-12-11 09:05:26 +00:00
from .handlers.webhook_handler import handle_payment_webhook
2024-12-13 09:57:12 +00:00
from .handlers.payment_handler import BTCPayHandler
from .utils.db.operations import DatabaseManager
2024-12-11 09:05:26 +00:00
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
app = Flask(__name__)
2025-01-10 08:20:21 +00:00
app.config['SECRET_KEY'] = os.getenv('FLASK_SECRET_KEY', 'dev-secret-key')
2024-12-13 09:57:12 +00:00
btcpay_handler = BTCPayHandler()
2024-12-11 09:05:26 +00:00
2025-01-10 08:20:21 +00:00
# Register blueprints
from .routes.user import user_bp
app.register_blueprint(user_bp, url_prefix='/user')
2024-12-13 09:57:12 +00:00
# Existing webhook route
2024-12-11 09:05:26 +00:00
@app.route('/webhook/vpn', methods=['POST'])
def handle_payment():
return handle_payment_webhook(request)
2024-12-13 09:57:12 +00:00
# Frontend routes
@app.route('/')
def index():
return render_template('index.html')
@app.route('/api/calculate-price', methods=['POST'])
def calculate_price():
hours = request.json.get('hours', 0)
# Basic price calculation - adjust formula as needed
base_price = hours * 100 # 100 sats per hour
# Apply volume discounts
if hours >= 720: # 1 month
base_price = base_price * 0.85 # 15% discount
elif hours >= 168: # 1 week
base_price = base_price * 0.90 # 10% discount
elif hours >= 24: # 1 day
base_price = base_price * 0.95 # 5% discount
return jsonify({
'price': int(base_price),
'duration': hours
})
@app.route('/create-invoice', methods=['POST'])
def create_invoice():
try:
logger.info("=== Create Invoice Request Started ===")
logger.info(f"Received invoice creation request with data: {request.json}")
2024-12-13 09:57:12 +00:00
data = request.json
logger.debug(f"Request data: {data}")
2024-12-13 09:57:12 +00:00
# Validate input data
duration_hours = data.get('duration')
user_id = data.get('user_id')
public_key = data.get('public_key')
logger.info(f"Validating request parameters: duration={duration_hours}, user_id={user_id}, has_public_key={bool(public_key)}")
# Validate required fields
2024-12-13 09:57:12 +00:00
if not duration_hours:
logger.error("Duration missing from request")
return jsonify({'error': 'Duration is required'}), 400
if not user_id:
logger.error("User ID missing from request")
return jsonify({'error': 'User ID is required'}), 400
if not public_key:
logger.error("Public key missing from request")
return jsonify({'error': 'Public key is required'}), 400
2024-12-13 09:57:12 +00:00
try:
duration_hours = int(duration_hours)
logger.info(f"Converted duration to integer: {duration_hours}")
2024-12-13 09:57:12 +00:00
except ValueError:
logger.error(f"Invalid duration value: {duration_hours}")
return jsonify({'error': 'Invalid duration value'}), 400
# Calculate price
2024-12-13 09:57:12 +00:00
base_price = duration_hours * 100 # 100 sats per hour
if duration_hours >= 720: # 1 month
base_price = base_price * 0.85 # 15% discount
elif duration_hours >= 168: # 1 week
base_price = base_price * 0.90 # 10% discount
elif duration_hours >= 24: # 1 day
base_price = base_price * 0.95 # 5% discount
2024-12-13 09:57:12 +00:00
amount_sats = int(base_price)
logger.info(f"Calculated price: {amount_sats} sats for {duration_hours} hours")
2024-12-13 09:57:12 +00:00
# Create BTCPay invoice
logger.info("Creating BTCPay invoice")
invoice_data = btcpay_handler.create_invoice(
amount_sats=amount_sats,
duration_hours=duration_hours,
user_id=user_id,
public_key=public_key
)
2024-12-13 09:57:12 +00:00
if not invoice_data:
logger.error("Failed to create invoice - no data returned from BTCPayHandler")
return jsonify({'error': 'Failed to create invoice'}), 500
2024-12-13 09:57:12 +00:00
logger.info(f"Successfully created invoice with ID: {invoice_data.get('invoice_id')}")
logger.info("=== Create Invoice Request Completed ===")
2024-12-13 09:57:12 +00:00
return jsonify(invoice_data)
2024-12-13 09:57:12 +00:00
except Exception as e:
logger.error(f"Error in create_invoice endpoint: {str(e)}")
logger.error(f"Traceback: ", exc_info=True)
2024-12-13 09:57:12 +00:00
return jsonify({'error': str(e)}), 500
@app.route('/api/vpn-config/<user_id>')
def get_vpn_config(user_id):
try:
logger.info(f"Fetching VPN config for user: {user_id}")
subscription = DatabaseManager.get_active_subscription_for_user(user_id)
if not subscription:
logger.error(f"No active subscription found for user {user_id}")
return jsonify({"error": "No active subscription found"}), 404
# Get the config based on test or production path
base_path = Path('/etc/wireguard')
if subscription.invoice_id.startswith('__test__'):
config_path = base_path / 'test_clients' / subscription.invoice_id / 'wg0.conf'
else:
config_path = base_path / 'clients' / subscription.invoice_id / 'wg0.conf'
logger.info(f"Looking for config at: {config_path}")
if not config_path.exists():
logger.error(f"Configuration file not found at {config_path}")
return jsonify({"error": "Configuration file not found"}), 404
with open(config_path) as f:
config_text = f.read()
logger.info(f"Successfully retrieved config for user {user_id}")
return jsonify({
"configText": config_text,
"status": "active",
"expiryTime": subscription.expiry_time.isoformat() if subscription.expiry_time else None
})
except Exception as e:
logger.error(f"Error retrieving VPN config: {str(e)}")
logger.error("Traceback:", exc_info=True)
return jsonify({"error": "Failed to retrieve configuration"}), 500
2024-12-13 09:57:12 +00:00
@app.route('/payment/success')
def payment_success():
return render_template('payment_success.html')
2024-12-11 09:05:26 +00:00
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)