auto-claude: 5.2 - Add POST /api/seo/audit endpoint (admin-only)
Added POST /api/seo/audit endpoint to trigger SEO audits for companies: - Admin-only access with current_user.is_admin check - Rate limited to 10 requests per hour per user - Accepts company_id or slug in JSON body - Runs full SEO audit (PageSpeed, on-page, technical) - Saves results to database and returns audit data - Comprehensive error handling and logging - Uses existing _build_seo_audit_response helper for response format 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
db28aa6419
commit
53cd95873e
159
app.py
159
app.py
@ -121,6 +121,19 @@ except ImportError:
|
||||
NEWS_SERVICE_AVAILABLE = False
|
||||
logger.warning("News service not available")
|
||||
|
||||
# SEO audit components for triggering audits via API
|
||||
import sys
|
||||
_scripts_path = os.path.join(os.path.dirname(__file__), 'scripts')
|
||||
if _scripts_path not in sys.path:
|
||||
sys.path.insert(0, _scripts_path)
|
||||
|
||||
try:
|
||||
from seo_audit import SEOAuditor, SEO_AUDIT_VERSION
|
||||
SEO_AUDIT_AVAILABLE = True
|
||||
except ImportError as e:
|
||||
SEO_AUDIT_AVAILABLE = False
|
||||
logger.warning(f"SEO audit service not available: {e}")
|
||||
|
||||
# Initialize Flask app
|
||||
app = Flask(__name__)
|
||||
app.config['SECRET_KEY'] = os.getenv('SECRET_KEY', 'dev-secret-key-change-in-production')
|
||||
@ -2480,6 +2493,152 @@ def api_seo_audit_by_slug(slug):
|
||||
db.close()
|
||||
|
||||
|
||||
@app.route('/api/seo/audit', methods=['POST'])
|
||||
@login_required
|
||||
@limiter.limit("10 per hour")
|
||||
def api_seo_audit_trigger():
|
||||
"""
|
||||
API: Trigger SEO audit for a company (admin-only).
|
||||
|
||||
This endpoint runs a full SEO audit including:
|
||||
- Google PageSpeed Insights analysis
|
||||
- On-page SEO analysis (meta tags, headings, images, links)
|
||||
- Technical SEO checks (robots.txt, sitemap, canonical URLs)
|
||||
|
||||
Request JSON body:
|
||||
- company_id: Company ID (integer) OR
|
||||
- slug: Company slug (string)
|
||||
|
||||
Returns:
|
||||
- Success: Full SEO audit results saved to database
|
||||
- Error: Error message with status code
|
||||
|
||||
Rate limited to 10 requests per hour per user to prevent API abuse.
|
||||
"""
|
||||
# Admin-only check
|
||||
if not current_user.is_admin:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': 'Brak uprawnień. Tylko administrator może uruchamiać audyty SEO.'
|
||||
}), 403
|
||||
|
||||
# Check if SEO audit service is available
|
||||
if not SEO_AUDIT_AVAILABLE:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': 'Usługa audytu SEO jest niedostępna. Sprawdź konfigurację serwera.'
|
||||
}), 503
|
||||
|
||||
# Parse request data
|
||||
data = request.get_json()
|
||||
if not data:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': 'Brak danych w żądaniu. Podaj company_id lub slug.'
|
||||
}), 400
|
||||
|
||||
company_id = data.get('company_id')
|
||||
slug = data.get('slug')
|
||||
|
||||
if not company_id and not slug:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': 'Podaj company_id lub slug firmy do audytu.'
|
||||
}), 400
|
||||
|
||||
db = SessionLocal()
|
||||
try:
|
||||
# Find company by ID or slug
|
||||
if company_id:
|
||||
company = db.query(Company).filter_by(id=company_id, status='active').first()
|
||||
else:
|
||||
company = db.query(Company).filter_by(slug=slug, status='active').first()
|
||||
|
||||
if not company:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': 'Firma nie znaleziona lub nieaktywna.'
|
||||
}), 404
|
||||
|
||||
# Check if company has a website
|
||||
if not company.website:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': f'Firma "{company.name}" nie ma zdefiniowanej strony internetowej.',
|
||||
'company_id': company.id,
|
||||
'company_name': company.name
|
||||
}), 400
|
||||
|
||||
logger.info(f"SEO audit triggered by admin {current_user.email} for company: {company.name} (ID: {company.id})")
|
||||
|
||||
# Initialize SEO auditor and run audit
|
||||
try:
|
||||
auditor = SEOAuditor()
|
||||
|
||||
# Prepare company dict for auditor
|
||||
company_dict = {
|
||||
'id': company.id,
|
||||
'name': company.name,
|
||||
'slug': company.slug,
|
||||
'website': company.website,
|
||||
'address_city': company.address_city
|
||||
}
|
||||
|
||||
# Run the audit
|
||||
audit_result = auditor.audit_company(company_dict)
|
||||
|
||||
# Check for errors
|
||||
if audit_result.get('errors') and not audit_result.get('onpage') and not audit_result.get('pagespeed'):
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': f'Audyt nie powiódł się: {", ".join(audit_result["errors"])}',
|
||||
'company_id': company.id,
|
||||
'company_name': company.name,
|
||||
'website': company.website
|
||||
}), 422
|
||||
|
||||
# Save result to database
|
||||
saved = auditor.save_audit_result(audit_result)
|
||||
|
||||
if not saved:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': 'Audyt został wykonany, ale nie udało się zapisać wyników do bazy danych.',
|
||||
'company_id': company.id,
|
||||
'company_name': company.name
|
||||
}), 500
|
||||
|
||||
# Get the updated analysis record to return
|
||||
db.expire_all() # Refresh the session to get updated data
|
||||
analysis = db.query(CompanyWebsiteAnalysis).filter_by(
|
||||
company_id=company.id
|
||||
).order_by(CompanyWebsiteAnalysis.analyzed_at.desc()).first()
|
||||
|
||||
# Build response using the existing helper function
|
||||
response = _build_seo_audit_response(company, analysis)
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'message': f'Audyt SEO dla firmy "{company.name}" został zakończony pomyślnie.',
|
||||
'audit_version': SEO_AUDIT_VERSION,
|
||||
'triggered_by': current_user.email,
|
||||
'triggered_at': datetime.now().isoformat(),
|
||||
**response
|
||||
}), 200
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"SEO audit error for company {company.id}: {e}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': f'Błąd podczas wykonywania audytu: {str(e)}',
|
||||
'company_id': company.id,
|
||||
'company_name': company.name
|
||||
}), 500
|
||||
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@app.route('/api/check-email', methods=['POST'])
|
||||
def api_check_email():
|
||||
"""API: Check if email is available"""
|
||||
|
||||
Loading…
Reference in New Issue
Block a user