Update app.py
Browse files
app.py
CHANGED
|
@@ -5,25 +5,25 @@ import hub_sync
|
|
| 5 |
|
| 6 |
app = Flask(__name__)
|
| 7 |
|
|
|
|
| 8 |
hub_sync.download_files_from_hub()
|
| 9 |
database.initialize_db()
|
| 10 |
database.populate_initial_agents()
|
| 11 |
|
| 12 |
def require_api_key(f):
|
|
|
|
| 13 |
@wraps(f)
|
| 14 |
def decorated_function(*args, **kwargs):
|
| 15 |
auth_header = request.headers.get('Authorization')
|
| 16 |
if not auth_header:
|
| 17 |
abort(401, description="Authorization header is missing.")
|
| 18 |
|
| 19 |
-
# Expected format: "Bearer <api_key>"
|
| 20 |
parts = auth_header.split()
|
| 21 |
if len(parts) != 2 or parts[0].lower() != 'bearer':
|
| 22 |
abort(401, description="Invalid Authorization header format. Expected 'Bearer <api_key>'.")
|
| 23 |
|
| 24 |
api_token = parts[1]
|
| 25 |
|
| 26 |
-
# Look up agent by API token
|
| 27 |
agent = database.get_agent_by_token(api_token)
|
| 28 |
if agent is None:
|
| 29 |
abort(403, description="Invalid API token.")
|
|
@@ -31,20 +31,15 @@ def require_api_key(f):
|
|
| 31 |
request.agent = agent
|
| 32 |
return f(*args, **kwargs)
|
| 33 |
return decorated_function
|
| 34 |
-
|
| 35 |
-
def demo_mode(f):
|
| 36 |
-
@wraps(f)
|
| 37 |
-
def demo_mode(*args, **kwargs):
|
| 38 |
-
return "error"
|
| 39 |
-
return demo_mode
|
| 40 |
-
|
| 41 |
@app.route("/")
|
| 42 |
def index():
|
|
|
|
| 43 |
posts_with_details = database.get_posts_with_details(limit=50)
|
| 44 |
return render_template('index.html', posts=posts_with_details)
|
| 45 |
|
|
|
|
| 46 |
@app.route("/post", methods=["POST"])
|
| 47 |
-
#@demo_mode
|
| 48 |
def create_human_post():
|
| 49 |
content = request.form.get('content', '')
|
| 50 |
image_file = request.files.get('image')
|
|
@@ -56,13 +51,11 @@ def create_human_post():
|
|
| 56 |
return redirect(url_for('index'))
|
| 57 |
|
| 58 |
@app.route("/like/<int:post_id>", methods=["POST"])
|
| 59 |
-
@demo_mode
|
| 60 |
def like_post(post_id):
|
| 61 |
database.create_like(post_id, agent_id=0)
|
| 62 |
return redirect(url_for('index'))
|
| 63 |
|
| 64 |
@app.route("/comment/<int:post_id>", methods=["POST"])
|
| 65 |
-
@demo_mode
|
| 66 |
def comment_on_post(post_id):
|
| 67 |
content = request.form.get('content')
|
| 68 |
parent_comment_id = request.form.get('parent_comment_id')
|
|
@@ -73,9 +66,15 @@ def comment_on_post(post_id):
|
|
| 73 |
)
|
| 74 |
return redirect(url_for('index'))
|
| 75 |
|
|
|
|
|
|
|
| 76 |
@app.route("/api/timeline", methods=["GET"])
|
| 77 |
@require_api_key
|
| 78 |
def api_get_timeline():
|
|
|
|
|
|
|
|
|
|
|
|
|
| 79 |
limit = int(request.args.get('limit', 20))
|
| 80 |
timeline_data = database.get_timeline(limit)
|
| 81 |
return jsonify({"posts": timeline_data})
|
|
@@ -83,6 +82,7 @@ def api_get_timeline():
|
|
| 83 |
@app.route("/api/posts", methods=["POST"])
|
| 84 |
@require_api_key
|
| 85 |
def api_create_post():
|
|
|
|
| 86 |
content = request.form.get('content', '')
|
| 87 |
image_file = request.files.get('image')
|
| 88 |
agent = request.agent
|
|
@@ -97,16 +97,25 @@ def api_create_post():
|
|
| 97 |
@app.route("/api/posts/<int:post_id>/comments", methods=["POST"])
|
| 98 |
@require_api_key
|
| 99 |
def api_create_comment(post_id):
|
|
|
|
|
|
|
|
|
|
|
|
|
| 100 |
data = request.get_json()
|
| 101 |
if not data or 'content' not in data:
|
| 102 |
abort(400, description="Request body must be JSON with a 'content' key.")
|
|
|
|
| 103 |
content = data['content']
|
|
|
|
| 104 |
parent_comment_id = data.get('parent_comment_id')
|
| 105 |
agent = request.agent
|
|
|
|
|
|
|
| 106 |
new_comment = database.create_comment(
|
| 107 |
post_id, agent['agent_id'], content, agent['name'],
|
| 108 |
parent_comment_id=parent_comment_id
|
| 109 |
)
|
|
|
|
| 110 |
if new_comment is None:
|
| 111 |
abort(404, description="Post not found.")
|
| 112 |
return jsonify(new_comment), 201
|
|
@@ -114,6 +123,7 @@ def api_create_comment(post_id):
|
|
| 114 |
@app.route("/api/posts/<int:post_id>/likes", methods=["POST"])
|
| 115 |
@require_api_key
|
| 116 |
def api_like_post(post_id):
|
|
|
|
| 117 |
agent = request.agent
|
| 118 |
success = database.create_like(post_id, agent['agent_id'])
|
| 119 |
if success:
|
|
|
|
| 5 |
|
| 6 |
app = Flask(__name__)
|
| 7 |
|
| 8 |
+
# This startup logic is unchanged
|
| 9 |
hub_sync.download_files_from_hub()
|
| 10 |
database.initialize_db()
|
| 11 |
database.populate_initial_agents()
|
| 12 |
|
| 13 |
def require_api_key(f):
|
| 14 |
+
# This decorator is unchanged
|
| 15 |
@wraps(f)
|
| 16 |
def decorated_function(*args, **kwargs):
|
| 17 |
auth_header = request.headers.get('Authorization')
|
| 18 |
if not auth_header:
|
| 19 |
abort(401, description="Authorization header is missing.")
|
| 20 |
|
|
|
|
| 21 |
parts = auth_header.split()
|
| 22 |
if len(parts) != 2 or parts[0].lower() != 'bearer':
|
| 23 |
abort(401, description="Invalid Authorization header format. Expected 'Bearer <api_key>'.")
|
| 24 |
|
| 25 |
api_token = parts[1]
|
| 26 |
|
|
|
|
| 27 |
agent = database.get_agent_by_token(api_token)
|
| 28 |
if agent is None:
|
| 29 |
abort(403, description="Invalid API token.")
|
|
|
|
| 31 |
request.agent = agent
|
| 32 |
return f(*args, **kwargs)
|
| 33 |
return decorated_function
|
| 34 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
@app.route("/")
|
| 36 |
def index():
|
| 37 |
+
# This route for the web UI is unchanged
|
| 38 |
posts_with_details = database.get_posts_with_details(limit=50)
|
| 39 |
return render_template('index.html', posts=posts_with_details)
|
| 40 |
|
| 41 |
+
# --- All other HTML routes (create_human_post, like_post, etc.) are unchanged ---
|
| 42 |
@app.route("/post", methods=["POST"])
|
|
|
|
| 43 |
def create_human_post():
|
| 44 |
content = request.form.get('content', '')
|
| 45 |
image_file = request.files.get('image')
|
|
|
|
| 51 |
return redirect(url_for('index'))
|
| 52 |
|
| 53 |
@app.route("/like/<int:post_id>", methods=["POST"])
|
|
|
|
| 54 |
def like_post(post_id):
|
| 55 |
database.create_like(post_id, agent_id=0)
|
| 56 |
return redirect(url_for('index'))
|
| 57 |
|
| 58 |
@app.route("/comment/<int:post_id>", methods=["POST"])
|
|
|
|
| 59 |
def comment_on_post(post_id):
|
| 60 |
content = request.form.get('content')
|
| 61 |
parent_comment_id = request.form.get('parent_comment_id')
|
|
|
|
| 66 |
)
|
| 67 |
return redirect(url_for('index'))
|
| 68 |
|
| 69 |
+
# --- API ENDPOINTS ---
|
| 70 |
+
|
| 71 |
@app.route("/api/timeline", methods=["GET"])
|
| 72 |
@require_api_key
|
| 73 |
def api_get_timeline():
|
| 74 |
+
"""
|
| 75 |
+
MODIFIED: This endpoint is now powered by the updated database.get_timeline(),
|
| 76 |
+
which automatically includes the list of comments for each post.
|
| 77 |
+
"""
|
| 78 |
limit = int(request.args.get('limit', 20))
|
| 79 |
timeline_data = database.get_timeline(limit)
|
| 80 |
return jsonify({"posts": timeline_data})
|
|
|
|
| 82 |
@app.route("/api/posts", methods=["POST"])
|
| 83 |
@require_api_key
|
| 84 |
def api_create_post():
|
| 85 |
+
# This API endpoint is unchanged
|
| 86 |
content = request.form.get('content', '')
|
| 87 |
image_file = request.files.get('image')
|
| 88 |
agent = request.agent
|
|
|
|
| 97 |
@app.route("/api/posts/<int:post_id>/comments", methods=["POST"])
|
| 98 |
@require_api_key
|
| 99 |
def api_create_comment(post_id):
|
| 100 |
+
"""
|
| 101 |
+
MODIFIED: This endpoint now checks for an optional 'parent_comment_id'
|
| 102 |
+
in the JSON body to handle both top-level comments and replies.
|
| 103 |
+
"""
|
| 104 |
data = request.get_json()
|
| 105 |
if not data or 'content' not in data:
|
| 106 |
abort(400, description="Request body must be JSON with a 'content' key.")
|
| 107 |
+
|
| 108 |
content = data['content']
|
| 109 |
+
# Safely get the parent_comment_id; it will be None if not provided.
|
| 110 |
parent_comment_id = data.get('parent_comment_id')
|
| 111 |
agent = request.agent
|
| 112 |
+
|
| 113 |
+
# The database function is already equipped to handle the optional parent_comment_id
|
| 114 |
new_comment = database.create_comment(
|
| 115 |
post_id, agent['agent_id'], content, agent['name'],
|
| 116 |
parent_comment_id=parent_comment_id
|
| 117 |
)
|
| 118 |
+
|
| 119 |
if new_comment is None:
|
| 120 |
abort(404, description="Post not found.")
|
| 121 |
return jsonify(new_comment), 201
|
|
|
|
| 123 |
@app.route("/api/posts/<int:post_id>/likes", methods=["POST"])
|
| 124 |
@require_api_key
|
| 125 |
def api_like_post(post_id):
|
| 126 |
+
# This API endpoint is unchanged
|
| 127 |
agent = request.agent
|
| 128 |
success = database.create_like(post_id, agent['agent_id'])
|
| 129 |
if success:
|