diff --git a/pushserver/api/routes/__init__.py b/pushserver/api/routes/__init__.py index 5431210..0bca928 100644 --- a/pushserver/api/routes/__init__.py +++ b/pushserver/api/routes/__init__.py @@ -1 +1 @@ -__all__ = ['api', 'home', 'push'] +__all__ = ['api', 'home', 'push', 'v2'] diff --git a/pushserver/api/routes/v2/__init__.py b/pushserver/api/routes/v2/__init__.py new file mode 100644 index 0000000..7b54194 --- /dev/null +++ b/pushserver/api/routes/v2/__init__.py @@ -0,0 +1 @@ +__all__ = ['push', 'add', 'remove'] diff --git a/pushserver/api/routes/v2/add.py b/pushserver/api/routes/v2/add.py new file mode 100644 index 0000000..ff28792 --- /dev/null +++ b/pushserver/api/routes/v2/add.py @@ -0,0 +1,66 @@ +from fastapi import APIRouter, BackgroundTasks, Request +from fastapi.responses import JSONResponse + +from pushserver.models.requests import AddRequest, fix_platform_name, AddResponse +from pushserver.resources import settings +from pushserver.resources.storage import TokenStorage +from pushserver.resources.utils import (check_host, + log_event, log_add_request) + +router = APIRouter() + + +@router.post('/{account}', response_model=AddResponse) +async def add_requests(account: str, + request: Request, + add_request: AddRequest, + background_tasks: BackgroundTasks): + + add_request.platform = fix_platform_name(add_request.platform) + + host, port = request.client.host, request.client.port + + code, description, data = '', '', {} + + if check_host(host, settings.params.allowed_pool): + request_id = f"{add_request.app_id}-{add_request.device_id}" + + if not settings.params.return_async: + background_tasks.add_task(log_add_request, task='log_request', + host=host, loggers=settings.params.loggers, + request_id=request_id, body=add_request.__dict__) + + background_tasks.add_task(log_add_request, task='log_success', + host=host, loggers=settings.params.loggers, + request_id=request_id, body=add_request.__dict__) + + storage = TokenStorage() + background_tasks.add_task(storage.add, account, add_request) + + return add_request + else: + log_add_request(task='log_request', + host=host, loggers=settings.params.loggers, + request_id=request_id, body=add_request.__dict__) + + log_add_request(task='log_success', + host=host, loggers=settings.params.loggers, + request_id=request_id, body=add_request.__dict__) + storage = TokenStorage() + storage.add(account, add_request) + return add_request + else: + msg = f'incoming request from {host} is denied' + log_event(loggers=settings.params.loggers, + msg=msg, level='deb') + code = 403 + description = 'access denied by access list' + data = {} + + if settings.params.loggers['debug']: + log_event(loggers=settings.params.loggers, + msg=msg, level='deb', to_file=True) + + return JSONResponse(status_code=code, content={'code': code, + 'description': description, + 'data': data}) diff --git a/pushserver/api/routes/v2/push.py b/pushserver/api/routes/v2/push.py new file mode 100644 index 0000000..488fa3a --- /dev/null +++ b/pushserver/api/routes/v2/push.py @@ -0,0 +1,130 @@ +import json + +from fastapi import APIRouter, BackgroundTasks, Request, status +from fastapi.responses import JSONResponse + +from fastapi.encoders import jsonable_encoder +from pydantic import ValidationError + +from pushserver.models.requests import WakeUpRequest, PushRequest +from pushserver.resources import settings +from pushserver.resources.storage import TokenStorage +from pushserver.resources.notification import handle_request +from pushserver.resources.utils import (check_host, + log_event, log_incoming_request, log_push_request) + +router = APIRouter() + + +@router.post('/{account}/push', response_model=PushRequest) +async def push_requests(account: str, + request: Request, + push_request: PushRequest, + background_tasks: BackgroundTasks): + + host, port = request.client.host, request.client.port + + code, description, data = '', '', [] + + if check_host(host, settings.params.allowed_pool): + request_id = f"{push_request.event}-{account}-{push_request.call_id}" + + if not settings.params.return_async: + background_tasks.add_task(log_push_request, task='log_request', + host=host, loggers=settings.params.loggers, + request_id=request_id, body=push_request.__dict__) + background_tasks.add_task(log_incoming_request, task='log_success', + host=host, loggers=settings.params.loggers, + request_id=request_id, body=push_request.__dict__) + background_tasks.add_task(handle_request, + wp_request=push_request, + request_id=request_id) + status_code, code = status.HTTP_202_ACCEPTED, 202 + description, data = 'accepted for delivery', {} + + try: + return JSONResponse(status_code=status_code, + content={'code': code, + 'description': description, + 'data': data}) + except json.decoder.JSONDecodeError: + return JSONResponse(status_code=status_code, + content={'code': code, + 'description': description, + 'data': {}}) + + else: + storage = TokenStorage() + storage_data = storage[account] + expired_devices = [] + + log_push_request(task='log_request', + host=host, loggers=settings.params.loggers, + request_id=request_id, body=push_request.__dict__) + if not storage_data: + description, data = 'Push request was not sent: user not found', {"account": account} + return JSONResponse(status_code=status.HTTP_404_NOT_FOUND, + content={'code': 404, + 'description': description, + 'data': data}) + + for device, push_parameters in storage_data.items(): + push_parameters.update(push_request.__dict__) + + reversed_push_parameters = {} + for item in push_parameters.keys(): + value = push_parameters[item] + if item in ('sip_to', 'sip_from'): + item = item.split('_')[1] + else: + item = item.replace('_', '-') + reversed_push_parameters[item] = value + + try: + wp = WakeUpRequest(**reversed_push_parameters) + except ValidationError as e: + error_msg = e.errors()[0]['msg'] + log_push_request(task='log_failure', host=host, + loggers=settings.params.loggers, + request_id=request_id, body=push_request.__dict__, + error_msg=error_msg) + content = jsonable_encoder({'code': 400, + 'description': error_msg, + 'data': ''}) + return JSONResponse(status_code=status.HTTP_400_BAD_REQUEST, + content=content) + + + log_incoming_request(task='log_success', + host=host, loggers=settings.params.loggers, + request_id=request_id, body=wp.__dict__) + results = handle_request(wp, request_id=request_id) + + code = results.get('code') + if code == 410: + expired_devices.append(device) + code = 200 + description = 'push notification responses' + data.append(results) + + for device in expired_devices: + msg = f'Removing {device} from {account}' + log_event(loggers=settings.params.loggers, + msg=msg, level='deb') + storage.remove(account, device) + + else: + msg = f'incoming request from {host} is denied' + log_event(loggers=settings.params.loggers, + msg=msg, level='deb') + code = 403 + description = 'access denied by access list' + data = {} + + if settings.params.loggers['debug']: + log_event(loggers=settings.params.loggers, + msg=msg, level='deb', to_file=True) + + return JSONResponse(status_code=code, content={'code': code, + 'description': description, + 'data': data}) diff --git a/pushserver/api/routes/v2/remove.py b/pushserver/api/routes/v2/remove.py new file mode 100644 index 0000000..3fa3095 --- /dev/null +++ b/pushserver/api/routes/v2/remove.py @@ -0,0 +1,90 @@ +from fastapi import APIRouter, BackgroundTasks, Request, status +from fastapi.responses import JSONResponse + +from pushserver.models.requests import RemoveRequest, RemoveResponse +from pushserver.resources import settings +from pushserver.resources.storage import TokenStorage +from pushserver.resources.utils import (check_host, + log_event, log_remove_request) + +router = APIRouter() + + +@router.delete('/{account}', response_model=RemoveResponse) +async def remove_requests(account: str, + request: Request, + rm_request: RemoveRequest, + background_tasks: BackgroundTasks): + + host, port = request.client.host, request.client.port + + code, description, data = '', '', {} + + if check_host(host, settings.params.allowed_pool): + request_id = f"{rm_request.device_id}-{rm_request.app_id}" + + if not settings.params.return_async: + background_tasks.add_task(log_remove_request, task='log_request', + host=host, loggers=settings.params.loggers, + request_id=request_id, body=rm_request.__dict__) + background_tasks.add_task(log_remove_request, task='log_success', + host=host, loggers=settings.params.loggers, + request_id=request_id, body=rm_request.__dict__) + storage = TokenStorage() + background_tasks.add_task(storage.remove, account, request_id) + + return rm_request + else: + log_remove_request(task='log_request', + host=host, loggers=settings.params.loggers, + request_id=request_id, body=rm_request.__dict__) + + storage = TokenStorage() + storage_data = storage[account] + print(storage_data) + if not storage_data: + log_remove_request(task='log_failure', + host=host, loggers=settings.params.loggers, + request_id=request_id, body=rm_request.__dict__, + error_msg="User not found in token storage") + return JSONResponse( + status_code=status.HTTP_404_NOT_FOUND, + content={'result': 'User not found'} + ) + + try: + device = storage_data[request_id] + except KeyError: + log_remove_request(task='log_failure', + host=host, loggers=settings.params.loggers, + request_id=request_id, body=rm_request.__dict__, + error_msg="Device or app_id not found in token storage") + return JSONResponse( + status_code=status.HTTP_404_NOT_FOUND, + content={'result': 'Not found'} + ) + else: + storage.remove(account, request_id) + log_remove_request(task='log_success', + host=host, loggers=settings.params.loggers, + request_id=request_id, body=rm_request.__dict__) + msg = f'Removing {device}' + log_event(loggers=settings.params.loggers, + msg=msg, level='deb') + return rm_request + + else: + msg = f'incoming request from {host} is denied' + log_event(loggers=settings.params.loggers, + msg=msg, level='deb') + code = 403 + description = 'access denied by access list' + data = {} + + if settings.params.loggers['debug']: + log_event(loggers=settings.params.loggers, + msg=msg, level='deb', to_file=True) + + return JSONResponse(status_code=code, content={'code': code, + 'description': description, + 'data': data})