BotZ/api/rest.py

163 lines
5.4 KiB
Python
Raw Normal View History

2019-07-30 22:52:44 +02:00
# -*- encoding: utf-8 -*-
"""
The REST endpoints.
"""
from aiohttp import web
2019-08-01 16:17:14 +02:00
import datetime
2019-07-30 22:52:44 +02:00
import logging
import os
import pkg_resources
2019-08-01 00:29:28 +02:00
import typing as T
2019-07-30 22:52:44 +02:00
2019-08-01 16:17:47 +02:00
from aiohttp_session import get_session, Session
2019-08-01 00:29:28 +02:00
from bot_z.async_operator import AsyncOperator
2019-08-01 16:17:14 +02:00
from api.async_bot import login, logout, checkin, checkout, status
from api import BASE_URI, DEBUG
2019-07-30 22:52:44 +02:00
alog = logging.getLogger("api")
routes = web.RouteTableDef()
2019-08-01 16:12:55 +02:00
OPERATORS = {} # type: T.Dict[T.Text, AsyncOperator]
2019-08-08 15:29:45 +02:00
BASE_PATH = pkg_resources.resource_filename(__name__, "assets")
2019-08-01 00:29:28 +02:00
async def get_set_operator(
request: web.Request, user: T.Text, password: T.Text
2019-08-01 16:12:55 +02:00
) -> T.Tuple[AsyncOperator, Session]:
2019-08-01 00:29:28 +02:00
session = await get_session(request)
op = None
2019-08-01 00:29:28 +02:00
if "async_operator" in session:
op = OPERATORS.get(session["async_operator"])
base_uri = request.app["base_uri"]
debug = request.app["debug"]
headless = request.app["headless"]
if op is None:
op = AsyncOperator(base_uri, name=user, headless=headless, debug=debug)
2019-08-01 00:29:28 +02:00
session["async_operator"] = user
OPERATORS[user] = op
return op, session
2019-07-30 22:52:44 +02:00
def add_static_routes(log: logging.Logger) -> None:
static_assets = [
os.path.abspath(os.path.join(BASE_PATH, path))
for path in os.listdir(BASE_PATH)
if os.path.isdir(os.path.join(BASE_PATH, path))
]
for asset in static_assets:
asset_path = os.path.relpath(asset, BASE_PATH)
log.debug(f"Linking: {asset_path} -> {asset}")
routes.static(f"/{asset_path}", asset)
@routes.get("/")
async def home_handle(request: web.Request) -> web.Response:
return web.FileResponse(os.path.join(BASE_PATH, "index.html"))
@routes.get("/favicon.ico")
async def favicon_handle(request: web.Request) -> web.Response:
return web.FileResponse(os.path.join(BASE_PATH, "favicon.ico"))
@routes.get("/api/login")
@routes.get("/api/badge")
2019-08-08 17:15:51 +02:00
@routes.get("/api/status")
async def routing_handler(request: web.Request) -> web.Response:
alog.debug("(%s) %s", request.method, request.path)
session = await get_session(request)
op = session.get("async_operator")
_logged_in = True if op else False
alog.info("%s - is%s in session", request.path, " NOT" if not _logged_in else "")
return web.json_response({"logged_in": _logged_in})
@routes.post("/api/login")
2019-07-30 22:52:44 +02:00
async def login_handler(request: web.Request) -> web.Response:
data = await request.json()
2019-07-30 22:52:44 +02:00
user = data.get("username")
password = data.get("password")
if not user or not password:
alog.debug("login - missing username or password: %s", data)
return web.json_response({"error": "Missing username or password"}, status=403)
2019-08-01 00:29:28 +02:00
op, session = await get_set_operator(request, user, password)
2019-07-30 22:52:44 +02:00
alog.debug("login - user: %s, password: %s", user, password)
res = await login(op, user, password)
alog.debug("login result: %s", res)
2019-08-01 00:29:28 +02:00
if not res:
session.invalidate()
alog.info("Login failed; session invalidated.")
2019-07-30 22:52:44 +02:00
return web.json_response({"logged_in": res}, status=200)
@routes.post("/api/logout")
2019-07-30 22:52:44 +02:00
async def logout_handler(request: web.Request) -> web.Response:
alog.debug("logout")
2019-08-01 00:29:28 +02:00
session = await get_session(request)
op_key = session.get("async_operator")
op = OPERATORS.get(op_key)
2019-08-01 00:29:28 +02:00
if not op:
return web.json_response(
{"error": "No session", "logged_in": False}, status=403
)
2019-07-30 22:52:44 +02:00
res = await logout(op)
alog.debug("logout result: %s", res)
2019-08-01 16:17:47 +02:00
# FIX: assess if better to invalidate session and dump the browser instance.
del OPERATORS[op_key]
2019-07-30 22:52:44 +02:00
return web.json_response({"logged_in": res}, status=200)
2019-08-01 16:17:47 +02:00
@routes.post("/api/checkin")
2019-08-01 16:17:47 +02:00
async def checkin_handler(request: web.Request) -> web.Response:
alog.debug("checkin")
session = await get_session(request)
op = OPERATORS.get(session.get("async_operator"))
if not op:
return web.json_response(
{"error": "No session", "logged_in": False}, status=403
)
2019-08-01 16:17:47 +02:00
res = await checkin(op)
alog.debug("checkin result: %s", res)
return web.json_response({"checked_in": res, "logged_in": True}, status=200)
2019-08-01 16:17:47 +02:00
@routes.post("/api/checkout")
2019-08-01 16:17:47 +02:00
async def checkout_handler(request: web.Request) -> web.Response:
alog.debug("checkout")
session = await get_session(request)
op = OPERATORS.get(session.get("async_operator"))
if not op:
return web.json_response(
{"error": "No session", "logged_in": False}, status=403
)
2019-08-01 16:17:47 +02:00
res = await checkout(op)
alog.debug("checkout result: %s", res)
return web.json_response({"checked_in": res, "logged_in": True}, status=200)
2019-08-01 16:17:47 +02:00
@routes.get("/api/movements")
2019-08-01 16:17:14 +02:00
async def movements_handle(request: web.Request) -> web.Response:
alog.debug("movements")
session = await get_session(request)
op = OPERATORS.get(session.get("async_operator"))
if not op:
return web.json_response(
{"error": "No session", "logged_in": False}, status=403
)
2019-08-01 16:17:14 +02:00
res = await status(op)
alog.debug("movements result: %s", res)
if not res:
return web.json_response(
{"error": "No movements found", "logged_in": True}, status=404
)
movements = []
for r in res:
if r and len(r) == 2:
movements.append({"time": r[1], "type": r[0]})
resp_data = {"movements": movements}
resp_data["logged_in"] = True # type: ignore
2019-08-01 16:17:14 +02:00
return web.json_response(resp_data, status=200)