-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapp.py
More file actions
130 lines (107 loc) · 4.65 KB
/
Copy pathapp.py
File metadata and controls
130 lines (107 loc) · 4.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import os
import logging
from flask import Flask, request, jsonify, send_from_directory
from flask_cors import CORS
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
from dotenv import load_dotenv
from internship_agent import scrape_website_text, generate_internship_content
load_dotenv()
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
app = Flask(__name__, static_folder="frontend/dist", static_url_path="/")
app.config["MAX_CONTENT_LENGTH"] = 100 * 1024 # 100KB max request body
allowed_origins = [origin.strip() for origin in os.getenv("ALLOWED_ORIGINS", "").split(",") if origin.strip()]
if not allowed_origins:
allowed_origins = [
"http://localhost:5173",
"http://127.0.0.1:5173",
"https://aswin-m-kumar.github.io",
]
CORS(
app,
resources={r"/api/*": {"origins": allowed_origins}},
methods=["POST", "OPTIONS"],
allow_headers=["Content-Type"],
)
limiter = Limiter(
get_remote_address,
app=app,
default_limits=["20 per hour"],
storage_uri=os.getenv("RATELIMIT_STORAGE_URI", "memory://")
)
MAX_RAW_TEXT_LENGTH = 20000
MAX_URL_LENGTH = 2048
@app.after_request
def set_security_headers(response):
response.headers["X-Content-Type-Options"] = "nosniff"
response.headers["X-Frame-Options"] = "DENY"
response.headers["Referrer-Policy"] = "no-referrer"
response.headers["Permissions-Policy"] = "camera=(), microphone=(), geolocation=()"
if request.path.startswith("/api/"):
response.headers["Cache-Control"] = "no-store"
return response
@app.route("/")
def index():
return send_from_directory(app.static_folder, "index.html")
@app.errorhandler(413)
def payload_too_large(_):
return jsonify({"success": False, "error": "Payload too large."}), 413
@app.route("/api/generate", methods=["POST"])
@limiter.limit("5 per minute")
def generate():
if not request.is_json:
return jsonify({"success": False, "error": "Request must be JSON."}), 415
data = request.get_json(silent=True)
if not isinstance(data, dict):
return jsonify({"success": False, "error": "Invalid JSON payload."}), 400
url = data.get("url")
user_raw_text = data.get("raw_text") # Can be effectively sent to bypass scraping if website blocks it
api_key = os.getenv("NVIDIA_API_KEY")
if url is not None and not isinstance(url, str):
return jsonify({"success": False, "error": "URL must be a string."}), 400
if user_raw_text is not None and not isinstance(user_raw_text, str):
return jsonify({"success": False, "error": "raw_text must be a string."}), 400
if url and len(url) > MAX_URL_LENGTH:
return jsonify({"success": False, "error": "URL is too long."}), 400
if user_raw_text and len(user_raw_text) > MAX_RAW_TEXT_LENGTH:
return jsonify({"success": False, "error": "raw_text exceeds maximum allowed length."}), 400
if not url and not user_raw_text:
return jsonify({"success": False, "error": "URL or Raw Text is required"}), 400
if not api_key:
logger.error("NVIDIA_API_KEY is missing.")
return jsonify({"success": False, "error": "Backend configuration error."}), 500
try:
# 1. Provide raw_text directly if user bypassed scraping, otherwise scrape the URL
if user_raw_text and user_raw_text.strip():
raw_text = user_raw_text
else:
raw_text = scrape_website_text(url)
# 2. Complete Generation passing url or a fallback "No URL Provided"
content = generate_internship_content(raw_text, url or "No URL Provided", api_key)
# Optionally, we can attempt to parse the content into Poster and WhatsApp Caption
# We will let the frontend handle the display. It might be easier to just split by "TASK 2: WHATSAPP CAPTION" or similar if we want.
# But for now, we just pass the full content and the frontend can parse it.
return jsonify({
"success": True,
"content": content
})
except ValueError as e:
# ValueError is raised by our own validation (SSRF, bad URL, etc.)
# Safe to show to the client as we control the message.
return jsonify({
"success": False,
"error": str(e)
}), 400
except Exception as e:
logger.exception("Unhandled error during content generation")
return jsonify({
"success": False,
"error": "An internal server error occurred. Please try again later."
}), 500
if __name__ == "__main__":
app.run(debug=False)