mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-14 15:15:22 +03:00
POC: Added FastAPI with one endpoint (get /logs/service)
This commit is contained in:
parent
17901fcfef
commit
c50283b55e
@ -2,7 +2,7 @@ daemon off;
|
||||
user root;
|
||||
worker_processes auto;
|
||||
|
||||
error_log /dev/stdout warn;
|
||||
error_log /dev/stdout debug;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
events {
|
||||
@ -222,12 +222,16 @@ http {
|
||||
include proxy.conf;
|
||||
}
|
||||
|
||||
location ~* /api/.*\.(jpg|jpeg|png|webp|gif)$ {
|
||||
include auth_request.conf;
|
||||
rewrite ^/api/(.*)$ $1 break;
|
||||
proxy_pass http://frigate_api;
|
||||
include proxy.conf;
|
||||
}
|
||||
# FIXME: Needed to disabled this rule, otherwise it fails for endpoints that end with one of those file extensions
|
||||
# 1. with httptools it passes the auth.conf but then throws a 400 error "WARN "Invalid HTTP request received." -> https://github.com/encode/uvicorn/blob/47304d9ae76321f0f5f649ff4f73e09b17085933/uvicorn/protocols/http/httptools_impl.py#L165
|
||||
# 2. With h11 it goes through the auth.conf but returns a 404 error
|
||||
# We might need to add extra rules that will allow endpoint that end with an extension OR find a fix without creating other rules
|
||||
# location ~* /api/.*\.(jpg|jpeg|png|webp|gif)$ {
|
||||
# include auth_request.conf;
|
||||
# rewrite ^/api/(.*)$ $1 break;
|
||||
# proxy_pass http://frigate_api;
|
||||
# include proxy.conf;
|
||||
# }
|
||||
|
||||
location /api/ {
|
||||
include auth_request.conf;
|
||||
|
||||
@ -10,6 +10,9 @@ from functools import reduce
|
||||
from typing import Optional
|
||||
|
||||
import requests
|
||||
from fastapi import APIRouter, Path
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from fastapi.responses import JSONResponse
|
||||
from flask import Blueprint, Flask, current_app, jsonify, make_response, request
|
||||
from markupsafe import escape
|
||||
from peewee import operator
|
||||
@ -17,6 +20,7 @@ from playhouse.sqliteq import SqliteQueueDatabase
|
||||
from werkzeug.middleware.proxy_fix import ProxyFix
|
||||
|
||||
from frigate.api.auth import AuthBp, get_jwt_secret, limiter
|
||||
from frigate.api.defs.tags import Tags
|
||||
from frigate.api.event import EventBp
|
||||
from frigate.api.export import ExportBp
|
||||
from frigate.api.media import MediaBp
|
||||
@ -52,6 +56,8 @@ bp.register_blueprint(ReviewBp)
|
||||
bp.register_blueprint(AuthBp)
|
||||
bp.register_blueprint(NotificationBp)
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
def create_app(
|
||||
frigate_config,
|
||||
@ -454,19 +460,26 @@ def vainfo():
|
||||
)
|
||||
|
||||
|
||||
@bp.route("/logs/<service>", methods=["GET"])
|
||||
def logs(service: str):
|
||||
@router.get("/logs/{service}", tags=[Tags.logs])
|
||||
def logs(
|
||||
service: str = Path(enum=["frigate", "nginx", "go2rtc", "chroma"]),
|
||||
download: Optional[str] = None,
|
||||
start: Optional[int] = 0,
|
||||
end: Optional[int] = None,
|
||||
):
|
||||
"""Get logs for the requested service (frigate/nginx/go2rtc/chroma)"""
|
||||
|
||||
def download_logs(service_location: str):
|
||||
try:
|
||||
file = open(service_location, "r")
|
||||
contents = file.read()
|
||||
file.close()
|
||||
return jsonify(contents)
|
||||
return JSONResponse(jsonable_encoder(contents))
|
||||
except FileNotFoundError as e:
|
||||
logger.error(e)
|
||||
return make_response(
|
||||
jsonify({"success": False, "message": "Could not find log file"}),
|
||||
500,
|
||||
return JSONResponse(
|
||||
content={"success": False, "message": "Could not find log file"},
|
||||
status_code=500,
|
||||
)
|
||||
|
||||
log_locations = {
|
||||
@ -478,17 +491,14 @@ def logs(service: str):
|
||||
service_location = log_locations.get(service)
|
||||
|
||||
if not service_location:
|
||||
return make_response(
|
||||
jsonify({"success": False, "message": "Not a valid service"}),
|
||||
404,
|
||||
return JSONResponse(
|
||||
content={"success": False, "message": "Not a valid service"},
|
||||
status_code=404,
|
||||
)
|
||||
|
||||
if request.args.get("download", type=bool, default=False):
|
||||
if download:
|
||||
return download_logs(service_location)
|
||||
|
||||
start = request.args.get("start", type=int, default=0)
|
||||
end = request.args.get("end", type=int)
|
||||
|
||||
try:
|
||||
file = open(service_location, "r")
|
||||
contents = file.read()
|
||||
@ -529,15 +539,15 @@ def logs(service: str):
|
||||
|
||||
logLines.append(currentLine)
|
||||
|
||||
return make_response(
|
||||
jsonify({"totalLines": len(logLines), "lines": logLines[start:end]}),
|
||||
200,
|
||||
return JSONResponse(
|
||||
content={"totalLines": len(logLines), "lines": logLines[start:end]},
|
||||
status_code=200,
|
||||
)
|
||||
except FileNotFoundError as e:
|
||||
logger.error(e)
|
||||
return make_response(
|
||||
jsonify({"success": False, "message": "Could not find log file"}),
|
||||
500,
|
||||
return JSONResponse(
|
||||
content={"success": False, "message": "Could not find log file"},
|
||||
status_code=500,
|
||||
)
|
||||
|
||||
|
||||
|
||||
7
frigate/api/defs/tags.py
Normal file
7
frigate/api/defs/tags.py
Normal file
@ -0,0 +1,7 @@
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class Tags(Enum):
|
||||
preview = "Preview"
|
||||
logs = "Logs"
|
||||
media = "Media"
|
||||
53
frigate/api/fastapi_app.py
Normal file
53
frigate/api/fastapi_app.py
Normal file
@ -0,0 +1,53 @@
|
||||
import logging
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
from frigate.api import app as main_app
|
||||
from frigate.api.defs.tags import Tags
|
||||
from frigate.plus import PlusApi
|
||||
from frigate.ptz.onvif import OnvifController
|
||||
from frigate.stats.emitter import StatsEmitter
|
||||
from frigate.storage import StorageMaintainer
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# https://fastapi.tiangolo.com/tutorial/metadata/#use-your-tags
|
||||
tags_metadata = [
|
||||
{
|
||||
"name": Tags.preview,
|
||||
"description": "Preview routes",
|
||||
},
|
||||
{
|
||||
"name": Tags.logs,
|
||||
"description": "Logs routes",
|
||||
},
|
||||
{
|
||||
"name": Tags.media,
|
||||
"description": "Media routes",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def create_fastapi_app(
|
||||
frigate_config,
|
||||
detected_frames_processor,
|
||||
storage_maintainer: StorageMaintainer,
|
||||
onvif: OnvifController,
|
||||
plus_api: PlusApi,
|
||||
stats_emitter: StatsEmitter,
|
||||
):
|
||||
logger.info("Starting FastAPI app")
|
||||
app = FastAPI(debug=False, tags_metadata=tags_metadata)
|
||||
# Routes
|
||||
app.include_router(main_app.router)
|
||||
# App Properties
|
||||
app.frigate_config = frigate_config
|
||||
app.detected_frames_processor = detected_frames_processor
|
||||
app.storage_maintainer = storage_maintainer
|
||||
app.camera_error_image = None
|
||||
app.onvif = onvif
|
||||
app.plus_api = plus_api
|
||||
app.stats_emitter = stats_emitter
|
||||
|
||||
return app
|
||||
@ -14,6 +14,8 @@ from types import FrameType
|
||||
from typing import Optional
|
||||
|
||||
import psutil
|
||||
import uvicorn
|
||||
from fastapi.middleware.wsgi import WSGIMiddleware
|
||||
from peewee_migrate import Router
|
||||
from playhouse.sqlite_ext import SqliteExtDatabase
|
||||
from playhouse.sqliteq import SqliteQueueDatabase
|
||||
@ -21,6 +23,7 @@ from pydantic import ValidationError
|
||||
|
||||
from frigate.api.app import create_app
|
||||
from frigate.api.auth import hash_password
|
||||
from frigate.api.fastapi_app import create_fastapi_app
|
||||
from frigate.comms.config_updater import ConfigPublisher
|
||||
from frigate.comms.dispatcher import Communicator, Dispatcher
|
||||
from frigate.comms.inter_process import InterProcessCommunicator
|
||||
@ -397,6 +400,15 @@ class FrigateApp:
|
||||
self.stats_emitter,
|
||||
)
|
||||
|
||||
self.fastapi_app = create_fastapi_app(
|
||||
self.config,
|
||||
self.detected_frames_processor,
|
||||
self.storage_maintainer,
|
||||
self.onvif_controller,
|
||||
self.plus_api,
|
||||
self.stats_emitter,
|
||||
)
|
||||
|
||||
def init_onvif(self) -> None:
|
||||
self.onvif_controller = OnvifController(self.config, self.ptz_metrics)
|
||||
|
||||
@ -754,11 +766,17 @@ class FrigateApp:
|
||||
signal.signal(signal.SIGTERM, receiveSignal)
|
||||
|
||||
try:
|
||||
self.flask_app.run(host="127.0.0.1", port=5001, debug=False, threaded=True)
|
||||
# Run the flask app inside fastapi: https://fastapi.tiangolo.com/advanced/sub-applications/
|
||||
self.fastapi_app.mount("", WSGIMiddleware(self.flask_app))
|
||||
uvicorn.run(
|
||||
self.fastapi_app,
|
||||
host="127.0.0.1",
|
||||
port=5001,
|
||||
)
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
|
||||
logger.info("Flask has exited...")
|
||||
logger.info("FastAPI/Flask has exited...")
|
||||
|
||||
self.stop()
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user