littleshop/frontend_link_crawler.py
SysAdmin e1b377a042 Initial commit of LittleShop project (excluding large archives)
- 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>
2025-09-17 15:07:38 +01:00

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())