Update API, fix bugs and add more methods

This commit is contained in:
Santiago Lo Coco 2024-11-05 18:42:26 +01:00
parent c2668b0bfd
commit a807f899f5
8 changed files with 151 additions and 45 deletions

View File

@ -8,8 +8,6 @@ RUN pip install --no-cache-dir -r requirements.txt
COPY . . COPY . .
EXPOSE 5000
STOPSIGNAL SIGTERM STOPSIGNAL SIGTERM
CMD ["python", "app.py"] CMD ["python", "app.py"]

View File

@ -4,3 +4,5 @@ SERVICE_NAME=room7200
SERVICE_TYPE=_http._tcp.local. SERVICE_TYPE=_http._tcp.local.
SERVICE_IP=192.168.137.1 SERVICE_IP=192.168.137.1
SERVICE_PORT=5000 SERVICE_PORT=5000
DEFAULT_ENDPOINTS=true
LOG_LEVEL=WARN

View File

@ -1,33 +1,54 @@
import socket
from flask import Blueprint, abort, jsonify, request from flask import Blueprint, abort, jsonify, request
from config import API_HOST from db import (
from db import get_endpoint_by_id, get_endpoints_from_db, update_endpoint_in_db add_endpoint_to_db,
delete_endpoint_from_db,
get_endpoint_by_id,
get_endpoint_by_url,
get_endpoints_from_db,
update_endpoint_in_db,
)
from utils import is_valid_url
api = Blueprint("api", __name__) api = Blueprint("api", __name__)
@api.route("/api/ip", methods=["GET"])
def get_ip():
try:
ip_address = socket.gethostbyname(API_HOST)
return jsonify({"ip": ip_address}), 200
except socket.gaierror:
return jsonify({"error": f"Could not resolve IP address for {API_HOST}"}), 404
@api.route("/api/endpoints", methods=["GET"]) @api.route("/api/endpoints", methods=["GET"])
def get_endpoints(): def get_endpoints():
endpoints = get_endpoints_from_db() endpoints = get_endpoints_from_db()
return jsonify(endpoints) return jsonify(endpoints), 200
@api.route("/api/endpoints", methods=["POST"])
def create_endpoint():
data = request.get_json()
new_url = data.get("url")
if new_url is None or not is_valid_url(new_url):
return jsonify({"status": "error", "message": "Invalid or missing URL"}), 400
existing_endpoint = get_endpoint_by_url(new_url)
if existing_endpoint:
return (
jsonify(
{
"status": "error",
"message": "Endpoint already exists",
"id": existing_endpoint["id"],
}
),
409,
)
endpoint_id = add_endpoint_to_db(new_url)
return jsonify({"id": endpoint_id, "url": new_url}), 201
@api.route("/api/endpoints/<int:endpoint_id>", methods=["GET"]) @api.route("/api/endpoints/<int:endpoint_id>", methods=["GET"])
def get_endpoint(endpoint_id): def get_endpoint(endpoint_id):
endpoint = get_endpoint_by_id(endpoint_id) endpoint = get_endpoint_by_id(endpoint_id)
if endpoint: if endpoint:
return jsonify(endpoint) return jsonify(endpoint), 200
else: else:
abort(404) abort(404)
@ -37,16 +58,25 @@ def update_endpoint(endpoint_id):
data = request.get_json() data = request.get_json()
new_url = data.get("url") new_url = data.get("url")
if new_url: if not new_url or not is_valid_url(new_url):
update_endpoint_in_db(endpoint_id, new_url) return jsonify({"status": "error", "message": "Invalid or missing URL"}), 400
return (
jsonify( updated = update_endpoint_in_db(endpoint_id, new_url)
{ if updated:
"status": "success", return "", 204
"message": f"Endpoint {endpoint_id} updated to {new_url}",
}
),
200,
)
else: else:
return jsonify({"status": "error", "message": "No new endpoint provided."}), 400 return jsonify({"status": "error", "message": "Endpoint not found"}), 404
@api.route("/api/endpoints/<int:endpoint_id>", methods=["DELETE"])
def delete_endpoint(endpoint_id):
deleted = delete_endpoint_from_db(endpoint_id)
if deleted:
return "", 204
else:
return jsonify({"status": "error", "message": "Endpoint not found"}), 404
@api.route("/api/health", methods=["GET"])
def health():
return jsonify({"status": "ok"})

View File

@ -1,3 +1,4 @@
import logging
import signal import signal
import sys import sys
@ -5,7 +6,7 @@ from flask import Flask, render_template
from waitress import serve from waitress import serve
from api import api from api import api
from config import API_HOST, DATABASE_URI from config import API_HOST, DATABASE_URI, LOG_LEVEL, SERVICE_IP, SERVICE_PORT
from db import get_endpoints_from_db, init_db from db import get_endpoints_from_db, init_db
from register import register_service, unregister_service from register import register_service, unregister_service
@ -32,9 +33,11 @@ def homepage():
if __name__ == "__main__": if __name__ == "__main__":
logging.basicConfig(level=logging.getLevelName(LOG_LEVEL))
register_service() register_service()
try: try:
serve(app, host="0.0.0.0", port=5000) serve(app, host=SERVICE_IP, port=SERVICE_PORT)
finally: finally:
unregister_service() unregister_service()

View File

