Cristobal299/flask-sqlite-api
0
1# -*- coding: utf-8 -*-2import os3import sqlite34import json5import requests6from flask import Flask, request, jsonify, g7 8app = Flask(__name__)9 10@app.after_request11def add_cors_headers(response):12 response.headers['Access-Control-Allow-Origin'] = '*'13 response.headers['Access-Control-Allow-Methods'] = 'GET,POST,PUT,DELETE,OPTIONS'14 response.headers['Access-Control-Allow-Headers'] = 'Content-Type,Authorization'15 return response16 17# ----------------------------------------------------------------------18# Database utilities (unchanged)19# ----------------------------------------------------------------------20DATABASE = os.path.join(os.path.dirname(__file__), "tasks.db")21 22def get_db():23 db = getattr(g, "_database", None)24 if db is None:25 db = g._database = sqlite3.connect(DATABASE, check_same_thread=False)26 db.row_factory = sqlite3.Row27 return db28 29def init_db():30 conn = sqlite3.connect(DATABASE)31 cursor = conn.cursor()32 cursor.execute(33 """34 CREATE TABLE IF NOT EXISTS tasks (35 id INTEGER PRIMARY KEY AUTOINCREMENT,36 title TEXT NOT NULL,37 completed INTEGER NOT NULL DEFAULT 038 )39 """40 )41 conn.commit()42 conn.close()43 44@app.teardown_appcontext45def close_connection(exception):46 db = getattr(g, "_database", None)47 if db is not None:48 db.close()49 50# ----------------------------------------------------------------------51# Existing task endpoints (unchanged)52# ----------------------------------------------------------------------53@app.route("/tasks", methods=["GET"])54def list_tasks():55 db = get_db()56 cursor = db.execute("SELECT id, title, completed FROM tasks")57 tasks = [58 {"id": row["id"], "title": row["title"], "completed": bool(row["completed"])}59 for row in cursor.fetchall()60 ]61 return jsonify(tasks)62 63@app.route("/tasks", methods=["POST"])64def create_task():65 data = request.get_json()66 title = data.get("title")67 if not title:68 return jsonify({"error": "title is required"}), 40069 db = get_db()70 cursor = db.execute(71 "INSERT INTO tasks (title, completed) VALUES (?, ?)",72 (title, 0),73 )74 db.commit()75 task_id = cursor.lastrowid76 return jsonify({"id": task_id, "title": title, "completed": False}), 20177 78@app.route("/tasks/<int:task_id>", methods=["PUT"])79def update_task(task_id):80 data = request.get_json()81 title = data.get("title")82 completed = data.get("completed")83 if title is None and completed is None:84 return jsonify({"error": "nothing to update"}), 40085 db = get_db()86 if title is not None:87 db.execute("UPDATE tasks SET title = ? WHERE id = ?", (title, task_id))88 if completed is not None:89 db.execute(90 "UPDATE tasks SET completed = ? WHERE id = ?",91 (1 if completed else 0, task_id),92 )93 db.commit()94 return jsonify({"id": task_id, "title": title, "completed": completed})95 96@app.route("/tasks/<int:task_id>", methods=["DELETE"])97def delete_task(task_id):98 db = get_db()99 db.execute("DELETE FROM tasks WHERE id = ?", (task_id,))100 db.commit()101 return "", 204102 103# ----------------------------------------------------------------------104# Health endpoints (unchanged)105# ----------------------------------------------------------------------106@app.route("/", methods=["GET"])107@app.route("/test", methods=["GET"])108def index():109 return "Task API is running."110 111@app.route("/ping", methods=["GET"])112def ping():113 return "pong", 200114 115# ----------------------------------------------------------------------116# New: Generic API connector117# ----------------------------------------------------------------------118@app.route("/api/connector", methods=["GET", "POST"])119def api_connector():120 """121 Expects a JSON payload with the following fields:122 {123 "url": "https://example.com/endpoint",124 "method": "GET|POST|PUT|DELETE|PATCH",125 "headers": {"Authorization": "Bearer ...", ...}, # optional126 "params": {"q": "search"}, # optional, for query string127 "json": {"key": "value"} # optional, for JSON body128 }129 The endpoint forwards the request to the target API and returns130 the raw response (status code, headers and JSON body if possible).131 """132 payload = request.get_json()133 if not payload:134 return jsonify({"error": "invalid json payload"}), 400135 136 url = payload.get("url")137 # placeholder replacement removed138 method = payload.get("method", "GET").upper()139 headers = payload.get("headers", {})140 params = payload.get("params", {})141 json_body = payload.get("json", None)142 143 if not url:144 return jsonify({"error": "url is required"}), 400145 146 # Optional: inject a default API key from environment if not provided147 default_key = os.environ.get("DEFAULT_API_KEY")148 if default_key and "Authorization" not in headers:149 headers["Authorization"] = f"Bearer {default_key}"150 151 try:152 response = requests.request(153 method=method,154 url=url,155 headers=headers,156 params=params,157 json=json_body,158 timeout=30159 )160 # Do not raise for HTTP errors; return the response as is161 except requests.RequestException as e:162 return jsonify({"error": "request failed", "details": str(e)}), 502163 164 # Try to parse JSON, fallback to raw text165 try:166 resp_content = response.json()167 except ValueError:168 resp_content = response.text169 170 result = {171 "status_code": response.status_code,172 "headers": dict(response.headers),173 "content": resp_content174 }175 return jsonify(result), response.status_code176 177# ----------------------------------------------------------------------178# Error handling (unchanged)179# ----------------------------------------------------------------------180@app.errorhandler(404)181def not_found(e):182 return jsonify({"error": "not found"}), 404183 184# ----------------------------------------------------------------------185# Main entry point (unchanged)186# ----------------------------------------------------------------------187if __name__ == "__main__":188 if not os.path.isfile(DATABASE):189 init_db()190 app.run(host="0.0.0.0", port=7860)