Files
backPRO/.paul/phases/01-statlink-autolinking/01-01-PLAN.md
2026-04-09 11:44:45 +02:00

9.6 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, delegation
phase plan type wave depends_on files_modified autonomous delegation
01-statlink-autolinking 01 execute 1
migrations/013_statlink_tracking.sql
src/Services/StatLinkService.php
src/Controllers/SettingsController.php
templates/settings/index.php
.env
true off
## Goal Zbudować mechanizm automatycznego dodawania opublikowanych artykułów do StatLink.pl oraz ich automatycznego usuwania po 60 dniach.

Purpose

Każdy opublikowany artykuł na stronach zapleczowych powinien automatycznie otrzymywać linkowanie w systemie StatLink.pl na okres 60 dni, co zwiększy efektywność pozycjonowania. Po 60 dniach link jest automatycznie usuwany, aby nie marnować punktów.

Output

  • StatLinkService.php — serwis PHP z logowaniem do StatLink via Guzzle (cookies), dodawaniem i usuwaniem linków
  • Migracja SQL do śledzenia linków w StatLink (statlink_id, article_id, added_at, expires_at)
  • Endpoint cron do automatycznego uruchamiania (dodaj nowe / usuń wygasłe)
## Project Context @.paul/PROJECT.md @.paul/ROADMAP.md @.paul/STATE.md

Source Files

@src/Services/PublisherService.php @src/Models/Article.php @src/Controllers/SettingsController.php @.env

StatLink.pl Research (from browser exploration)

  • Login: POST to https://statlink.pl/ with fields: email (textbox), haslo (textbox), submit ZALOGUJ
  • Session: cookie-based (PHP session)
  • Add link: POST to /148,twoje-linki#lista with fields:
    • niepozwol: CSRF token (must be scraped from page)
    • https: 1 (radio, 0=http, 1=https)
    • link: URL without protocol (e.g. "example.com/article-slug")
    • anchor: anchor text (article title or topic keyword)
    • fraza_kluczowa1, fraza_kluczowa2, fraza_kluczowa3: (empty)
    • wylacznosc: unchecked
    • frazowy: unchecked
    • tylko_https: unchecked
    • min_ilosc_znakow: 0
    • statrank_min: 0, statrank_max: 10
    • semstorm_keywords_top_min: 0
    • ilosc_dziennie: 0.14 (= 1 link co 2 dni)
    • ilosc_max: 10
    • ilosc_nofollow: 0
    • max_ilosc_domena: (default 5)
    • id_kategorie_multiple[]: all category values selected
    • zapisz: DODAJ
  • Delete link: POST to /148,twoje-linki#lista0 with fields:
    • statlink_id: ID of the link
    • usun: Usuń
  • Category checkboxes: multiple id_kategorie_multiple[] values (all selected)
  • NOWY LINK form is inside div#nowy_link2vis
  • Each link row has Edytuj and Usuń buttons with statlink_id hidden field

<acceptance_criteria>

Given dane logowania w .env (statlink_url, statlink_login, statlink_password)
When StatLinkService wykonuje login via Guzzle z CookieJar
Then sesja jest utrzymana i kolejne requesty zwracają stronę zalogowanego użytkownika
Given opublikowany artykuł z wp_post_url i tytułem
When StatLinkService::addLink() jest wywołane
Then link zostaje dodany w StatLink.pl z prawidłowymi parametrami (anchor=tytuł/keyword, ilosc_dziennie=0.14, ilosc_max=10, wszystkie kategorie)
And statlink_id zostaje zapisany w tabeli statlink_links

AC-3: Usuwanie wygasłych linków

Given link w tabeli statlink_links z expires_at < NOW()
When StatLinkService::removeExpiredLinks() jest wywołane
Then link zostaje usunięty ze StatLink.pl via POST z usun
And rekord w tabeli statlink_links zostaje oznaczony jako removed

AC-4: Cron endpoint

Given endpoint /cron/statlink z tokenem autoryzacyjnym
When endpoint jest wywołany
Then nowe opublikowane artykuły (bez wpisu w statlink_links) dostają linki w StatLink
And wygasłe linki (expires_at < NOW()) są usuwane ze StatLink

AC-5: Tracking w bazie danych

Given tabela statlink_links
When artykuł dostaje link w StatLink
Then zapisywany jest: article_id, site_id, statlink_id, anchor, added_at, expires_at (added_at + 60 dni), status

</acceptance_criteria>

Task 1: Migracja SQL + Model śledzenia StatLink migrations/013_statlink_tracking.sql Utworzyć migrację tworzącą tabelę statlink_links: - id INT AUTO_INCREMENT PRIMARY KEY - article_id INT NOT NULL (FK do articles) - site_id INT NOT NULL (FK do sites) - statlink_id INT NULL (ID linku w systemie StatLink — parsowany z odpowiedzi) - anchor VARCHAR(500) NOT NULL - link_url VARCHAR(500) NOT NULL - added_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP - expires_at DATETIME NOT NULL (added_at + 60 dni) - removed_at DATETIME NULL - status ENUM('active', 'expired', 'removed', 'failed') DEFAULT 'active' - error_message TEXT NULL - created_at DATETIME DEFAULT CURRENT_TIMESTAMP
Indeksy: (article_id), (status, expires_at), (site_id)
SQL jest poprawny składniowo, tabela zawiera wszystkie kolumny AC-5 satisfied: tabela statlink_links gotowa do śledzenia linków Task 2: StatLinkService — login, dodawanie, usuwanie linków src/Services/StatLinkService.php Utworzyć StatLinkService z metodami:
1. **login()** — POST do statlink.pl z email+haslo, utrzymuj CookieJar w Guzzle
   - Sprawdź odpowiedź czy zawiera "Zalogowano"
   - Rzuć wyjątek jeśli login się nie powiedzie