@ -10,3 +10,5 @@ SERVICE_NAME = os.getenv("SERVICE_NAME")
SERVICE_TYPE = os.getenv("SERVICE_TYPE") SERVICE_TYPE = os.getenv("SERVICE_TYPE")
SERVICE_IP = os.getenv("SERVICE_IP") SERVICE_IP = os.getenv("SERVICE_IP")
SERVICE_PORT = os.getenv("SERVICE_PORT") SERVICE_PORT = os.getenv("SERVICE_PORT")
DEFAULT_ENDPOINTS = os.getenv("DEFAULT_ENDPOINTS")
LOG_LEVEL = os.getenv("LOG_LEVEL")

View File

@ -1,6 +1,8 @@
import logging
from flask_sqlalchemy import SQLAlchemy from flask_sqlalchemy import SQLAlchemy
from config import API_HOST from config import API_HOST, DEFAULT_ENDPOINTS
db = SQLAlchemy() db = SQLAlchemy()
@ -14,6 +16,7 @@ def init_db(app):
db.init_app(app) db.init_app(app)
with app.app_context(): with app.app_context():
db.create_all() db.create_all()
if DEFAULT_ENDPOINTS:
init_default_endpoints() init_default_endpoints()
@ -28,6 +31,7 @@ def init_default_endpoints():
updated = False updated = False
for endpoint in default_endpoints: for endpoint in default_endpoints:
if endpoint.id not in existing_endpoints: if endpoint.id not in existing_endpoints:
logging.info(f"Adding default endpoint {endpoint.url}")
db.session.add(endpoint) db.session.add(endpoint)
updated = True updated = True
@ -40,13 +44,49 @@ def get_endpoints_from_db():
return [{"id": endpoint.id, "url": endpoint.url} for endpoint in endpoints] return [{"id": endpoint.id, "url": endpoint.url} for endpoint in endpoints]
def add_endpoint_to_db(url):
new_endpoint = Endpoint(url=url)
db.session.add(new_endpoint)
db.session.commit()
logging.info(f"Added new endpoint {url}")
return new_endpoint.id
# def update_endpoint_in_db(endpoint_id, new_url):
# with db.session.begin():
# endpoint = Endpoint.query.get(endpoint_id)
# if endpoint:
# endpoint.url = new_url
# db.session.add(endpoint)
# return True
# else:
# db.session.add(Endpoint(id=endpoint_id, url=new_url))
# return False
def update_endpoint_in_db(endpoint_id, new_url): def update_endpoint_in_db(endpoint_id, new_url):
with db.session.begin():
endpoint = Endpoint.query.get(endpoint_id) endpoint = Endpoint.query.get(endpoint_id)
if endpoint: if endpoint:
endpoint.url = new_url endpoint.url = new_url
else: db.session.commit()
db.session.add(Endpoint(id=endpoint_id, url=new_url)) logging.info(f"Updated endpoint {endpoint_id} to {new_url}")
return True
return False
def get_endpoint_by_url(url):
endpoint = Endpoint.query.filter_by(url=url).first()
return {"id": endpoint.id, "url": endpoint.url} if endpoint else None
def delete_endpoint_from_db(endpoint_id):
endpoint = Endpoint.query.get(endpoint_id)
if endpoint:
db.session.delete(endpoint)
db.session.commit()
logging.info(f"Deleted endpoint {endpoint_id}")
return True
return False
def get_endpoint_by_id(endpoint_id): def get_endpoint_by_id(endpoint_id):

View File

@ -1,3 +1,5 @@
import logging
from zeroconf import ServiceInfo, Zeroconf from zeroconf import ServiceInfo, Zeroconf
from config import SERVICE_IP, SERVICE_NAME, SERVICE_PORT, SERVICE_TYPE from config import SERVICE_IP, SERVICE_NAME, SERVICE_PORT, SERVICE_TYPE
@ -15,11 +17,25 @@ info = ServiceInfo(
def register_service(): def register_service():
try:
zeroconf.register_service(info) zeroconf.register_service(info)
print(f"Service {SERVICE_NAME}.{SERVICE_TYPE} registered") logging.info(f"Service {SERVICE_NAME}.{SERVICE_TYPE} registered")
logging.info(
f"Service is available at http://{SERVICE_NAME}.local:{SERVICE_PORT}"
)
except Exception:
logging.error(f"Failed to register service {SERVICE_NAME}.{SERVICE_TYPE}")
logging.error(
f"Please add {SERVICE_NAME}.local to /etc/hosts or to your DNS server"
)
pass
def unregister_service(): def unregister_service():
try:
zeroconf.unregister_service(info) zeroconf.unregister_service(info)
print(f"Service {SERVICE_NAME}.{SERVICE_TYPE} unregistered") logging.info(f"Service {SERVICE_NAME}.{SERVICE_TYPE} unregistered")
zeroconf.close() zeroconf.close()
except Exception:
logging.error(f"Failed to unregister service {SERVICE_NAME}.{SERVICE_TYPE}")
pass

15
src/utils.py Normal file
View File

@ -0,0 +1,15 @@
import re
def is_valid_url(url):
url_regex = re.compile(
r"^(https?):\/\/"
r"(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}\.?)|"
r"localhost|"
r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|"
r"\[?[A-F0-9]*:[A-F0-9:]+\]?)"
r"(?::\d+)?"
r"(?:\/[^\s]*)?$",
re.IGNORECASE,
)
return re.match(url_regex, url) is not None