- Fix broken news thumbnails by adding og:image extraction during content
scraping (replaces Brave proxy URLs that block hotlinking)
- Add image onerror fallback in templates showing domain favicon when
original image fails to load
- Decode Brave proxy image URLs to original source URLs before saving
- Enforce English-only entity types in AI extraction prompt to prevent
mixed Polish/English type names
- Add migration 083 to normalize 14 existing Polish entity types and
clean up 5 stale fetch jobs stuck in 'running' status
- Add backfill script for existing articles with broken image URLs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
External monitoring via UptimeRobot (free tier) with internal health
logger to differentiate ISP outages from server issues. Includes:
- 4 new DB models (UptimeMonitor, UptimeCheck, UptimeIncident, InternalHealthLog)
- Migration 082 with tables, indexes, and permissions
- Internal health logger script (cron */5 min)
- UptimeRobot sync script (cron hourly) with automatic cause correlation
- Admin dashboard /admin/uptime with uptime %, response time charts,
incident log with editable notes/causes, pattern analysis, monthly report
- SLA comparison table (99.9%/99.5%/99%)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Short user sessions (<5min) never triggered the flush, leaving
page_views_count at 0 for most users despite actual browsing activity.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Prevents confusing "link invalid" error when verification link is
clicked twice (or prefetched by email clients like Outlook). The token
now expires naturally instead of being cleared on first use.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The periods data from Google Places API (New) uses {hour, minute} instead
of {time}. The JS check now handles both formats so business hours status
displays correctly.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New page /admin/gbp-audit/match-places shows companies without
google_place_id. Admin searches Google Maps, reviews results, and
confirms the correct match. Adds search_places_raw() to return all
results without name filtering.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Companies with an existing google_place_id now skip the name-matching
search and use the verified Place ID directly. This fixes companies like
ALMARES ("PSB Profi Almares" in Google) whose single-word name failed
strict matching. Also removes invalid 'source' kwarg from GBPAudit.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Batch audit now collects changes without saving to DB. Admin must
review before/after differences and approve or discard. Mirrors the
existing social audit enrichment review pattern.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reviews and business type names were returned in English because
the get_place_details endpoint lacked the languageCode parameter,
unlike searchText and searchNearby which already had it.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Run audit for all active companies with background thread, live progress
panel, and optional Google Places API data refresh. Pattern mirrors
existing social audit batch implementation.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Personalized guide with GBP links (write review, view profile, photos)
for each member's company. Includes tips on collecting reviews,
responding to feedback, Local Guides program, and Google policies.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace hardcoded email check for audit panels with role-based
SUPERADMIN check. ADMIN retains all management capabilities but
SUPERADMIN adds access to technical audits (SEO, IT, GBP, Social,
Access Control).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Admin-sent reset emails now say "Administrator portalu wygenerował
link do ustawienia hasła" instead of generic "zażądano resetowania".
Validity period shown correctly: 24h for admin, 1h for self-service.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The admin reset-password endpoint used datetime.utcnow() while the
validation used datetime.now(), causing tokens to appear expired
immediately on CET servers. Changed to datetime.now() and extended
admin-initiated resets to 24 hours validity.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Admin can now send a password reset email directly from /admin/users
instead of manually copying and sharing the reset link.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Split monolithic "Zarządzanie" dropdown (17 items) into 5 thematic groups:
Audyty, Izba, Social media, System, Treści — sorted alphabetically
left-to-right with items within each dropdown also alphabetized.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove unused social-media analytics panel (replaced by social-audit).
Rename admin menu items for clarity:
- Forum → Moderacja forum
- Ogłoszenia → Moderacja ogłoszeń
- Rekomendacje → Moderacja rekomendacji
- Deklaracje → Zarządzanie deklaracjami
- Składki → Zarządzanie składkami
- Korzyści → Zarządzanie korzyściami
- Social Media → Audyt social media
- Social Dashboard → Publikacja social media
- Kalendarz → Zarządzanie kalendarzem
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace generic "OAuth API niedostępne" with platform-specific
instructions explaining what's needed to fetch data:
- Instagram: 3-step guide (Business account, FB link, OAuth)
- Twitter/YouTube: note that data is fetched automatically
- Facebook: prompt to connect OAuth
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add twitter_service.py using Twitter's internal GraphQL API with
guest token authentication (free, no API key needed). Fetches
followers, tweets, bio, location, media count, and more.
Integrated into social audit enrichment with _twitter_extra data
stored in content_types JSONB, displayed on audit detail cards.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The regex [A-Za-z0-9_-]+ truncated handles containing dots
(e.g. @prosport.rowery → prosport), causing API lookups to
resolve to wrong channels.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- YouTubeService now fetches: subscribers, views, video count, description,
avatar, banner, country, creation date, recent 5 videos
- Enricher uses API first, falls back to scraping
- Extra YouTube data stored in content_types JSONB
- Audit detail shows view count, country, creation date, recent videos
- Requires enabling YouTube Data API v3 in Google Cloud Console
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Users can now select which platforms to scan (Facebook, Instagram,
LinkedIn, YouTube, Twitter/X, TikTok) via checkboxes in the
confirmation dialog. Backend filters both company list and profiles.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
3 attempts with 2-5s random delay between retries. Detects authwall
and rate limit (429/999) responses. Updated status message to explain
LinkedIn's inconsistent availability to users.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
og:description from LinkedIn/Facebook embeds dynamic follower counts
and varies by language, causing false "changes" on every scan.
The field is still saved to DB, just not tracked for review.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
content_types and followers_history were mutated in-place on the same
object reference, so SQLAlchemy didn't detect changes and skipped the
UPDATE. Now creates a fresh copy before modifying.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Added: talking_about_count, username, verification_status, price_range,
category_list, page_posts_impressions, video_views, daily_follows/unfollows.
Updated audit detail UI with new data sections.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Use config.access_token (Page Token) instead of User Token for feed/insights
- Remove deprecated 'type' field from feed, use attachments{media_type}
- Fetch insights individually to handle unavailable metrics gracefully
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Instead of checking profile.source (which was never 'facebook_api'),
now checks SocialMediaConfig for companies with OAuth page_id.
Added progress indicator with spinner and percentage during scan.
After API sync, page auto-refreshes to show new data.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Instead of skipping OAuth-connected Facebook profiles, the enrichment
scan now calls sync_facebook_to_social_media() to fetch fresh data
via Graph API.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add picture,cover fields to get_page_info() request
- New get_page_posts_stats() fetches post count (30d/365d) and last post date
- Set has_profile_photo, has_cover_photo, posts_count_30d/365d, last_post_date, posting_frequency_score
- Include profile photo in completeness score (5 fields × 20% = 100%)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix checkmarks showing as ✓ by using Unicode ✓/✗ directly
- Decode HTML entities (' &) from og:meta in enricher results
- Replace native confirm()/alert() with styled modal dialogs and toasts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>