This commit is contained in:
@@ -5,6 +5,7 @@ RUN apt-get update && apt-get install -y \
|
|||||||
wget \
|
wget \
|
||||||
gnupg2 \
|
gnupg2 \
|
||||||
procps \
|
procps \
|
||||||
|
sqlite3 \
|
||||||
&& wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | gpg --dearmor -o /usr/share/keyrings/google-chrome-keyring.gpg \
|
&& wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | gpg --dearmor -o /usr/share/keyrings/google-chrome-keyring.gpg \
|
||||||
&& echo "deb [arch=amd64 signed-by=/usr/share/keyrings/google-chrome-keyring.gpg] http://dl.google.com/linux/chrome/deb/ stable main" | tee /etc/apt/sources.list.d/google-chrome.list \
|
&& echo "deb [arch=amd64 signed-by=/usr/share/keyrings/google-chrome-keyring.gpg] http://dl.google.com/linux/chrome/deb/ stable main" | tee /etc/apt/sources.list.d/google-chrome.list \
|
||||||
&& apt-get update \
|
&& apt-get update \
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ import os
|
|||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
|
import sqlite3
|
||||||
|
import time
|
||||||
|
from datetime import datetime, timedelta
|
||||||
from typing import Optional, Dict, List, Any
|
from typing import Optional, Dict, List, Any
|
||||||
from urllib.parse import unquote
|
from urllib.parse import unquote
|
||||||
|
|
||||||
@@ -17,6 +20,59 @@ if not API_KEY:
|
|||||||
# Define custom user agent
|
# Define custom user agent
|
||||||
CUSTOM_USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'
|
CUSTOM_USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'
|
||||||
|
|
||||||
|
# Initialize SQLite database
|
||||||
|
def init_db():
|
||||||
|
conn = sqlite3.connect('cache.db')
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute('''
|
||||||
|
CREATE TABLE IF NOT EXISTS cache (
|
||||||
|
url TEXT,
|
||||||
|
route TEXT,
|
||||||
|
data TEXT,
|
||||||
|
timestamp INTEGER,
|
||||||
|
PRIMARY KEY (url, route)
|
||||||
|
)
|
||||||
|
''')
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
# Get cached data if it exists and is not older than 36 hours
|
||||||
|
def get_cached_data(url, route):
|
||||||
|
conn = sqlite3.connect('cache.db')
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cache_expiry = int(time.time()) - (36 * 60 * 60) # 36 hours in seconds
|
||||||
|
cursor.execute(
|
||||||
|
"SELECT data FROM cache WHERE url = ? AND route = ? AND timestamp > ?",
|
||||||
|
(url, route, cache_expiry)
|
||||||
|
)
|
||||||
|
result = cursor.fetchone()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
if result:
|
||||||
|
print(f"Cache hit for {url} on route {route}")
|
||||||
|
return json.loads(result[0])
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Save data to cache
|
||||||
|
def save_to_cache(url, route, data):
|
||||||
|
conn = sqlite3.connect('cache.db')
|
||||||
|
cursor = conn.cursor()
|
||||||
|
timestamp = int(time.time())
|
||||||
|
|
||||||
|
# Convert data to JSON string
|
||||||
|
data_json = json.dumps(data)
|
||||||
|
|
||||||
|
cursor.execute(
|
||||||
|
"INSERT OR REPLACE INTO cache (url, route, data, timestamp) VALUES (?, ?, ?, ?)",
|
||||||
|
(url, route, data_json, timestamp)
|
||||||
|
)
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
print(f"Saved to cache: {url} on route {route}")
|
||||||
|
|
||||||
|
# Initialize database on startup
|
||||||
|
init_db()
|
||||||
|
|
||||||
async def wait_for_network_idle(page):
|
async def wait_for_network_idle(page):
|
||||||
"""Wait until no network requests are in flight"""
|
"""Wait until no network requests are in flight"""
|
||||||
await page.waitForNetworkIdle(idleTime=500, timeout=30000)
|
await page.waitForNetworkIdle(idleTime=500, timeout=30000)
|
||||||
@@ -31,10 +87,16 @@ async def visit_url(url: str, x_api_key: Optional[str] = Header(None)):
|
|||||||
if not x_api_key or x_api_key != API_KEY:
|
if not x_api_key or x_api_key != API_KEY:
|
||||||
raise HTTPException(status_code=401, detail="Invalid API key")
|
raise HTTPException(status_code=401, detail="Invalid API key")
|
||||||
|
|
||||||
|
# Decode URL if it's encoded
|
||||||
|
decoded_url = unquote(url)
|
||||||
|
|
||||||
|
# Check cache first
|
||||||
|
cached_result = get_cached_data(decoded_url, "visit")
|
||||||
|
if cached_result:
|
||||||
|
return cached_result
|
||||||
|
|
||||||
browser = None
|
browser = None
|
||||||
try:
|
try:
|
||||||
# Decode URL if it's encoded
|
|
||||||
decoded_url = unquote(url)
|
|
||||||
print(decoded_url)
|
print(decoded_url)
|
||||||
|
|
||||||
# Launch browser using installed Chrome
|
# Launch browser using installed Chrome
|
||||||
@@ -59,7 +121,12 @@ async def visit_url(url: str, x_api_key: Optional[str] = Header(None)):
|
|||||||
# Close page
|
# Close page
|
||||||
await page.close()
|
await page.close()
|
||||||
|
|
||||||
return {"status": "success", "content": content}
|
result = {"status": "success", "content": content}
|
||||||
|
|
||||||
|
# Save to cache
|
||||||
|
save_to_cache(decoded_url, "visit", result)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise HTTPException(status_code=500, detail=str(e))
|
raise HTTPException(status_code=500, detail=str(e))
|
||||||
@@ -76,10 +143,16 @@ async def extract_seo(url: str, x_api_key: Optional[str] = Header(None)):
|
|||||||
if not x_api_key or x_api_key != API_KEY:
|
if not x_api_key or x_api_key != API_KEY:
|
||||||
raise HTTPException(status_code=401, detail="Invalid API key")
|
raise HTTPException(status_code=401, detail="Invalid API key")
|
||||||
|
|
||||||
|
# Decode URL if it's encoded
|
||||||
|
decoded_url = unquote(url)
|
||||||
|
|
||||||
|
# Check cache first
|
||||||
|
cached_result = get_cached_data(decoded_url, "seo")
|
||||||
|
if cached_result:
|
||||||
|
return cached_result
|
||||||
|
|
||||||
browser = None
|
browser = None
|
||||||
try:
|
try:
|
||||||
# Decode URL if it's encoded
|
|
||||||
decoded_url = unquote(url)
|
|
||||||
print(f"Extracting SEO from: {decoded_url}")
|
print(f"Extracting SEO from: {decoded_url}")
|
||||||
|
|
||||||
# Launch browser using installed Chrome
|
# Launch browser using installed Chrome
|
||||||
@@ -169,7 +242,12 @@ async def extract_seo(url: str, x_api_key: Optional[str] = Header(None)):
|
|||||||
"twitter_card": twitter_data
|
"twitter_card": twitter_data
|
||||||
}
|
}
|
||||||
|
|
||||||
return {"status": "success", "seo_data": seo_data}
|
result = {"status": "success", "seo_data": seo_data}
|
||||||
|
|
||||||
|
# Save to cache
|
||||||
|
save_to_cache(decoded_url, "seo", result)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise HTTPException(status_code=500, detail=str(e))
|
raise HTTPException(status_code=500, detail=str(e))
|
||||||
@@ -186,10 +264,16 @@ async def extract_meta_tags(url: str, x_api_key: Optional[str] = Header(None)):
|
|||||||
if not x_api_key or x_api_key != API_KEY:
|
if not x_api_key or x_api_key != API_KEY:
|
||||||
raise HTTPException(status_code=401, detail="Invalid API key")
|
raise HTTPException(status_code=401, detail="Invalid API key")
|
||||||
|
|
||||||
|
# Decode URL if it's encoded
|
||||||
|
decoded_url = unquote(url)
|
||||||
|
|
||||||
|
# Check cache first
|
||||||
|
cached_result = get_cached_data(decoded_url, "meta")
|
||||||
|
if cached_result:
|
||||||
|
return cached_result
|
||||||
|
|
||||||
browser = None
|
browser = None
|
||||||
try:
|
try:
|
||||||
# Decode URL if it's encoded
|
|
||||||
decoded_url = unquote(url)
|
|
||||||
print(f"Extracting meta tags from: {decoded_url}")
|
print(f"Extracting meta tags from: {decoded_url}")
|
||||||
|
|
||||||
# Launch browser using installed Chrome
|
# Launch browser using installed Chrome
|
||||||
@@ -227,13 +311,18 @@ async def extract_meta_tags(url: str, x_api_key: Optional[str] = Header(None)):
|
|||||||
# Close page
|
# Close page
|
||||||
await page.close()
|
await page.close()
|
||||||
|
|
||||||
return {
|
result = {
|
||||||
"status": "success",
|
"status": "success",
|
||||||
"url": decoded_url,
|
"url": decoded_url,
|
||||||
"title": title,
|
"title": title,
|
||||||
"meta_tags": meta_tags
|
"meta_tags": meta_tags
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Save to cache
|
||||||
|
save_to_cache(decoded_url, "meta", result)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise HTTPException(status_code=500, detail=str(e))
|
raise HTTPException(status_code=500, detail=str(e))
|
||||||
|
|
||||||
@@ -242,6 +331,55 @@ async def extract_meta_tags(url: str, x_api_key: Optional[str] = Header(None)):
|
|||||||
if browser:
|
if browser:
|
||||||
await browser.close()
|
await browser.close()
|
||||||
|
|
||||||
|
@app.get("/cache/clear")
|
||||||
|
async def clear_cache(x_api_key: Optional[str] = Header(None)):
|
||||||
|
"""Clear the entire cache database"""
|
||||||
|
# Validate API key
|
||||||
|
if not x_api_key or x_api_key != API_KEY:
|
||||||
|
raise HTTPException(status_code=401, detail="Invalid API key")
|
||||||
|
|
||||||
|
conn = sqlite3.connect('cache.db')
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("DELETE FROM cache")
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
return {"status": "success", "message": "Cache cleared successfully"}
|
||||||
|
|
||||||
|
@app.get("/cache/stats")
|
||||||
|
async def cache_stats(x_api_key: Optional[str] = Header(None)):
|
||||||
|
"""Get cache statistics"""
|
||||||
|
# Validate API key
|
||||||
|
if not x_api_key or x_api_key != API_KEY:
|
||||||
|
raise HTTPException(status_code=401, detail="Invalid API key")
|
||||||
|
|
||||||
|
conn = sqlite3.connect('cache.db')
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
# Get total entries
|
||||||
|
cursor.execute("SELECT COUNT(*) FROM cache")
|
||||||
|
total_entries = cursor.fetchone()[0]
|
||||||
|
|
||||||
|
# Get entries by route
|
||||||
|
cursor.execute("SELECT route, COUNT(*) FROM cache GROUP BY route")
|
||||||
|
routes = {route: count for route, count in cursor.fetchall()}
|
||||||
|
|
||||||
|
# Get recent entries (last 24 hours)
|
||||||
|
recent_timestamp = int(time.time()) - (24 * 60 * 60)
|
||||||
|
cursor.execute("SELECT COUNT(*) FROM cache WHERE timestamp > ?", (recent_timestamp,))
|
||||||
|
recent_entries = cursor.fetchone()[0]
|
||||||
|
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
return {
|
||||||
|
"status": "success",
|
||||||
|
"stats": {
|
||||||
|
"total_entries": total_entries,
|
||||||
|
"entries_by_route": routes,
|
||||||
|
"recent_entries": recent_entries
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import uvicorn
|
import uvicorn
|
||||||
uvicorn.run(app, host="0.0.0.0", port=8000)
|
uvicorn.run(app, host="0.0.0.0", port=8000)
|
||||||
|
|||||||
Reference in New Issue
Block a user