diff --git a/.env b/.env new file mode 100644 index 0000000..99139f8 --- /dev/null +++ b/.env @@ -0,0 +1 @@ +DB_PASS=y8Khu7pJQZq6ywFDIJiqpx4zYmclHGHw diff --git a/Dockerfile b/Dockerfile index b0dde83..444d8b2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,4 +7,4 @@ RUN pip install --no-cache-dir -r requirements.txt COPY . . -CMD ["python", "api/main.py"] +CMD ["python", "-m", "api.main"] diff --git a/api/routes/example.py b/api/routes/example.py index dd896c2..1f1ff2b 100644 --- a/api/routes/example.py +++ b/api/routes/example.py @@ -7,16 +7,22 @@ This module demonstrates: 3. Making database calls via postgres module """ -import flask -import sys import os - -sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - +import flask +import jwt import core.auth as auth import core.postgres as postgres +def _get_user_uuid(token): + """Decode JWT to extract user UUID. Returns None on failure.""" + try: + payload = jwt.decode(token, os.getenv("JWT_SECRET"), algorithms=["HS256"]) + return payload.get("sub") + except (jwt.ExpiredSignatureError, jwt.InvalidTokenError): + return None + + def register(app): """Register routes with the Flask app.""" @@ -27,8 +33,8 @@ def register(app): return flask.jsonify({"error": "missing token"}), 401 token = header[7:] - decoded = auth.verifyLoginToken(token, userUUID=True) - if decoded != True: + user_uuid = _get_user_uuid(token) + if not user_uuid or not auth.verifyLoginToken(token, userUUID=user_uuid): return flask.jsonify({"error": "unauthorized"}), 401 items = postgres.select("examples") @@ -41,8 +47,8 @@ def register(app): return flask.jsonify({"error": "missing token"}), 401 token = header[7:] - decoded = auth.verifyLoginToken(token, userUUID=True) - if decoded != True: + user_uuid = _get_user_uuid(token) + if not user_uuid or not auth.verifyLoginToken(token, userUUID=user_uuid): return flask.jsonify({"error": "unauthorized"}), 401 data = flask.request.get_json() diff --git a/bot/bot.py b/bot/bot.py index 5ef6dab..c7a7502 100644 --- a/bot/bot.py +++ b/bot/bot.py @@ -18,8 +18,6 @@ import requests import bcrypt import pickle -sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - from bot.command_registry import get_handler, list_registered import ai.parser as ai_parser diff --git a/bot/commands/example.py b/bot/commands/example.py index 87e20c0..b3cad7b 100644 --- a/bot/commands/example.py +++ b/bot/commands/example.py @@ -7,11 +7,6 @@ This module demonstrates: 3. Making API calls """ -import sys -import os - -sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - from bot.command_registry import register_module import ai.parser as ai_parser diff --git a/config/.env b/config/.env new file mode 100644 index 0000000..c203d3d --- /dev/null +++ b/config/.env @@ -0,0 +1,11 @@ +DISCORD_BOT_TOKEN=MTQ2NzYwMTc2ODM0NjE2MTE3Mw.G7BKQ-.kivCRj7mOl6aS5VyX4RW9hirqzm7qJ8nJOVMpE +API_URL=http://app:5000 +DB_HOST=db +DB_PORT=5432 +DB_NAME=app +DB_USER=app +DB_PASS=y8Khu7pJQZq6ywFDIJiqpx4zYmclHGHw +JWT_SECRET=bf773b4562221bef4d304ae5752a68931382ea3e98fe38394a098f73e0c776e1 +OPENROUTER_API_KEY=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 +OPENROUTER_BASE_URL=https://openrouter.ai/api/v1 +AI_CONFIG_PATH=/app/ai/ai_config.json diff --git a/core/auth.py b/core/auth.py index 60cef1f..3f40bae 100644 --- a/core/auth.py +++ b/core/auth.py @@ -1,5 +1,5 @@ -import users -import postgres +import core.users as users +import core.postgres as postgres import bcrypt import jwt from jwt.exceptions import ExpiredSignatureError, InvalidTokenError @@ -19,9 +19,9 @@ def verifyLoginToken(login_token, username=False, userUUID=False): if decoded_token.get("sub") == str(userUUID): return True return False - except: - return 401 - return 401 + except (ExpiredSignatureError, InvalidTokenError): + return False + return False def getUserpasswordHash(userUUID): diff --git a/core/notifications.py b/core/notifications.py index e6dc581..91d0ede 100644 --- a/core/notifications.py +++ b/core/notifications.py @@ -4,7 +4,7 @@ notifications.py - Multi-channel notification routing Supported channels: Discord webhook, ntfy """ -import postgres +import core.postgres as postgres import uuid import requests import time diff --git a/core/users.py b/core/users.py index 6032445..e267fa2 100644 --- a/core/users.py +++ b/core/users.py @@ -1,5 +1,5 @@ import uuid -import postgres +import core.postgres as postgres import bcrypt diff --git a/docker-compose.yml b/docker-compose.yml index 6a96754..c2552c7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -19,7 +19,7 @@ services: app: build: . ports: - - "5000:5000" + - "8080:5000" env_file: config/.env depends_on: db: @@ -27,7 +27,7 @@ services: scheduler: build: . - command: ["python", "scheduler/daemon.py"] + command: ["python", "-m", "scheduler.daemon"] env_file: config/.env depends_on: db: @@ -35,7 +35,7 @@ services: bot: build: . - command: ["python", "bot/bot.py"] + command: ["python", "-m", "bot.bot"] env_file: config/.env depends_on: app: diff --git a/scheduler/daemon.py b/scheduler/daemon.py index 6644e5f..388811a 100644 --- a/scheduler/daemon.py +++ b/scheduler/daemon.py @@ -4,6 +4,7 @@ daemon.py - Background polling loop for scheduled tasks Override poll_callback() with your domain-specific logic. """ +import os import time import logging @@ -32,6 +33,4 @@ def daemon_loop(): if __name__ == "__main__": - import os - daemon_loop()