2. **addLink(array $article, string $anchor)** — dodaje link do StatLink:
   - Najpierw GET /148,twoje-linki aby pobrać token CSRF (pole "niepozwol" — regex z HTML)
   - POST do /148,twoje-linki#lista z parametrami:
     - niepozwol: token z GET
     - https: 1 (jeśli URL artykułu jest HTTPS) lub 0
     - link: URL artykułu bez protokołu (np. "domena.pl/slug")
     - anchor: tytuł artykułu lub keyword tematu (naprzemiennie)
     - fraza_kluczowa1/2/3: puste
     - ilosc_dziennie: 0.14
     - ilosc_max: 10
     - ilosc_nofollow: 0
     - statrank_min: 0, statrank_max: 10
     - id_kategorie_multiple[]: wszystkie kategorie (pobrać listę z HTML)
     - zapisz: DODAJ
   - Parsuj statlink_id z odpowiedzi (szukaj nowego ID w tabeli linków)
   - Return statlink_id lub null

3. **removeLink(int $statlinkId)** — usuwa link ze StatLink:
   - POST do /148,twoje-linki#lista0 z statlink_id + usun=Usuń
   - Sprawdź czy usunięcie się powiodło

4. **getExistingLinkIds()** — parsuje listę linków z /148,twoje-linki
   - Zwraca tablicę statlink_id dla weryfikacji

5. **scrapeCategories()** — parsuje checkboxy kategorii z formularza
   - Zwraca tablicę wartości id_kategorie_multiple[] do zaznaczenia

6. **processNewArticles()** — główna metoda:
   - Pobierz opublikowane artykuły bez wpisu w statlink_links
   - Zaloguj się do StatLink
   - Dla każdego artykułu: addLink() + zapisz do statlink_links z expires_at = NOW + 60 dni
   - Anchor naprzemiennie: tytuł artykułu / keyword tematu

7. **removeExpiredLinks()** — główna metoda usuwania:
   - Pobierz linki z status='active' AND expires_at < NOW()
   - Zaloguj się do StatLink
   - Dla każdego: removeLink() + ustaw status='removed', removed_at=NOW()

Użyj GuzzleHttp\Client z CookieJar.
Loguj operacje przez Logger (kanał 'statlink').
Odporność: try-catch per link, nie przerywaj całego batch przy błędzie jednego.
Avoid: nie wysyłaj więcej niż 5 linków w jednym uruchomieniu crona (rate limiting).
Klasa kompiluje się bez błędów, metody mają prawidłowe sygnatury AC-1, AC-2, AC-3 satisfied: serwis loguje się, dodaje i usuwa linki Task 3: Cron endpoint + integracja z routerem src/Controllers/SettingsController.php, src/Core/Router.php Dodać endpoint /cron/statlink w routerze (wzorowany na istniejących cron endpointach): - Walidacja tokenu (SEO_TRIGGER_TOKEN lub nowy STATLINK_TRIGGER_TOKEN) - Wywołanie StatLinkService::processNewArticles() — dodaj nowe - Wywołanie StatLinkService::removeExpiredLinks() — usuń wygasłe - Zwróć JSON z podsumowaniem (added: N, removed: N, errors: N)
Sprawdź jak działają istniejące cron endpointy w projekcie i zastosuj ten sam wzorzec.
Endpoint /cron/statlink odpowiada JSON-em z podsumowaniem AC-4 satisfied: cron endpoint do automatycznego zarządzania linkami StatLink

DO NOT CHANGE

  • src/Services/PublisherService.php (nie modyfikuj flow publikacji)
  • src/Models/Article.php (nie zmieniaj istniejących metod)
  • migrations/001-012 (istniejące migracje niemodyfikowalne)
  • src/Services/InternalLinkService.php (osobny mechanizm linkowania)

SCOPE LIMITS

  • Ten plan NIE integruje StatLink z procesem publikacji (to osobny cron)
  • Nie dodajemy UI do zarządzania StatLink w panelu backPRO (może w przyszłości)
  • Nie modyfikujemy istniejących endpointów cron
Before declaring plan complete: - [ ] Migracja 013 tworzy tabelę statlink_links - [ ] StatLinkService loguje się do statlink.pl (test ręczny) - [ ] StatLinkService dodaje link (test ręczny z jednym artykułem) - [ ] StatLinkService usuwa link (test ręczny) - [ ] Endpoint /cron/statlink zwraca JSON - [ ] Logger zapisuje operacje na kanale 'statlink' - [ ] Nie więcej niż 5 linków dodanych per uruchomienie crona

<success_criteria>

  • Wszystkie taski wykonane
  • StatLinkService działa end-to-end (login → add → track → remove po 60 dniach)
  • Endpoint cron działa z tokenem
  • Brak błędów w istniejącej funkcjonalności </success_criteria>
After completion, create `.paul/phases/01-statlink-autolinking/01-01-SUMMARY.md`