Fix distillation bugs: imports, auth security, and run configuration
- Fix bare imports in core/ modules to use fully-qualified paths (core.users, core.postgres) - Fix scheduler/daemon.py importing os before use - Fix verifyLoginToken returning truthy 401 on failure (security: invalid tokens were passing auth checks) - Fix api/routes/example.py passing literal True as userUUID instead of decoded JWT sub - Switch all services to python -m invocation so /app is always on sys.path - Remove orphaned sys.path.insert hacks from bot.py, commands/example.py, routes/example.py - Change API port mapping from 5000 to 8080 - Add config/.env and root .env for docker-compose variable substitution Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -7,4 +7,4 @@ RUN pip install --no-cache-dir -r requirements.txt
|
|||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
CMD ["python", "api/main.py"]
|
CMD ["python", "-m", "api.main"]
|
||||||
|
|||||||
@@ -7,16 +7,22 @@ This module demonstrates:
|
|||||||
3. Making database calls via postgres module
|
3. Making database calls via postgres module
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import flask
|
|
||||||
import sys
|
|
||||||
import os
|
import os
|
||||||
|
import flask
|
||||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
import jwt
|
||||||
|
|
||||||
import core.auth as auth
|
import core.auth as auth
|
||||||
import core.postgres as postgres
|
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):
|
def register(app):
|
||||||
"""Register routes with the Flask app."""
|
"""Register routes with the Flask app."""
|
||||||
|
|
||||||
@@ -27,8 +33,8 @@ def register(app):
|
|||||||
return flask.jsonify({"error": "missing token"}), 401
|
return flask.jsonify({"error": "missing token"}), 401
|
||||||
token = header[7:]
|
token = header[7:]
|
||||||
|
|
||||||
decoded = auth.verifyLoginToken(token, userUUID=True)
|
user_uuid = _get_user_uuid(token)
|
||||||
if decoded != True:
|
if not user_uuid or not auth.verifyLoginToken(token, userUUID=user_uuid):
|
||||||
return flask.jsonify({"error": "unauthorized"}), 401
|
return flask.jsonify({"error": "unauthorized"}), 401
|
||||||
|
|
||||||
items = postgres.select("examples")
|
items = postgres.select("examples")
|
||||||
@@ -41,8 +47,8 @@ def register(app):
|
|||||||
return flask.jsonify({"error": "missing token"}), 401
|
return flask.jsonify({"error": "missing token"}), 401
|
||||||
token = header[7:]
|
token = header[7:]
|
||||||
|
|
||||||
decoded = auth.verifyLoginToken(token, userUUID=True)
|
user_uuid = _get_user_uuid(token)
|
||||||
if decoded != True:
|
if not user_uuid or not auth.verifyLoginToken(token, userUUID=user_uuid):
|
||||||
return flask.jsonify({"error": "unauthorized"}), 401
|
return flask.jsonify({"error": "unauthorized"}), 401
|
||||||
|
|
||||||
data = flask.request.get_json()
|
data = flask.request.get_json()
|
||||||
|
|||||||
@@ -18,8 +18,6 @@ import requests
|
|||||||
import bcrypt
|
import bcrypt
|
||||||
import pickle
|
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
|
from bot.command_registry import get_handler, list_registered
|
||||||
import ai.parser as ai_parser
|
import ai.parser as ai_parser
|
||||||
|
|
||||||
|
|||||||
@@ -7,11 +7,6 @@ This module demonstrates:
|
|||||||
3. Making API calls
|
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
|
from bot.command_registry import register_module
|
||||||
import ai.parser as ai_parser
|
import ai.parser as ai_parser
|
||||||
|
|
||||||
|
|||||||
11
config/.env
Normal file
11
config/.env
Normal file
@@ -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
|
||||||
10
core/auth.py
10
core/auth.py
@@ -1,5 +1,5 @@
|
|||||||
import users
|
import core.users as users
|
||||||
import postgres
|
import core.postgres as postgres
|
||||||
import bcrypt
|
import bcrypt
|
||||||
import jwt
|
import jwt
|
||||||
from jwt.exceptions import ExpiredSignatureError, InvalidTokenError
|
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):
|
if decoded_token.get("sub") == str(userUUID):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
except:
|
except (ExpiredSignatureError, InvalidTokenError):
|
||||||
return 401
|
return False
|
||||||
return 401
|
return False
|
||||||
|
|
||||||
|
|
||||||
def getUserpasswordHash(userUUID):
|
def getUserpasswordHash(userUUID):
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ notifications.py - Multi-channel notification routing
|
|||||||
Supported channels: Discord webhook, ntfy
|
Supported channels: Discord webhook, ntfy
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import postgres
|
import core.postgres as postgres
|
||||||
import uuid
|
import uuid
|
||||||
import requests
|
import requests
|
||||||
import time
|
import time
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import uuid
|
import uuid
|
||||||
import postgres
|
import core.postgres as postgres
|
||||||
import bcrypt
|
import bcrypt
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ services:
|
|||||||
app:
|
app:
|
||||||
build: .
|
build: .
|
||||||
ports:
|
ports:
|
||||||
- "5000:5000"
|
- "8080:5000"
|
||||||
env_file: config/.env
|
env_file: config/.env
|
||||||
depends_on:
|
depends_on:
|
||||||
db:
|
db:
|
||||||
@@ -27,7 +27,7 @@ services:
|
|||||||
|
|
||||||
scheduler:
|
scheduler:
|
||||||
build: .
|
build: .
|
||||||
command: ["python", "scheduler/daemon.py"]
|
command: ["python", "-m", "scheduler.daemon"]
|
||||||
env_file: config/.env
|
env_file: config/.env
|
||||||
depends_on:
|
depends_on:
|
||||||
db:
|
db:
|
||||||
@@ -35,7 +35,7 @@ services:
|
|||||||
|
|
||||||
bot:
|
bot:
|
||||||
build: .
|
build: .
|
||||||
command: ["python", "bot/bot.py"]
|
command: ["python", "-m", "bot.bot"]
|
||||||
env_file: config/.env
|
env_file: config/.env
|
||||||
depends_on:
|
depends_on:
|
||||||
app:
|
app:
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ daemon.py - Background polling loop for scheduled tasks
|
|||||||
Override poll_callback() with your domain-specific logic.
|
Override poll_callback() with your domain-specific logic.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
import time
|
import time
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
@@ -32,6 +33,4 @@ def daemon_loop():
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import os
|
|
||||||
|
|
||||||
daemon_loop()
|
daemon_loop()
|
||||||
|
|||||||
Reference in New Issue
Block a user