- BTCPay Server integration - TeleBot Telegram bot - Review system - Admin area - Docker deployment configuration 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
277 lines
11 KiB
Python
277 lines
11 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Frontend Link Crawler for LittleShop Admin Panel
|
|
Tests all navigation links in both mobile and desktop views
|
|
"""
|
|
|
|
import requests
|
|
import re
|
|
from bs4 import BeautifulSoup
|
|
from urllib.parse import urljoin, urlparse
|
|
import json
|
|
import time
|
|
from collections import defaultdict
|
|
|
|
class FrontendLinkCrawler:
|
|
def __init__(self, base_url="http://localhost:5000", username="admin", password="admin"):
|
|
self.base_url = base_url
|
|
self.session = requests.Session()
|
|
self.visited_urls = set()
|
|
self.broken_links = []
|
|
self.redirect_links = []
|
|
self.navigation_links = {}
|
|
self.test_results = {
|
|
"mobile": {"working": [], "broken": [], "redirects": []},
|
|
"desktop": {"working": [], "broken": [], "redirects": []}
|
|
}
|
|
|
|
# Login credentials
|
|
self.username = username
|
|
self.password = password
|
|
|
|
def login(self):
|
|
"""Login to the admin panel"""
|
|
print("🔐 Logging into admin panel...")
|
|
|
|
# Get login page to get anti-forgery token
|
|
login_url = f"{self.base_url}/Admin/Account/Login"
|
|
response = self.session.get(login_url)
|
|
|
|
if response.status_code != 200:
|
|
print(f"❌ Failed to access login page: {response.status_code}")
|
|
return False
|
|
|
|
soup = BeautifulSoup(response.content, 'html.parser')
|
|
token_input = soup.find('input', {'name': '__RequestVerificationToken'})
|
|
|
|
if not token_input:
|
|
print("❌ Could not find anti-forgery token")
|
|
return False
|
|
|
|
token = token_input.get('value')
|
|
|
|
# Submit login form
|
|
login_data = {
|
|
'Username': self.username,
|
|
'Password': self.password,
|
|
'__RequestVerificationToken': token
|
|
}
|
|
|
|
response = self.session.post(login_url, data=login_data, allow_redirects=True)
|
|
|
|
# Check if login was successful (should redirect to dashboard)
|
|
if '/Admin/Dashboard' in response.url or response.url.endswith('/Admin'):
|
|
print("✅ Successfully logged in")
|
|
return True
|
|
else:
|
|
print(f"❌ Login failed - redirected to: {response.url}")
|
|
return False
|
|
|
|
def get_page_links(self, url, view_type="desktop"):
|
|
"""Extract all links from a page"""
|
|
headers = {
|
|
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15' if view_type == "mobile" else 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
|
|
}
|
|
|
|
try:
|
|
response = self.session.get(url, headers=headers, timeout=10)
|
|
response.raise_for_status()
|
|
|
|
soup = BeautifulSoup(response.content, 'html.parser')
|
|
links = []
|
|
|
|
# Extract all anchor tags
|
|
for a_tag in soup.find_all('a', href=True):
|
|
href = a_tag.get('href')
|
|
if href:
|
|
full_url = urljoin(url, href)
|
|
link_text = a_tag.get_text(strip=True)
|
|
link_classes = a_tag.get('class', [])
|
|
|
|
# Only include admin panel links
|
|
if '/Admin' in full_url:
|
|
links.append({
|
|
'url': full_url,
|
|
'text': link_text,
|
|
'classes': link_classes,
|
|
'source_page': url
|
|
})
|
|
|
|
return links, response.status_code
|
|
|
|
except requests.RequestException as e:
|
|
print(f"❌ Error accessing {url}: {str(e)}")
|
|
return [], 0
|
|
|
|
def test_link(self, link_info, view_type="desktop"):
|
|
"""Test if a link is working"""
|
|
url = link_info['url']
|
|
|
|
headers = {
|
|
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15' if view_type == "mobile" else 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
|
|
}
|
|
|
|
try:
|
|
response = self.session.get(url, headers=headers, timeout=10, allow_redirects=False)
|
|
|
|
result = {
|
|
'url': url,
|
|
'text': link_info['text'],
|
|
'source_page': link_info['source_page'],
|
|
'status_code': response.status_code,
|
|
'view_type': view_type
|
|
}
|
|
|
|
if response.status_code == 200:
|
|
self.test_results[view_type]["working"].append(result)
|
|
return "working"
|
|
elif response.status_code in [301, 302, 303, 307, 308]:
|
|
result['redirect_url'] = response.headers.get('Location', 'Unknown')
|
|
self.test_results[view_type]["redirects"].append(result)
|
|
return "redirect"
|
|
else:
|
|
self.test_results[view_type]["broken"].append(result)
|
|
return "broken"
|
|
|
|
except requests.RequestException as e:
|
|
result = {
|
|
'url': url,
|
|
'text': link_info['text'],
|
|
'source_page': link_info['source_page'],
|
|
'error': str(e),
|
|
'view_type': view_type
|
|
}
|
|
self.test_results[view_type]["broken"].append(result)
|
|
return "error"
|
|
|
|
def crawl_admin_panel(self):
|
|
"""Main crawling function"""
|
|
if not self.login():
|
|
return False
|
|
|
|
print("🕷️ Starting frontend link crawler...")
|
|
|
|
# Key admin pages to test
|
|
admin_pages = [
|
|
f"{self.base_url}/Admin/Dashboard",
|
|
f"{self.base_url}/Admin/Categories",
|
|
f"{self.base_url}/Admin/Products",
|
|
f"{self.base_url}/Admin/Orders",
|
|
f"{self.base_url}/Admin/Users",
|
|
f"{self.base_url}/Admin/Bots",
|
|
f"{self.base_url}/Admin/Messages",
|
|
f"{self.base_url}/Admin/ShippingRates"
|
|
]
|
|
|
|
all_links = set()
|
|
|
|
# Collect all links from each page
|
|
for page_url in admin_pages:
|
|
print(f"📄 Scanning page: {page_url}")
|
|
|
|
# Get links for both mobile and desktop views
|
|
for view_type in ["desktop", "mobile"]:
|
|
links, status_code = self.get_page_links(page_url, view_type)
|
|
|
|
if status_code == 200:
|
|
print(f" ✅ Found {len(links)} links in {view_type} view")
|
|
for link in links:
|
|
link['view_type'] = view_type
|
|
all_links.add((link['url'], link['text'], link['source_page'], view_type))
|
|
else:
|
|
print(f" ❌ Failed to access {page_url} in {view_type} view: {status_code}")
|
|
|
|
print(f"\n🔍 Testing {len(all_links)} unique links...")
|
|
|
|
# Test each unique link
|
|
for url, text, source_page, view_type in all_links:
|
|
link_info = {
|
|
'url': url,
|
|
'text': text,
|
|
'source_page': source_page
|
|
}
|
|
|
|
result = self.test_link(link_info, view_type)
|
|
status_emoji = "✅" if result == "working" else "🔄" if result == "redirect" else "❌"
|
|
print(f" {status_emoji} [{view_type}] {text}: {url} -> {result}")
|
|
|
|
# Small delay to avoid overwhelming the server
|
|
time.sleep(0.1)
|
|
|
|
def generate_report(self):
|
|
"""Generate a comprehensive test report"""
|
|
report = {
|
|
"timestamp": time.strftime("%Y-%m-%d %H:%M:%S"),
|
|
"summary": {
|
|
"mobile": {
|
|
"working": len(self.test_results["mobile"]["working"]),
|
|
"broken": len(self.test_results["mobile"]["broken"]),
|
|
"redirects": len(self.test_results["mobile"]["redirects"])
|
|
},
|
|
"desktop": {
|
|
"working": len(self.test_results["desktop"]["working"]),
|
|
"broken": len(self.test_results["desktop"]["broken"]),
|
|
"redirects": len(self.test_results["desktop"]["redirects"])
|
|
}
|
|
},
|
|
"details": self.test_results
|
|
}
|
|
|
|
# Save detailed report
|
|
with open('/mnt/c/Production/Source/LittleShop/frontend_test_report.json', 'w') as f:
|
|
json.dump(report, f, indent=2)
|
|
|
|
# Generate readable summary
|
|
print("\n" + "="*80)
|
|
print("🔍 FRONTEND LINK CRAWLER REPORT")
|
|
print("="*80)
|
|
|
|
for view_type in ["mobile", "desktop"]:
|
|
print(f"\n📱 {view_type.upper()} VIEW RESULTS:")
|
|
summary = report["summary"][view_type]
|
|
print(f" ✅ Working Links: {summary['working']}")
|
|
print(f" 🔄 Redirects: {summary['redirects']}")
|
|
print(f" ❌ Broken Links: {summary['broken']}")
|
|
|
|
# Show broken links in detail
|
|
if self.test_results[view_type]["broken"]:
|
|
print(f"\n❌ BROKEN LINKS ({view_type.upper()}):")
|
|
for broken in self.test_results[view_type]["broken"]:
|
|
print(f" • {broken['text']}: {broken['url']}")
|
|
print(f" Source: {broken['source_page']}")
|
|
if 'error' in broken:
|
|
print(f" Error: {broken['error']}")
|
|
else:
|
|
print(f" Status: {broken['status_code']}")
|
|
print()
|
|
|
|
# Show suspicious redirects
|
|
if self.test_results[view_type]["redirects"]:
|
|
print(f"\n🔄 REDIRECTS ({view_type.upper()}):")
|
|
for redirect in self.test_results[view_type]["redirects"]:
|
|
print(f" • {redirect['text']}: {redirect['url']}")
|
|
print(f" Redirects to: {redirect.get('redirect_url', 'Unknown')}")
|
|
print(f" Source: {redirect['source_page']}")
|
|
print()
|
|
|
|
print("="*80)
|
|
print(f"📊 Report saved to: frontend_test_report.json")
|
|
|
|
return report
|
|
|
|
def main():
|
|
crawler = FrontendLinkCrawler()
|
|
crawler.crawl_admin_panel()
|
|
report = crawler.generate_report()
|
|
|
|
# Quick summary
|
|
total_broken = sum(len(report["details"][view]["broken"]) for view in ["mobile", "desktop"])
|
|
if total_broken > 0:
|
|
print(f"\n⚠️ Found {total_broken} broken links that need attention!")
|
|
return 1
|
|
else:
|
|
print(f"\n🎉 All links are working correctly!")
|
|
return 0
|
|
|
|
if __name__ == "__main__":
|
|
exit(main()) |