earncoding commited on
Commit
5d17348
ยท
verified ยท
1 Parent(s): 49b3c10

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +62 -59
main.py CHANGED
@@ -1,6 +1,7 @@
1
  import os
2
  import secrets
3
  import dns.resolver
 
4
  from fastapi import FastAPI, HTTPException, Security, Depends
5
  from fastapi.security.api_key import APIKeyHeader
6
  from fastapi.middleware.cors import CORSMiddleware
@@ -14,35 +15,61 @@ SUPABASE_URL = os.getenv("SUPABASE_URL")
14
  SUPABASE_KEY = os.getenv("SUPABASE_KEY")
15
  ADMIN_SECRET = os.getenv("ADMIN_SECRET")
16
 
17
- # Check credentials immediately
18
- if not SUPABASE_URL or not SUPABASE_KEY or not ADMIN_SECRET:
19
- # Fallback for build phase, but will error at runtime if missing
20
- print("WARNING: Secrets are missing. Check HF Settings.")
21
 
22
- # Initialize Supabase
23
  try:
24
  supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY)
25
  except:
26
  supabase = None
27
 
28
- # Security Header
29
  API_KEY_NAME = "X-API-Key"
30
  api_key_header = APIKeyHeader(name=API_KEY_NAME, auto_error=False)
31
 
32
- # Domain Lists
33
- DISPOSABLE_DOMAINS = {"tempmail.com", "10minutemail.com", "guerrillamail.com", "yopmail.com", "sharklasers.com"}
34
- FREE_PROVIDERS = {"gmail.com", "yahoo.com", "outlook.com", "hotmail.com", "icloud.com", "protonmail.com"}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
 
36
  # --- APP SETUP ---
37
  app = FastAPI(title="Secure Email Validator")
38
 
39
- # 1. FIX: ENABLE CORS (Allows your Frontend to talk to Backend)
40
  app.add_middleware(
41
  CORSMiddleware,
42
- allow_origins=["*"], # Allows all origins
43
  allow_credentials=True,
44
- allow_methods=["*"], # Allows all methods
45
- allow_headers=["*"], # Allows all headers
46
  )
47
 
48
  # --- MODELS ---
@@ -55,20 +82,16 @@ class EmailRequest(BaseModel):
55
 
56
  # --- HELPER FUNCTIONS ---
57
  def attempt_transaction(api_key: str):
58
- """Deduct 1 credit using Supabase secure function."""
59
  if api_key == ADMIN_SECRET:
60
- return True # Admin bypass
61
-
62
  try:
63
- # Call the SQL function we created earlier
64
  response = supabase.rpc("deduct_credit", {"check_key": api_key}).execute()
65
- return response.data # Returns True or False
66
  except Exception as e:
67
  print(f"DB Error: {e}")
68
  return False
69
 
70
- # --- FRONTEND DASHBOARD (Embedded HTML) ---
71
- # This ensures you don't need a separate index.html file
72
  html_content = """
73
  <!DOCTYPE html>
74
  <html lang="en">
@@ -92,13 +115,10 @@ html_content = """
92
  <body>
93
  <div class="container">
94
  <h1>๐Ÿ“ง Validator Control Panel</h1>
95
-
96
- <!-- ADMIN SECTION -->
97
  <div class="section">
98
  <h3>๐Ÿ”‘ Admin: Create API Key</h3>
99
  <label>Admin Secret</label>
100
  <input type="password" id="adminSecret" placeholder="Enter your ADMIN_SECRET">
101
-
102
  <div style="display: flex; gap: 10px;">
103
  <div style="flex: 1;"><label>Client Name</label><input type="text" id="clientName"></div>
104
  <div style="flex: 1;"><label>Credits</label><input type="number" id="credits" value="1000"></div>
@@ -106,8 +126,6 @@ html_content = """
106
  <button onclick="createKey()">Generate Key</button>
107
  <pre id="adminOutput">Waiting...</pre>
108
  </div>
109
-
110
- <!-- USER SECTION -->
111
  <div class="section">
112
  <h3>๐Ÿš€ User: Test API</h3>
113
  <label>API Key</label>
@@ -118,18 +136,14 @@ html_content = """
118
  <pre id="userOutput">Waiting...</pre>
