Skip to content
This repository was archived by the owner on Jan 27, 2025. It is now read-only.
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "constants"]
path = constants
url = https://github.com/ps2warpgate/constants.git
19 changes: 19 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v3.2.0
hooks:
- id: trailing-whitespace
args: [--markdown-linebreak-ext=md]
- id: end-of-file-fixer
- id: check-yaml
- id: check-toml
- id: check-added-large-files
- repo: https://github.com/python-poetry/poetry
rev: ''
hooks:
- id: poetry-check
- id: poetry-lock
- id: poetry-export
args: ["-f", "requirements.txt", "-o", "requirements.txt"]
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@ Quick start: `uvicorn main:app --reload`
### Docker:
```docker
docker run -d --name warpgate-api \
-e REDIS_HOST= \
-e REDIS_PORT= \
-e REDIS_DB= \
-e REDIS_PASS= \
-e MONGODB_URL= \
-e MONGODB_DB= \
-e RABBITMQ_URL= \
-e LOG_LEVEL=INFO \
-p 8080:80 \
ghcr.io/ps2warpgate/api
Expand Down
19 changes: 4 additions & 15 deletions config/db.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import os
import redis
from motor import motor_asyncio
from dotenv import load_dotenv

from config.utils import is_docker
Expand All @@ -10,19 +10,8 @@


LOG_LEVEL = os.getenv('LOG_LEVEL') or 'INFO'
REDIS_HOST = os.getenv('REDIS_HOST') or 'localhost'
REDIS_PORT = os.getenv('REDIS_PORT') or 6379
REDIS_DB = os.getenv('REDIS_DB') or 0
REDIS_PASS = os.getenv('REDIS_PASS') or None
MONGODB_URL = os.getenv('MONGODB_URL', None)


def create_redis():
return redis.ConnectionPool(
host=REDIS_HOST,
port=REDIS_PORT,
db=REDIS_DB,
password=REDIS_PASS,
decode_responses=True
)

pool = create_redis()
async def get_mongo():
return motor_asyncio.AsyncIOMotorClient(MONGODB_URL)
22 changes: 5 additions & 17 deletions config/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,21 @@
from dotenv import load_dotenv
import logging


def is_docker() -> bool:
path = '/proc/self/cgroup'
return (
os.path.exists('/.dockerenv') or
os.path.isfile(path) and any('docker' in line for line in open(path))
os.path.exists('/.dockerenv') or
os.path.isfile(path) and any('docker' in line for line in open(path))
)


if is_docker() is False: # Use .env file for secrets if outside of a container
if is_docker() is False: # Use .env file for secrets if outside a container
load_dotenv()

LOG_LEVEL = os.getenv('LOG_LEVEL') or 'INFO'


class CustomFormatter(logging.Formatter): # Formatter

grey = "\x1b[38;20m"
Expand All @@ -38,17 +40,3 @@ def format(self, record):
dt_fmt = '%m/%d/%Y %I:%M:%S'
formatter = logging.Formatter(log_fmt, dt_fmt)
return formatter.format(record)


# log = logging.getLogger()
# log.setLevel(LOG_LEVEL)
# handler = logging.StreamHandler()
# handler.setFormatter(CustomFormatter())
# log.addHandler(handler)


log = logging.getLogger()
log.setLevel(LOG_LEVEL)
handler = logging.StreamHandler()
handler.setFormatter(CustomFormatter())
log.addHandler(handler)
1 change: 1 addition & 0 deletions constants
Submodule constants added at e8809a
49 changes: 49 additions & 0 deletions consumer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
from typing import List
from starlette.websockets import WebSocket

import asyncio
from aio_pika import connect, Message, IncomingMessage, ExchangeType


class Consumer:
def __init__(self):
self.connections: List[WebSocket] = []
self.is_ready = False

async def setup(self, rabbitmq_url: str, queue_name: str):
self.rmq_conn = await connect(
url=rabbitmq_url,
loop=asyncio.get_running_loop()
)
self.channel = await self.rmq_conn.channel()
exchange = await self.channel.declare_exchange(
name='events',
type=ExchangeType.DIRECT,
)
self.queue_name = queue_name
queue = await self.channel.declare_queue(self.queue_name)
await queue.bind(exchange=exchange, routing_key='metagame')
await queue.consume(self._send)
self.is_ready = True

async def push(self, msg: str):
await self.channel.default_exchange.publish(
Message(msg.encode("ascii")),
routing_key=self.queue_name,
)

async def connect(self, websocket: WebSocket):
await websocket.accept()
self.connections.append(websocket)

def remove(self, websocket: WebSocket):
self.connections.remove(websocket)

async def _send(self, message: IncomingMessage):
living_connections = []
while len(self.connections) > 0:
websocket = self.connections.pop()
await websocket.send_text(f"{message.body}")
await message.ack()
living_connections.append(websocket)
self.connections = living_connections
108 changes: 87 additions & 21 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
import os
import redis
import uvicorn
from fastapi import Depends, FastAPI
from fastapi import Depends, FastAPI, WebSocket, WebSocketDisconnect
from fastapi.middleware.cors import CORSMiddleware
from motor.motor_asyncio import AsyncIOMotorClient
from dotenv import load_dotenv
from enum import IntEnum

from constants.models import MetagameEvent, WorldPopulation, WorldZones
from config.utils import is_docker
from config.db import pool

from config.db import get_mongo
from consumer import Consumer

if is_docker() is False: # Use .env file for secrets
load_dotenv()


BASE_URL = os.getenv('BASE_URL') or None
LOG_LEVEL = os.getenv('LOG_LEVEL') or 'INFO'
VERSION = os.getenv('VERSION') or '1.0.0'
RABBITMQ_URL = os.getenv('RABBITMQ_URL') or None
MONGODB_DB = os.getenv('MONGODB_DB', 'warpgate')

WORLD_IDS = {
'connery': 1,
Expand All @@ -27,18 +30,23 @@
}


def get_redis():
return redis.Redis(connection_pool=pool)
class WorldIds(IntEnum):
connery = 1
miller = 10
cobalt = 13
emerald = 17
jaeger = 19
soltech = 40


app = FastAPI(
title = "Warpgate API",
version = VERSION,
license_info = {
title="Warpgate API",
version=VERSION,
license_info={
"name": "GNU General Public License v3.0",
"url": "https://www.gnu.org/licenses/gpl-3.0.html"
},
root_path = BASE_URL
root_path=BASE_URL
)

origins = [
Expand All @@ -55,22 +63,80 @@ def get_redis():
allow_headers=["*"],
)

consumer = Consumer()


@app.on_event("startup")
async def startup_event():
if not consumer.is_ready:
await consumer.setup(
rabbitmq_url=RABBITMQ_URL,
queue_name='warpgate'
)


@app.get("/")
def read_root():
return {"Hello": "World"}

@app.get("/worlds/")
def read_world(id: int, cache = Depends(get_redis)):
for i in WORLD_IDS:
if WORLD_IDS[i] == id:
world_data = cache.json().get(i)
return world_data


@app.get("/health")
def healthcheck():
return {"status": "UP"}
@app.get("/zones/")
async def get_zone_status(
world_id: WorldIds = None,
client: AsyncIOMotorClient = Depends(get_mongo)) -> WorldZones | list[WorldZones]:
db = client[MONGODB_DB]
if world_id:
response = await db.continents.find_one({'world_id': world_id}, {'_id': False})
return response
else:
cursor = db.continents.find({}, {'_id': False})
response = []
for i in await cursor.to_list(length=6):
response.append(i)
return response


@app.get("/population/")
async def get_population(
world_id: WorldIds = None,
client: AsyncIOMotorClient = Depends(get_mongo)) -> WorldPopulation | list[WorldPopulation]:
db = client[MONGODB_DB]
if world_id:
response = await db.population.find_one({'world_id': world_id}, {'_id': False})
return response
else:
cursor = db.population.find({}, {'_id': False})
response = []
for i in await cursor.to_list(length=6):
response.append(i)
return response


@app.get("/alerts/")
async def get_alerts(
world_id: WorldIds = None,
client: AsyncIOMotorClient = Depends(get_mongo)) -> list[MetagameEvent]:
db = client[MONGODB_DB]
alert_collection = db.alerts
if world_id:
cursor = alert_collection.find({'world_id': world_id}, {'_id': False})
else:
cursor = alert_collection.find({}, {'_id': False})
response = []
for i in await cursor.to_list(length=30):
response.append(i)
return response


@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
await consumer.connect(websocket)
try:
while True:
data = await websocket.receive_text()
await websocket.send_text(f"Message text was: {data}")
except WebSocketDisconnect:
consumer.remove(websocket)


if __name__ == "__main__":
Expand Down
Loading