#!/usr/bin/env python3 """ Shared utilities for API v1 — error helpers, pagination, DB access. """ import sqlite3 import os from flask import jsonify, request DB_PATH = os.environ.get("TURF_SAAS_DB", "/home/h3r7/turf_saas/turf_saas.db") # ────────────────────────────────────────────────────────────── # Database # ────────────────────────────────────────────────────────────── def get_db(): """Return a SQLite connection with Row factory (reads TURF_SAAS_DB dynamically).""" db_path = os.environ.get("TURF_SAAS_DB", DB_PATH) conn = sqlite3.connect(db_path) conn.row_factory = sqlite3.Row return conn def table_exists(conn, table_name: str) -> bool: row = conn.execute( "SELECT 1 FROM sqlite_master WHERE type='table' AND name=?", (table_name,) ).fetchone() return row is not None # ────────────────────────────────────────────────────────────── # Uniform error responses # ────────────────────────────────────────────────────────────── def error_response(message: str, code: int, status: str = "error"): """Return a JSON error envelope consistent with the API contract. Shape: {"status": "error", "message": "...", "code": 400} """ return jsonify({"status": status, "message": message, "code": code}), code def not_found(message: str = "Resource not found"): return error_response(message, 404) def bad_request(message: str = "Bad request"): return error_response(message, 400) def forbidden(message: str = "Forbidden", required_plans=None, current_plan=None): payload = {"status": "error", "message": message, "code": 403} if required_plans: payload["required_plans"] = required_plans if current_plan: payload["current_plan"] = current_plan payload["upgrade_url"] = "/api/v1/subscription/upgrade" return jsonify(payload), 403 def internal_error(message: str = "Internal server error"): return error_response(message, 500) # ────────────────────────────────────────────────────────────── # Pagination helpers # ────────────────────────────────────────────────────────────── def get_pagination_params(default_limit: int = 20, max_limit: int = 100): """Extract and validate limit/offset from query-string.""" try: limit = int(request.args.get("limit", default_limit)) except (ValueError, TypeError): limit = default_limit try: offset = int(request.args.get("offset", 0)) except (ValueError, TypeError): offset = 0 limit = max(1, min(limit, max_limit)) offset = max(0, offset) return limit, offset def paginate_query(rows, total: int, limit: int, offset: int): """Wrap a list of rows in a pagination envelope.""" return { "pagination": { "total": total, "limit": limit, "offset": offset, "has_more": (offset + limit) < total, } }