119
  </div>
120
  </div>
121
-
122
  <script>
123
  async function createKey() {
124
  const secret = document.getElementById('adminSecret').value;
125
  const name = document.getElementById('clientName').value;
126
  const credits = document.getElementById('credits').value;
127
  const out = document.getElementById('adminOutput');
128
-
129
  if(!secret) return alert("Enter Admin Secret!");
130
-
131
  out.innerText = "Processing...";
132
-
133
  try {
134
  const res = await fetch('/admin/create-key', {
135
  method: 'POST',
@@ -141,16 +155,12 @@ html_content = """
141
  if(data.api_key) document.getElementById('apiKey').value = data.api_key;
142
  } catch(e) { out.innerText = "Error: " + e; }
143
  }
144
-
145
  async function testEmail() {
146
  const key = document.getElementById('apiKey').value;
147
  const email = document.getElementById('testEmail').value;
148
  const out = document.getElementById('userOutput');
149
-
150
  if(!key) return alert("Enter API Key!");
151
-
152
  out.innerText = "Checking...";
153
-
154
  try {
155
  const res = await fetch('/validate', {
156
  method: 'POST',
@@ -166,62 +176,48 @@ html_content = """
166
  </html>
167
  """
168
 
169
- # --- API ROUTES ---
170
-
171
  @app.get("/", response_class=HTMLResponse)
172
  async def home():
173
- """Serves the Dashboard UI"""
174
  return html_content
175
 
176
  @app.post("/admin/create-key")
177
  async def create_user(request: CreateUserRequest, x_api_key: str = Security(api_key_header)):
178
- """Admin endpoint to generate keys."""
179
- # Debugging: Print to logs if authentication fails
180
  if x_api_key != ADMIN_SECRET:
181
- print(f"Auth Failed. Provided: {x_api_key}, Expected: {ADMIN_SECRET}")
182
  raise HTTPException(status_code=403, detail="Invalid Admin Secret")
183
-
184
  new_key = secrets.token_urlsafe(24)
185
- data = {
186
- "client_name": request.client_name,
187
- "api_key": new_key,
188
- "credits": request.credits
189
- }
190
-
191
  try:
192
  supabase.table("api_users").insert(data).execute()
193
  return {"status": "success", "api_key": new_key, "credits": request.credits}
194
  except Exception as e:
195
- print(f"Supabase Error: {str(e)}")
196
- raise HTTPException(status_code=500, detail=f"Database Error: {str(e)}")
197
 
198
  @app.post("/validate")
199
  async def validate_email_endpoint(request: EmailRequest, x_api_key: str = Security(api_key_header)):
200
- """The main validation logic."""
201
  if not x_api_key:
202
  raise HTTPException(status_code=403, detail="Missing API Key")
203
-
204
- # 1. Deduct Credit
205
  success = attempt_transaction(x_api_key)
206
  if not success:
207
  raise HTTPException(status_code=402, detail="Invalid Key or No Credits")
208
 
209
- # 2. Validate
210
  email = request.email.lower().strip()
211
  result = {"email": email, "is_valid": False, "score": 0.0, "reason": "Unknown", "details": {}}
212
 
213
  try:
214
- # Syntax Check
215
  v = validate_email(email, check_deliverability=False)
216
  domain = v.domain
217
 
218
- # Disposable Check
 
219
  if domain in DISPOSABLE_DOMAINS:
220
- result["reason"] = "Disposable Domain"
221
  result["details"]["disposable"] = True
 
222
  return result
223
 
224
- # MX Check
225
  try:
226
  dns.resolver.resolve(domain, 'MX')
227
  mx_found = True
@@ -235,10 +231,17 @@ async def validate_email_endpoint(request: EmailRequest, x_api_key: str = Securi
235
  result["score"] = 0.1
236
  return result
237
 
238
- # Success
239
  result["is_valid"] = True
240
- result["score"] = 0.8 if domain in FREE_PROVIDERS else 1.0
241
- result["type"] = "personal" if domain in FREE_PROVIDERS else "business"
 
 
 
 
 
 
 
242
  result["reason"] = "Valid"
243
 
244
  except EmailNotValidError as e:
 
1
  import os
2
  import secrets
3
  import dns.resolver
4
+ import requests
5
  from fastapi import FastAPI, HTTPException, Security, Depends
6
  from fastapi.security.api_key import APIKeyHeader
7
  from fastapi.middleware.cors import CORSMiddleware
 
15
  SUPABASE_KEY = os.getenv("SUPABASE_KEY")
16
  ADMIN_SECRET = os.getenv("ADMIN_SECRET")
17
 
18
+ if not SUPABASE_URL or not SUPABASE_KEY:
19
+ print("WARNING: Supabase Credentials missing.")
 
 
20
 
 
21
  try:
22
  supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY)
23
  except:
24
  supabase = None
25
 
26
+ # Security
27
  API_KEY_NAME = "X-API-Key"
28
  api_key_header = APIKeyHeader(name=API_KEY_NAME, auto_error=False)
29
 
30
+ # --- DATA LOADING (The Fix) ---
31
+ # 1. Start with a set of known free providers
32
+ FREE_PROVIDERS = {"gmail.com", "yahoo.com", "outlook.com", "hotmail.com", "icloud.com", "protonmail.com", "aol.com", "zoho.com"}
33
+
34
+ # 2. Initialize Disposable Domains with some manual overrides
35
+ DISPOSABLE_DOMAINS = {"naqulu.com", "temp-mail.org", "10minutemail.com"}
36
+
37
+ def load_blocklist():
38
+ """
39
+ Downloads 50,000+ fake domains from a community GitHub list.
40
+ """
41
+ print("โณ Downloading Fake Domain Database...")
42
+ try:
43
+ # This is the industry standard open-source list
44
+ url = "https://raw.githubusercontent.com/disposable-email-domains/disposable-email-domains/master/disposable_email_blocklist.conf"
45
+ response = requests.get(url, timeout=10)
46
+
47
+ if response.status_code == 200:
48
+ count = 0
49
+ for line in response.text.splitlines():
50
+ domain = line.strip().lower()
51
+ if domain:
52
+ DISPOSABLE_DOMAINS.add(domain)
53
+ count += 1
54
+ print(f"โœ… Database Loaded: {count} fake domains active.")
55
+ else:
56
+ print("โš ๏ธ Failed to download list. Using basic list.")
57
+
58
+ except Exception as e:
59
+ print(f"โš ๏ธ Error loading blocklist: {e}")
60
+
61
+ # Load data immediately on startup
62
+ load_blocklist()
63
 
64
  # --- APP SETUP ---
65
  app = FastAPI(title="Secure Email Validator")
66
 
 
67
  app.add_middleware(
68
  CORSMiddleware,
69
+ allow_origins=["*"],
70
  allow_credentials=True,
71
+ allow_methods=["*"],
72
+ allow_headers=["*"],
73
  )
74
 
75
  # --- MODELS ---
 
82
 
83
  # --- HELPER FUNCTIONS ---
84
  def attempt_transaction(api_key: str):
 
85
  if api_key == ADMIN_SECRET:
86
+ return True
 
87
  try:
 
88
  response = supabase.rpc("deduct_credit", {"check_key": api_key}).execute()
89
+ return response.data
90
  except Exception as e:
91
  print(f"DB Error: {e}")
92
  return False
93
 
94
+ # --- UI ---
 
95
  html_content = """
96
  <!DOCTYPE html>
97
  <html lang="en">
 
115
  <body>
116
  <div class="container">
117
  <h1>๐Ÿ“ง Validator Control Panel</h1>
 
 
118
  <div class="section">
119
  <h3>๐Ÿ”‘ Admin: Create API Key</h3>
120
  <label>Admin Secret</label>
121
  <input type="password" id="adminSecret" placeholder="Enter your ADMIN_SECRET">
 
122
  <div style="display: flex; gap: 10px;">
123
  <div style="flex: 1;"><label>Client Name</label><input type="text" id="clientName"></div>
124
  <div style="flex: 1;"><label>Credits</label><input type="number" id="credits" value="1000"></div>
 
126
  <button onclick="createKey()">Generate Key</button>
127
  <pre id="adminOutput">Waiting...</pre>
128
  </div>
 
 
129
  <div class="section">
130
  <h3>๐Ÿš€ User: Test API</h3>
131
  <label>API Key</label>
 
136
  <pre id="userOutput">Waiting...</pre>
137
  </div>
138
  </div>
 
139
  <script>
140
  async function createKey() {
141
  const secret = document.getElementById('adminSecret').value;
142
  const name = document.getElementById('clientName').value;
143
  const credits = document.getElementById('credits').value;
144
  const out = document.getElementById('adminOutput');
 
145
  if(!secret) return alert("Enter Admin Secret!");
 
146
  out.innerText = "Processing...";
 
147
  try {
148
  const res = await fetch('/admin/create-key', {
149
  method: 'POST',
 
155
  if(data.api_key) document.getElementById('apiKey').value = data.api_key;
156
  } catch(e) { out.innerText = "Error: " + e; }
157
  }
 
158
  async function testEmail() {
159
  const key = document.getElementById('apiKey').value;
160
  const email = document.getElementById('testEmail').value;
161
  const out = document.getElementById('userOutput');
 
162
  if(!key) return alert("Enter API Key!");
 
163
  out.innerText = "Checking...";
 
164
  try {
165
  const res = await fetch('/validate', {
166
  method: 'POST',
 
176
  </html>
177
  """
178
 
 
 
179
  @app.get("/", response_class=HTMLResponse)
180
  async def home():
 
181
  return html_content
182
 
183
  @app.post("/admin/create-key")
184
  async def create_user(request: CreateUserRequest, x_api_key: str = Security(api_key_header)):
 
 
185
  if x_api_key != ADMIN_SECRET:
 
186
  raise HTTPException(status_code=403, detail="Invalid Admin Secret")
 
187
  new_key = secrets.token_urlsafe(24)
188
+ data = {"client_name": request.client_name, "api_key": new_key, "credits": request.credits}
 
 
 
 
 
189
  try:
190
  supabase.table("api_users").insert(data).execute()
191
  return {"status": "success", "api_key": new_key, "credits": request.credits}
192
  except Exception as e:
193
+ raise HTTPException(status_code=500, detail=str(e))
 
194
 
195
  @app.post("/validate")
196
  async def validate_email_endpoint(request: EmailRequest, x_api_key: str = Security(api_key_header)):
 
197
  if not x_api_key:
198
  raise HTTPException(status_code=403, detail="Missing API Key")
199
+
 
200
  success = attempt_transaction(x_api_key)
201
  if not success:
202
  raise HTTPException(status_code=402, detail="Invalid Key or No Credits")
203
 
 
204
  email = request.email.lower().strip()
205
  result = {"email": email, "is_valid": False, "score": 0.0, "reason": "Unknown", "details": {}}
206
 
207
  try:
208
+ # 1. Syntax
209
  v = validate_email(email, check_deliverability=False)
210
  domain = v.domain
211
 
212
+ # 2. DISPOSABLE CHECK (Updated Logic)
213
+ # Check if domain is in our massive list
214
  if domain in DISPOSABLE_DOMAINS:
215
+ result["reason"] = "Disposable Domain (Blacklisted)"
216
  result["details"]["disposable"] = True
217
+ result["score"] = 0.0
218
  return result
219
 
220
+ # 3. MX Record Check
221
  try:
222
  dns.resolver.resolve(domain, 'MX')
223
  mx_found = True
 
231
  result["score"] = 0.1
232
  return result
233
 
234
+ # 4. Success Classification
235
  result["is_valid"] = True
236
+
237
+ if domain in FREE_PROVIDERS:
238
+ result["score"] = 0.8
239
+ result["type"] = "personal"
240
+ else:
241
+ # It passed the blocklist and has MX, so likely business
242
+ result["score"] = 1.0
243
+ result["type"] = "business"
244
+
245
  result["reason"] = "Valid"
246
 
247
  except EmailNotValidError as e: