| Key | Name | Labels | Description | Attachments | Status | Resolution | Current Environment | Dates | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| PRESSWEB-110 | Beta system fixes, email campaigns, SMTP/DKIM, malware analysis, search backend |
Session 2026-05-06 — backend and infrastructure work.
*Ligh...
Session 2026-05-06 — backend and infrastructure work.
*Light Theme:*
- Recolored all 4 beta pages to match pressblk.com (white bg, blue #0693e3)
- Fixed footer links: /about-us/ -> /about/, /contact-us/ -> /contact/
- terms-of-use.html and privacy-policy.html mounted in Docker (fixed 500)
*Email Verification:*
- Added /beta-verify/{token} endpoint — proper gate before media library access
- Media library blocks unverified users (403)
- Tickets require email_verified_at
- Email button: Verify My Email -> /beta-verify/{token}
*Email Campaign System (Filament Admin):*
- New EmailCampaignResource with RichEditor for email body
- 3 recipient fields: Users dropdown, Beta Testers dropdown, manual email input
- Delivery types: manual (send now), scheduled, event triggers
- SendCampaignJob with per-recipient logging
- campaigns:send-scheduled command (every minute)
- Fixed recipient_type default value (500 on create)
*SMTP/DKIM/SSL:*
- SSL cert was expired (Plesk self-signed from 2022) — admin installed Let Encrypt
- DKIM configured and passing
- SPF/DKIM/DMARC all PASS — spam is reputation-based, not auth
*Search Backend:*
- SearchController now uses ST_Distance_Sphere with lat/lng/distance
- Auto-fallback: if 0 results in radius, shows 5 nearest
- Hidden binary POINT location from Business JSON (was crashing mobile)
- Added JSON_INVALID_UTF8_SUBSTITUTE to prevent 500 on bad data
- Deleted test businesses with Cyrillic names
*Chat Push Notifications:*
- PushNotificationService: filter is_active tokens, parse Expo ticket errors
- Auto-deactivate DeviceNotRegistered tokens
- Added diagnostic endpoint GET /messages/test-push/{userId}
*Security — Malware:*
- pressblk.com crypto-drainer root cause: WakeX API port 3847 + phpMyAdmin port 8081 open to internet
- MySQL root password hardcoded in server.js
- Recommended: close ports, change all passwords
*Docker:*
- Mounted entrypoint.sh for Filament assets auto-publish
- Filament ticket view shows attached screenshots
- Ticket list shows beta tester name correctly
*Commits:* d0b5d91, 3d75e2f, 0c83437, b58ba2f, 3841907, 5e87720, 4010d42, 0709944, d46a43e + more
|
Backlog | Unresolved | -- |
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PRESSWEB-109 | Beta Testing System — Registration, Media Library, Tickets, FAQ, Email Verification |
Implemented a complete beta tester management system with 4 ...
Implemented a complete beta tester management system with 4 public pages and full workflow automation.
*Pages created:*
- /beta-testers — Registration form with NDA, signature canvas, iOS/Android platform selection
- /my-media/{token} — Personal media library for each tester (upload screenshots, grid view, lightbox, iOS Shortcut support)
- /beta-tickets — Bug report / feature request form (4-step wizard with media library integration)
- /beta-faq — 39 Q&A items across 7 sections
*Backend:*
- BetaTester model + migration (name, email, phone, platform, NDA signature, media_token, email_verified_at)
- BetaMedia model + migration (file uploads, per-tester storage)
- MediaLibraryController — upload, list, delete, list-by-email endpoints
- BetaTicketController — public ticket creation, library_ids attachment, email verification check
- AppStoreConnectService — JWT auth, auto-add iOS testers to TestFlight, skip invite for existing ASC testers
- BetaTesterWelcome email — verification link, TestFlight instructions, media library link, ticket guide
- Docker: persistent volume for media uploads, mounted HTML/blade/ASC key files
*TestFlight integration:*
- Auto-invite new iOS testers to External Beta Testers group via ASC API
- Skip invite for existing internal/external testers (isTesterExists check)
- Added build 2 to Beta Testers group
- Fixed Codemagic build numbering — now queries ASC API for max build + 1
- Fixed beta_groups name: Beta Testers (was AppBuilder Testers)
*IAP fix:*
- Fixed "Invalid request for iOS. The sku property is required" error
- react-native-iap v14.7.20 requires request.apple.sku format (not request.sku)
*Security — Malware analysis on pressblk.com:*
- Crypto-drainer returned (same attacker wallet 0x08207B...)
- Full scan: infection via wp_footer hook, /cart/ and /checkout/ clean (WooCommerce simplified template)
- Root cause found: WakeX chat-api on port 3847 exposed MySQL root password to internet
- phpMyAdmin on port 8081 also open externally
- Same password (PressBlk2025Secure!) used across all services
- SQL queries prepared for WordPress DB cleanup
- Recommended: close ports 3847/8081, change all passwords, install Wordfence
*SMTP issue:*
- mail.pressblk.net SSL port 465 down after Plesk cleanup
- Port 25 + STARTTLS works but SPF mismatch (our IP 148.72.133.193 not in SPF record)
- Fix: admin needs to add ip4:148.72.133.193 to pressblk.app SPF DNS record
*Commits:* 8c23313, 5c19878, 1da6226, 3bea5c9, 195138f, ab47ddd, cf3d881, ff6e96f, 7a5ec34, 0950eb4, 74459d0, 0e3bff0, 2973ea5 (mobile)
|
Backlog | Unresolved | -- |
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PRESSWEB-108 | Security: Malware cleanup on pressblk.com + website header fix |
pressblk.com WordPress site was compromised with a crypto-dr...
pressblk.com WordPress site was compromised with a crypto-drainer malware injected via a fake MU-plugin. The malware displayed a fake Cloudflare verification page to trick visitors into executing malicious code. Additionally, the site header/navigation menu was missing due to Hello Elementor theme not supporting standard WordPress menus.
Scope:
1. Security audit and malware removal
2. Website header/navigation restoration
3. Post-incident security recommendations
|
Done | Done | -- |
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PRESSWEB-107 | Multi-source BO directory scrapers — 15-site backlog |
h2. Sources to scrape
15 Black-owned-business directories r...
h2. Sources to scrape
15 Black-owned-business directories referred by Michael Battle (Oct/Jun/Jul 2025 emails) plus Max's earlier scraping targets. ByBlack is already covered (PRESSWEB-105 + the new staging:import-from-byblack-api command).
Each line below is a checkbox — update status as scrapers ship.
h3. Source list (status: ☐ pending · ⚙ recon · 🛠 built · ✅ live · 🚫 blocked)
*From Michael Battle — Oct 17, 2025 email:*
* ☐ www.buyblack.org
* ☐ www.ourgreenweb.com
* ☐ www.blackbusinesslist.com
* ☐ www.blackdirectory.com
* ☐ www.blacksheet.net
* ✅ www.byblack.us — covered in PRESSWEB-105 + new /api/v3/search/ harvester
* ☐ www.blackownedworld.org
* ☐ www.yourgreenbook.com
*From Michael Battle — June 19, 2025 email (Green Book):*
* ☐ greenbooktb.com
* ☐ www.floridablackchamber.com
*From Michael Battle — June 20, 2025 email (URL):*
* ☐ usblackchambers.org
*From Michael Battle — July 26, 2025 email (MS Black Pages):*
* ☐ MS Black Pages — need to find canonical URL
*Max's earlier scraping targets:*
* ☐ funtimesmagazine.com/business-directory/
* ☐ championblackbusinesses.com
* ☐ abc.iamblackbusiness.com
* ☐ blackwomenempowereddirectory.com
h2. Plan
*Phase 1 — Recon (in progress):* For each site, check robots.txt, look for sitemap.xml, sample one listing page + one detail page, identify CMS/SPA (WordPress, Wix, Joomla, custom SPA, etc.). Classify each as:
* *easy* — has sitemap, server-renders detail pages with clear structured data → reuse StagingImportFromDirectories framework, ~30 min per site to wire up
* *medium* — needs HTML regex/JSON-LD parsing, but data is server-rendered → ~1 hr per site
* *spa* — JS-rendered like ByBlack was → either find their backend API (best) or use Crawlee/Playwright (Phase 2)
* *blocked* — Cloudflare WAF / login-walled / hostile → defer or use paid scraping API
*Phase 2 — Build the easy wins:* For each easy/medium site, add a new entry to the $directories array in StagingImportFromDirectories.php and a new parser method. Reuse the WebShare proxy rotation, UA rotation, jitter, daily cron pattern that's already working for EatOkra and ByBlack.
*Phase 3 — SPA / blocked sites:* For sites that need JS rendering, either reverse-engineer their backend API (worked for ByBlack — found api.byblack.us/api/v3/search/) or use the Playwright pipeline at /home/pressblk-crawlee/.
h2. Expected yield
ByBlack alone has 17,979 listings hidden behind their SPA. If even 5 of these other 15 sites have similar volume, that's potentially 50K-100K new BO-curated rows added to business_staging.
h2. Tracking
Description gets updated as each site moves through the workflow. Each scraper gets its own commit + completes its checkbox above. Final summary added when the full list is done.
h2. Related
* PRESSWEB-105 — Scraper 2.0 base + EatOkra + ByBlack
* PRESSWEB-104 — fast-cycle cron + lock fixes (foundation infrastructure)
* /home/pressblk-crawlee/ — Playwright infrastructure for the SPA-only sites
|
Backlog | Unresolved | -- |
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PRESSWEB-106 | gmaps-scraper queue stuck — partition SQL bug + 16 stuck-queued rows |
h2. Symptom
User checking /scraper-queries observed 0 progr...
h2. Symptom
User checking /scraper-queries observed 0 progress drainage despite the cron showing every 10 min as scheduled. Investigation showed the gmaps-scraper had been *running* but only producing duplicates (every batch returned 13 businesses, all merged into existing rows).
h2. Three root causes
h3. Bug 1 — *%%* in whereRaw caused MySQL syntax error
In scripts/run-scraper-and-import.sh the partition picker was:
{noformat}
->whereRaw('id %% \${INSTANCE_TOTAL} = \${INSTANCE_NUM}')
{noformat}
PHP whereRaw passes the string verbatim. MySQL saw {{id %% 2 = 0}} which is invalid (MySQL modulo is single {{%}}). Stack trace from storage/logs/scraper.log:
{noformat}
SQLSTATE42000: Syntax error or access violation: 1064
... near '% 2 = 0 limit 10 for update'
{noformat}
The SQL exception fell through to a fallback that picked only 1 random pending query per tick instead of the requested 10. With ~12 ticks/hour × 1 query each = ~12 queries/hour throughput, where intended was 120/hour.
h3. Bug 2 — 16 rows stuck in *queued* state
The pickup logic sets {{status='queued'}} when reserving rows but only flips to {{completed}} after a successful run. When the script crashes / OOMs / network-fails between those steps, rows remain {{queued}} forever.
Query showed 16 rows had been {{queued}} since 2026-04-14 (12 days). Permanently lost from rotation until manually reset.
h3. Bug 3 — backlog vastly bigger than visible 'pending' suggested
Of the 8,284 pending queries, *5,760 had never been run* (last_run_at IS NULL) — that's the genuine backlog. The remaining 2,524 had been retried at least once.
h2. Fixes applied
# *Script SQL fix*: scripts/run-scraper-and-import.sh — single-character change {{%%}} → {{%}}. Verified via tinker that the partition picker now returns 5 partition-1 odd-id rows correctly (#293 Orlando real-estate, #1381 Phoenix barbershop, etc.).
# *Stuck-queued reset*: SQL {{UPDATE scraper_queries SET status='pending', last_run_at=NULL WHERE status='queued' AND updated_at < NOW() - INTERVAL 1 HOUR}} → 16 rows back into rotation. Pending count: 8,284 → 8,300.
# *Throughput bump*: host crontab — both A and B instances bumped from {{run-scraper-and-import.sh 10 60 120}} to {{... 30 60 120}} (10 → 30 queries per tick).
# *Zombie cleanup*: killed run-scraper-and-import.sh PIDs 681921+681924 (started 09:30 UTC — running 14h with no completions in the last 18h).
h2. Expected throughput
||Config||Queries/hour||Time to drain 5,760||
|Before fix (broken)|~12|~480 hours / 20 days|
|After Bug 1 fix only|~120|~48 hours / 2 days|
|After Bug 1 + batch 30|*~360*|*~16 hours*|
|+ 4 partitions later (option)|~720|~8 hours|
h2. Files changed
* scripts/run-scraper-and-import.sh — 1-char fix in line 52 (whereRaw)
* host crontab (root) — both A and B instances run-scraper-and-import.sh max-queries 10 → 30
h2. Commit
94054de on dev — "fix(gmaps-scraper): %% in whereRaw causing partition picker to fail"
h2. Verification
Waiting for next cron tick to confirm 30-query batch picks up at scale. Will append result as a comment.
h2. Related
* PRESSWEB-105 — Scraper 2.0 (separate system, directory imports). Surfaced this bug because user asked how /scraper-queries relates to Scraper 2.0; investigation revealed the gmaps-scraper queue had been quietly broken for ~2 weeks.
|
Backlog | Unresolved | -- |
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PRESSWEB-105 | Scraper 2.0 — bulk-import from curated Black-owned business directories |
h2. Why
LinkedIn DW enrichment was producing low-yield matc...
h2. Why
LinkedIn DW enrichment was producing low-yield matches because the populations don't overlap: the DW skews to white-collar corporate professionals while pressblk's target is small Black-owned local businesses. We pivoted away from DW-based enrichment and toward harvesting *curated Black-owned-business directories* — the right population for the platform.
h2. What was built
New artisan command *staging:import-from-directories*. Unlike the existing staging:verify-directories (which only flips black_owned_verified=true on existing rows), this command IMPORTS new rows from the directory: walks the sitemap, parses each detail page, inserts to business_staging with black_owned_verified=true and data_sources='directory_ Tasks Assign to Client
Tasks Assigned to Team
|