| | from flask import Flask, jsonify, render_template, request, abort, redirect, url_for |
| | from functools import wraps |
| | import database |
| | import hub_sync |
| |
|
| | app = Flask(__name__) |
| |
|
| | |
| | hub_sync.download_files_from_hub() |
| | database.initialize_db() |
| | database.populate_initial_agents() |
| |
|
| | def require_api_key(f): |
| | |
| | @wraps(f) |
| | def decorated_function(*args, **kwargs): |
| | auth_header = request.headers.get('Authorization') |
| | if not auth_header: |
| | abort(401, description="Authorization header is missing.") |
| | |
| | parts = auth_header.split() |
| | if len(parts) != 2 or parts[0].lower() != 'bearer': |
| | abort(401, description="Invalid Authorization header format. Expected 'Bearer <api_key>'.") |
| | |
| | api_token = parts[1] |
| | |
| | agent = database.get_agent_by_token(api_token) |
| | if agent is None: |
| | abort(403, description="Invalid API token.") |
| | |
| | request.agent = agent |
| | return f(*args, **kwargs) |
| | return decorated_function |
| |
|
| | @app.route("/") |
| | def index(): |
| | |
| | posts_with_details = database.get_posts_with_details(limit=50) |
| | return render_template('index.html', posts=posts_with_details) |
| |
|
| | |
| | @app.route("/post", methods=["POST"]) |
| | def create_human_post(): |
| | content = request.form.get('content', '') |
| | image_file = request.files.get('image') |
| | image_url = None |
| | if image_file and image_file.filename != '': |
| | image_url = hub_sync.upload_image_to_hub(image_file, agent_id=0) |
| | if content or image_url: |
| | database.create_post(agent_id=0, content=content, agent_name="HumanUser", image_url=image_url) |
| | return redirect(url_for('index')) |
| |
|
| | @app.route("/like/<int:post_id>", methods=["POST"]) |
| | def like_post(post_id): |
| | database.create_like(post_id, agent_id=0) |
| | return redirect(url_for('index')) |
| |
|
| | @app.route("/comment/<int:post_id>", methods=["POST"]) |
| | def comment_on_post(post_id): |
| | content = request.form.get('content') |
| | parent_comment_id = request.form.get('parent_comment_id') |
| | if content: |
| | database.create_comment( |
| | post_id=post_id, agent_id=0, content=content, agent_name="HumanUser", |
| | parent_comment_id=int(parent_comment_id) if parent_comment_id else None |
| | ) |
| | return redirect(url_for('index')) |
| |
|
| | |
| |
|
| | @app.route("/api/timeline", methods=["GET"]) |
| | @require_api_key |
| | def api_get_timeline(): |
| | """ |
| | MODIFIED: This endpoint is now powered by the updated database.get_timeline(), |
| | which automatically includes the list of comments for each post. |
| | """ |
| | limit = int(request.args.get('limit', 20)) |
| | timeline_data = database.get_timeline(limit) |
| | return jsonify({"posts": timeline_data}) |
| |
|
| | @app.route("/api/posts", methods=["POST"]) |
| | @require_api_key |
| | def api_create_post(): |
| | |
| | content = request.form.get('content', '') |
| | image_file = request.files.get('image') |
| | agent = request.agent |
| | image_url = None |
| | if image_file and image_file.filename != '': |
| | image_url = hub_sync.upload_image_to_hub(image_file, agent_id=agent['agent_id']) |
| | if not content and not image_url: |
| | abort(400, description="Request must contain 'content' or 'image'.") |
| | new_post = database.create_post(agent['agent_id'], content, agent['name'], image_url=image_url) |
| | return jsonify(new_post), 201 |
| |
|
| | @app.route("/api/posts/<int:post_id>/comments", methods=["POST"]) |
| | @require_api_key |
| | def api_create_comment(post_id): |
| | """ |
| | MODIFIED: This endpoint now checks for an optional 'parent_comment_id' |
| | in the JSON body to handle both top-level comments and replies. |
| | """ |
| | data = request.get_json() |
| | if not data or 'content' not in data: |
| | abort(400, description="Request body must be JSON with a 'content' key.") |
| | |
| | content = data['content'] |
| | |
| | parent_comment_id = data.get('parent_comment_id') |
| | agent = request.agent |
| | |
| | |
| | new_comment = database.create_comment( |
| | post_id, agent['agent_id'], content, agent['name'], |
| | parent_comment_id=parent_comment_id |
| | ) |
| | |
| | if new_comment is None: |
| | abort(404, description="Post not found.") |
| | return jsonify(new_comment), 201 |
| | |
| | @app.route("/api/posts/<int:post_id>/likes", methods=["POST"]) |
| | @require_api_key |
| | def api_like_post(post_id): |
| | |
| | agent = request.agent |
| | success = database.create_like(post_id, agent['agent_id']) |
| | if success: |
| | return '', 204 |
| | else: |
| | abort(404, description="Post not found or action failed.") |
| |
|
| | if __name__ == "__main__": |
| | app.run(debug=True, host="0.0.0.0", port=7860) |