import json, requests from flask import Flask, request, session, render_template, redirect, url_for, Response, send_file from flask_session import Session import pandas as pd import numpy as np from matplotlib.figure import Figure import matplotlib.dates as mdates import base64 from tqdm import tqdm import datetime from io import BytesIO import logging logger = logging.getLogger() from functools import wraps from EagleEyev3 import * from settings import config SECRET_KEY = "this needs to be changed to something unique and not checked into git" # check if it could pull in a config object from settings.py if config: # start checking for keys in config object and set sensible defaults if 'days_of_history' in config: DAYS_OF_HISTORY = config['days_of_history'] else: # fallback to a sane default DAYS_OF_HISTORY = 1 if 'log_level' in config: logger.setLevel(config['log_level']) else: logging.setLevel('INFO') if 'client_secret' in config: SECRET_KEY = config['client_secret'] else: logging.setLevel('INFO') logging.info(f"Using EagleEyev3 version {EagleEyev3.__version__}") app = Flask(__name__) app.secret_key = SECRET_KEY SESSION_TYPE = 'filesystem' SESSION_PERMANENT = False PERMANENT_SESSION_LIFETIME = 60 * 60 * 24 * 7 # one week in seconds app.config.from_object(__name__) Session(app) from flask_sqlalchemy import SQLAlchemy from models import * app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///project.db" db.init_app(app) with app.app_context(): db.create_all() # $ flask routes -s rule # INFO:root:Using EagleEyev3 version 0.0.17 # Endpoint Methods Rule # --------------------- ------- -------------------------------------------- # index GET / # accounts GET /accounts # camera_live_preivew GET /camera//preview # camera__preivew_image GET /camera//preview_image # camera_video_player GET /camera//video_player/ # camera_list_of_videos GET /camera//videos # cameras GET /cameras # landing GET /landing # login_callback GET /login_callback # logout GET /logout # static GET /static/ # switch_account GET /switch_account def login_required(f): @wraps(f) def decorated_function(*args, **kwargs): logging.debug(session) if 'een' in session and session['een'].access_token: logging.debug(f"@login_required access_token: {session['een'].access_token}") return f(*args, **kwargs) else: # failed to find a valid session so redirecting to landing page if 'een' in session: logging.debug(f"@login_required access_token: {session['een'].access_token}") else: logging.warn('@login_requried failed to find a valid session') return redirect(url_for('landing')) return decorated_function @app.route('/landing') def landing(): # sometimes you just want to get to the landing page and want to avoid the index redirect logic if 'een' in session: een = session['een'] else: een = EagleEyev3(config) session['een'] = een base_url = "https://auth.eagleeyenetworks.com/oauth2/authorize" path_url = f"?client_id={een.client_id}&response_type=code&scope=vms.all&redirect_uri={een.redirect_uri}" values = { "login_link": f"{base_url}{path_url}" } return render_template('cover.html', template_values=values) @app.route('/') @login_required def index(): # get een instance from session, @login_required already checked for it een = session['een'] # using current_user as a proxy for an established valid session if een.access_token == None: base_url = "https://auth.eagleeyenetworks.com/oauth2/authorize" path_url = f"?client_id={een.client_id}&response_type=code&scope=vms.all&redirect_uri={een.redirect_uri}" values = { "login_link": f"{base_url}{path_url}" } return render_template('cover.html', template_values=values) # call this to check if session is actually valid check = een.get_current_user() if 'success' not in check or check['success'] == False: base_url = "https://auth.eagleeyenetworks.com/oauth2/authorize" path_url = f"?client_id={een.client_id}&response_type=code&scope=vms.all&redirect_uri={een.redirect_uri}" values = { "login_link": f"{base_url}{path_url}" } return render_template('cover.html', template_values=values) else: logging.info(f"{check['success']} - check get_current_user {een.current_user['email']} {een.access_token}") een.get_list_of_cameras() een.get_list_of_accounts() save_list_of_cameras(een=een) if len(een.accounts) > 0 and een.active_account == None: # they need to pick and account logging.info(f"redirecting to accounts.html because they don't have an active account { een.active_account }") values = { "current_user": een.current_user, "accounts": een.accounts, "active_account": een.active_account, "user_base_url": een.user_base_url, "access_token": een.access_token } redirect(url_for('accounts')) values = { "current_user": een.current_user, "cameras": een.cameras, "camera_count": len(een.cameras), "camera_count_online": len([i for i in een.cameras if i.is_online()]), "camera_count_offline": len([i for i in een.cameras if i.is_offline()]), "accounts": een.accounts, "active_account": een.active_account, "user_base_url": een.user_base_url, "access_token": een.access_token } return render_template('index.html', template_values=values) @app.route('/login_callback') def login_callback(): # This is getting the ?code= querystring value from the HTTP request. code = request.args.get('code') # create a new een object and store it in their session een = EagleEyev3(config) session['een'] = een if (code): # use the include code parameter to complete login process oauth_object = een.login_tokens(code) logging.debug(oauth_object) # let's try resaving this to see if it fixs the missing access_token on first request after login session['een'] = een een.get_current_user() try: user = User.query.filter(User.email == een.current_user['email']).first() if user is None: logging.debug(f"no user found with email {een.current_user['email']}, creating new user") user = User(account_id=een.current_user['accountId'],\ user_id=een.current_user['id'],\ email=een.current_user['email'],\ timezone=een.current_user['timeZone']['timeZone'],\ access_token=een.access_token,\ refresh_token=een.refresh_token,\ last_login=datetime.now()) else: logging.debug(f"found user with {een.current_user['email']}, updating") user.last_login = datetime.now() user.access_token = een.access_token user.refresh_token = een.refresh_token logging.debug(f"saving user object") user.save() except Exception as e: logging.warn(e) return redirect(url_for('index')) @app.route('/logout') def logout(): if 'een' in session: een = session['een'] logging.debug(f"calling logout { een.access_token = }") een.logout() session.pop('een') return redirect(url_for('index')) def save_list_of_cameras(een=None): user = User.query.filter(User.email == een.current_user['email']).first() if user: user_id = user.id else: user_id = None for c in een.cameras: try: check_camera = Camera.query.filter(Camera.device_id == c.id).first() if check_camera is None: logging.debug(f"no camera found for {c.id}, creating new camera") check_camera = Camera(device_id=c.id,\ timezone=None, \ name=c.name, user_id=user_id) else: logging.debug(f"found camera for {c.id}") logging.debug(f"saving camera object") check_camera.save() except Exception as e: logging.warn(e) @app.route('/cameras') @login_required def cameras(): # get een instance from session, @login_required already checked for it een = session['een'] logging.debug(een.get_list_of_cameras()) save_list_of_cameras(een=een) values = { "current_user": een.current_user, "cameras": een.cameras, "camera_count": len(een.cameras), "camera_count_online": len([i for i in een.cameras if i.is_online()]), "camera_count_offline": len([i for i in een.cameras if i.is_offline()]) } return render_template('cameras_partial.html', template_values=values) @app.route('/accounts') @login_required def accounts(): # get een instance from session, @login_required already checked for it een = session['een'] logging.debug(een.get_list_of_accounts()) values = { "current_user": een.current_user, "accounts": een.accounts, "active_account": een.active_account } return render_template('accounts_partial.html', template_values=values) @app.route('/switch_account') @login_required def switch_account(): # This is getting the ?account= querystring value from the HTTP request. account = request.args.get('account') # get een instance from session, @login_required already checked for it een = session['een'] if (account): # switch into account logging.info(f"attempting to switch into account {account}") logging.debug(een.login_from_reseller(target_account_id=account)) return redirect(url_for('index')) @app.route('/camera//preview_image') @login_required def camera__preivew_image(esn=None): # get een instance from session, @login_required already checked for it een = session['een'] camera = een.get_camera_by_id(esn) res = camera.get_live_preview() if res: return send_file(BytesIO(res.content), mimetype='image/jpeg') else: return send_file('static/placeholder.png') @app.route('/camera//preview') @login_required def camera_live_preivew(esn=None): # get een instance from session, @login_required already checked for it een = session['een'] camera = een.get_camera_by_id(esn) values = { "current_user": een.current_user, "camera": camera, "events": camera.events['status'] } return render_template('camera_preview.html', template_values=values) @app.route('/camera//video_player/') @login_required def camera_video_player(esn=None, start_timestamp=None): # get een instance from session, @login_required already checked for it een = session['een'] camera = een.get_camera_by_id(esn) try: video_url = next(x['mp4Url'] for x in camera.videos if x['startTimestamp'] == start_timestamp) except StopIteration: logging.warn(f"couldn't find video with { start_timestamp } for camera { camera.id }") values = { "current_user": een.current_user, "camera_name": camera.name, "start_timestamp": start_timestamp, "video_url": video_url } return render_template('camera_video_player.html', template_values=values) @app.route('/camera//videos') @login_required def camera_list_of_videos(esn=None): # get een instance from session, @login_required already checked for it een = session['een'] camera = een.get_camera_by_id(esn) logging.debug(camera.get_list_of_videos(start_timestamp=een.time_before(hours=24 * DAYS_OF_HISTORY), end_timestamp=een.time_now())) db_camera = Camera.query.filter(Camera.device_id == esn).first() if db_camera: for v in camera.videos: try: check_video = Download.query.filter(Download.camera_id == db_camera.id)\ .filter(Download.start_timestamp == v['startTimestamp'])\ .first() if check_video is None: new_video = Download(url=v['mp4Url'],\ camera_id=db_camera.id,\ start_timestamp=datetime.fromisoformat(v['startTimestamp']),\ end_timestamp=datetime.fromisoformat(v['endTimestamp']),\ status='new', error=None) new_video.save() except Exception as e: logging.warn(f"saving videos to db: {e}") values = { "current_user": een.current_user, "camera": { 'id': camera.id, 'name': camera.name }, "videos": camera.videos } if 'HX-Request' in request.headers: return render_template('camera_videos_partial.html', template_values=values) else: return values if __name__ == '__main__': app.run(host=config['server_host'], port=config['server_port'])