From 5e6c3e46fc89699a9613005193a5d3ee3dbf85f3 Mon Sep 17 00:00:00 2001 From: Jacek Pyziak Date: Sat, 28 Feb 2026 00:26:09 +0100 Subject: [PATCH] docs: plan implementacji modulu Releases (dwukanalowy system aktualizacji) Co-Authored-By: Claude Sonnet 4.6 --- docs/plans/2026-02-28-update-channels.md | 766 +++++++++++++++++++++++ 1 file changed, 766 insertions(+) create mode 100644 docs/plans/2026-02-28-update-channels.md diff --git a/docs/plans/2026-02-28-update-channels.md b/docs/plans/2026-02-28-update-channels.md new file mode 100644 index 0000000..3ea3673 --- /dev/null +++ b/docs/plans/2026-02-28-update-channels.md @@ -0,0 +1,766 @@ +# Releases Module Implementation Plan + +> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. + +**Goal:** Wdrożyć dwukanałowy system aktualizacji (beta/stable) z zarządzaniem licencjami w panelu admina. + +**Architecture:** Dwie nowe tabele MySQL (`pp_update_licenses`, `pp_update_versions`) dostępne tylko na serwerze dewelopera. `updates/versions.php` czyta z DB przez Medoo. Nowy moduł `Releases` w panelu admina zarządza wersjami i licencjami. Całość wykluczona z paczek klientów przez `.updateignore`. + +**Tech Stack:** PHP 8.x, Medoo 1.x (`$mdb` global), jQuery UI (dialog), Bootstrap 3 (klasy CSS), `\Tpl` (admin templates z `admin/templates/`), `\S::get()` (POST→GET params). + +--- + +### Task 1: Dodaj wykluczenia do `.updateignore` + +**Files:** +- Modify: `.updateignore` + +**Step 1: Dopisz nowe wykluczenia** + +Otwórz `.updateignore` i dopisz na końcu: + +``` +# Moduł zarządzania releaseami (tylko serwer dewelopera) +autoload/admin/controls/class.Releases.php +autoload/admin/factory/class.Releases.php +autoload/admin/view/class.Releases.php +admin/templates/releases/ + +# Menu dewelopera +templates/additional-menu.php +``` + +**Step 2: Zweryfikuj manualnie** + +Uruchom build w trybie dry-run i sprawdź, że powyższe pliki NIE pojawiają się na liście: +```powershell +./build-update.ps1 -ToTag v9.999 -DryRun +``` + +**Step 3: Commit** + +```bash +git add .updateignore +git commit -m "chore: wyklucz modul Releases i menu dewelopera z paczek klientow" +``` + +--- + +### Task 2: Utwórz tabele DB (jednorazowo na serwerze) + +**Files:** +- Create: `_db_releases_setup.sql` (uruchom raz w phpMyAdmin, nie commituj) + +**Step 1: Utwórz plik SQL** + +```sql +CREATE TABLE pp_update_licenses ( + id INT AUTO_INCREMENT PRIMARY KEY, + `key` VARCHAR(64) NOT NULL UNIQUE, + domain VARCHAR(255) NOT NULL, + valid_to_date DATE NULL, + valid_to_version VARCHAR(10) NULL, + beta TINYINT(1) NOT NULL DEFAULT 0, + note TEXT NULL, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE pp_update_versions ( + id INT AUTO_INCREMENT PRIMARY KEY, + version VARCHAR(10) NOT NULL UNIQUE, + channel ENUM('beta','stable') NOT NULL DEFAULT 'beta', + created_at DATETIME NULL, + promoted_at DATETIME NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +``` + +**Step 2: Uruchom w phpMyAdmin na serwerze `cmspro.project-dc.pl`** + +Wklej SQL do phpMyAdmin → Execute. Obie tabele muszą być widoczne. + +**Step 3: Nie commituj** — ten plik jest tylko pomocniczy, możesz go usunąć po wykonaniu. + +--- + +### Task 3: Utwórz `admin\factory\Releases` + +**Files:** +- Create: `autoload/admin/factory/class.Releases.php` + +**Step 1: Napisz klasę** + +```php +select('pp_update_versions', '*', ['ORDER' => ['version' => 'DESC']]); + if (!$rows) return []; + foreach ($rows as &$row) + $row['zip_exists'] = file_exists('../updates/' . self::zip_dir($row['version']) . '/ver_' . $row['version'] . '.zip'); + return $rows; + } + + public static function promote(string $version): void + { + global $mdb; + $mdb->update('pp_update_versions', + ['channel' => 'stable', 'promoted_at' => date('Y-m-d H:i:s')], + ['version' => $version] + ); + } + + public static function demote(string $version): void + { + global $mdb; + $mdb->update('pp_update_versions', + ['channel' => 'beta', 'promoted_at' => null], + ['version' => $version] + ); + } + + public static function get_licenses(): array + { + global $mdb; + return $mdb->select('pp_update_licenses', '*', ['ORDER' => ['domain' => 'ASC']]) ?: []; + } + + public static function get_license(int $id): array + { + global $mdb; + return $mdb->get('pp_update_licenses', '*', ['id' => $id]) ?: []; + } + + public static function save_license(array $data): void + { + global $mdb; + $row = [ + 'key' => trim($data['key'] ?? ''), + 'domain' => trim($data['domain'] ?? ''), + 'valid_to_date' => $data['valid_to_date'] ?: null, + 'valid_to_version' => $data['valid_to_version'] ?: null, + 'beta' => (int)(bool)($data['beta'] ?? 0), + 'note' => trim($data['note'] ?? ''), + ]; + if (!empty($data['id'])) + $mdb->update('pp_update_licenses', $row, ['id' => (int)$data['id']]); + else + $mdb->insert('pp_update_licenses', $row); + } + + public static function delete_license(int $id): void + { + global $mdb; + $mdb->delete('pp_update_licenses', ['id' => $id]); + } + + public static function toggle_beta(int $id): void + { + global $mdb; + $license = $mdb->get('pp_update_licenses', ['id', 'beta'], ['id' => $id]); + if ($license) + $mdb->update('pp_update_licenses', ['beta' => $license['beta'] ? 0 : 1], ['id' => $id]); + } + + private static function zip_dir(string $version): string + { + return substr($version, 0, strlen($version) - (strlen($version) == 5 ? 2 : 1)) . '0'; + } +} +``` + +**Step 2: Weryfikacja składni** + +```bash +php -l autoload/admin/factory/class.Releases.php +``` +Oczekiwane: `No syntax errors detected` + +--- + +### Task 4: Utwórz `admin\controls\Releases` + +**Files:** +- Create: `autoload/admin/controls/class.Releases.php` + +**Step 1: Napisz klasę** + +```php +versions = \admin\factory\Releases::get_versions(); + $tpl->licenses = \admin\factory\Releases::get_licenses(); + return $tpl->render('releases/main-view'); + } +} +``` + +**Step 2: Weryfikacja składni** + +```bash +php -l autoload/admin/view/class.Releases.php +``` + +--- + +### Task 6: Utwórz szablon `admin/templates/releases/main-view.php` + +**Files:** +- Create: `admin/templates/releases/main-view.php` + +**Step 1: Utwórz katalog i plik** + +```php + + +
+
+ + + + + +
+
+

Wersje

+
+ + + + + + + + + versions as $v): ?> + + + + + + + + + + versions): ?> + + + +
WersjaKanałDodanaPromocja do stableZIPAkcje
+ + stable + + beta + + ✓' : '' ?> + + + Promuj →stable + + + + Cofnij →beta + + +
Brak wersji w bazie. Wersje będą rejestrowane automatycznie przy pierwszym odpytaniu versions.php.
+
+
+
+ + + + +
+
+ + +``` + +**Step 2: Zweryfikuj, że szablon jest poprawny** + +Otwórz w przeglądarce: `https://cmspro.project-dc.pl/admin/releases/main_view/` + +Oczekiwane: strona ładuje się bez błędów PHP, widoczne dwa taby, tabela wersji pusta lub z danymi. + +--- + +### Task 7: Dodaj pozycję menu dla dewelopera + +**Files:** +- Create: `templates/additional-menu.php` (ten plik nie trafia do klientów — dodany do `.updateignore` w Task 1) + +**Step 1: Utwórz plik** + +```php + +
Developer
+ +``` + +**Step 2: Sprawdź w przeglądarce** + +Po odświeżeniu panelu admina powinna pojawić się sekcja "Developer" z linkiem "Releases & Licencje" w menu bocznym. + +--- + +### Task 8: Migracja danych licencji do DB + +Licencje z hardkodowanej tablicy `$license` w `updates/versions.php` muszą trafić do `pp_update_licenses` **przed** przejściem na nową wersję `versions.php`. + +**Files:** +- Create: `_migrate_licenses.php` (tymczasowy, uruchom raz przez przeglądarkę, potem usuń) + +**Step 1: Utwórz skrypt migracji** + +```php + 'mysql', + 'database_name' => $database['name'], + 'server' => $database['host'], + 'username' => $database['user'], + 'password' => $database['password'], + 'charset' => 'utf8' +]); + +// Wyodrębnij $license z pliku versions.php bez uruchamiania die() +$source = file_get_contents('updates/versions.php'); + +// Wyciągnij wszystkie wpisy $license['key']['domain'] itd. +preg_match_all('/\$license\[\'([^\']*)\'\]\[\'domain\'\]\s*=\s*\'([^\']*)\';/', $source, $m_domain); +preg_match_all('/\$license\[\'([^\']*)\'\]\[\'valid_to_date\'\]\s*=\s*\'([^\']*)\';/', $source, $m_date); +preg_match_all('/\$license\[\'([^\']*)\'\]\[\'valid_to_version\'\]\s*=\s*\'([^\']*)\';/', $source, $m_ver); + +// Zbuduj mapę po kluczu +$dates = array_combine($m_date[1], $m_date[2]); +$vers = array_combine($m_ver[1], $m_ver[2]); + +$count = 0; +foreach ($m_domain[1] as $i => $key) { + $domain = $m_domain[2][$i]; + $row = [ + 'key' => $key, + 'domain' => $domain, + 'valid_to_date' => ($dates[$key] ?? '') ?: null, + 'valid_to_version' => ($vers[$key] ?? '') ?: null, + 'beta' => 0, + ]; + // Pomiń jeśli już istnieje + if ($mdb->has('pp_update_licenses', ['key' => $key])) { + echo "SKIP (już istnieje): $domain ($key)
"; + continue; + } + $mdb->insert('pp_update_licenses', $row); + echo "OK: $domain ($key)
"; + $count++; +} +echo "
Zmigrowano $count licencji."; +echo "
USUŃ ten plik z serwera!"; +``` + +**Step 2: Wgraj na serwer i uruchom** + +``` +https://cmspro.project-dc.pl/_migrate_licenses.php +``` + +Oczekiwane: lista "OK: domena (klucz)" dla każdej licencji, na końcu podsumowanie. + +**Step 3: Usuń skrypt z serwera** + +Skasuj `_migrate_licenses.php` — nie commituj go do repozytorium. + +**Step 4: Ustaw flagę beta dla swoich testowych stron** + +W panelu admina `/admin/releases/main_view/` → zakładka Licencje → przy swoich testowych domenach kliknij "stable" aby przełączyć na "beta". + +--- + +### Task 9: Przebuduj `updates/versions.php` + +**Files:** +- Modify: `updates/versions.php` + +> **WAŻNE:** Wykonaj ten krok dopiero po Task 8 (migracja danych do DB). + +**Step 1: Zastąp całą zawartość pliku** + +```php + 'mysql', + 'database_name' => $database['name'], + 'server' => $database['host'], + 'username' => $database['user'], + 'password' => $database['password'], + 'charset' => 'utf8' +] ); + +$current_ver = 1691; // aktualizowane automatycznie przez build-update.ps1 + +// 1. Skan filesystem — lista istniejących ZIPów +$versions = []; +for ( $i = 1; $i <= $current_ver; $i++ ) +{ + $dir = substr( number_format( $i / 1000, 3 ), 0, strlen( number_format( $i / 1000, 3 ) ) - 2 ) . '0'; + $version_old = number_format( $i / 1000, 2 ); + $version_new = number_format( $i / 1000, 3 ); + + if ( file_exists( '../updates/' . $dir . '/ver_' . $version_old . '.zip' ) ) + $versions[] = $version_old; + + if ( file_exists( '../updates/' . $dir . '/ver_' . $version_new . '.zip' ) ) + $versions[] = $version_new; +} +$versions = array_unique( $versions ); + +// 2. Walidacja klucza licencji +$license = $mdb->get( 'pp_update_licenses', '*', [ 'key' => ( $_GET['key'] ?? '' ) ] ); +if ( !$license ) + die(); + +// 3. Sprawdź ważność daty +if ( $license['valid_to_date'] && $license['valid_to_date'] < date( 'Y-m-d' ) ) + die(); + +// 4. Auto-discovery: rejestruj nowe ZIPy jako beta +$known = array_flip( $mdb->select( 'pp_update_versions', 'version', [] ) ?: [] ); +foreach ( $versions as $ver ) +{ + if ( !isset( $known[$ver] ) ) + { + @$mdb->insert( 'pp_update_versions', [ + 'version' => $ver, + 'channel' => 'beta', + 'created_at' => date( 'Y-m-d H:i:s' ) + ] ); + $known[$ver] = true; + } +} + +// 5. Filtruj wersje wg kanału (beta widzi beta+stable, reszta tylko stable) +$channels = $license['beta'] ? [ 'beta', 'stable' ] : [ 'stable' ]; +$allowed = array_flip( $mdb->select( 'pp_update_versions', 'version', [ 'channel' => $channels ] ) ?: [] ); + +// 6. Wypisz dostępne wersje +$valid_to_version = $license['valid_to_version']; +foreach ( $versions as $ver ) +{ + if ( !isset( $allowed[$ver] ) ) + continue; + + if ( $valid_to_version && $ver > $valid_to_version ) + continue; + + echo $ver . PHP_EOL; +} +``` + +**Step 2: Weryfikacja składni** + +```bash +php -l updates/versions.php +``` + +**Step 3: Test ręczny** + +Otwórz w przeglądarce (podaj klucz jednej z migrowanych licencji): +``` +https://cmspro.project-dc.pl/updates/versions.php?key=TWOJ_KLUCZ +``` + +Oczekiwane: +- Dla klucza z `beta=0`: zwraca TYLKO wersje `channel='stable'` (pusta lista jeśli żadna jeszcze niepromowana) +- Dla klucza z `beta=1`: zwraca wersje `channel='beta'` i `'stable'` +- Dla nieprawidłowego klucza: brak odpowiedzi (die()) + +**Step 4: Sprawdź panel aktualizacji u klienta** + +Na swojej testowej stronie (beta=1) otwórz `/admin/update/main_view/` — powinien widzieć dostępne wersje. + +--- + +### Task 10: Commit końcowy + +**Step 1: Sprawdź status** + +```bash +git status +git diff updates/versions.php +``` + +**Step 2: Commit** + +```bash +git add \ + .updateignore \ + autoload/admin/factory/class.Releases.php \ + autoload/admin/controls/class.Releases.php \ + autoload/admin/view/class.Releases.php \ + admin/templates/releases/main-view.php \ + templates/additional-menu.php \ + updates/versions.php + +git commit -m "$(cat <<'EOF' +feat: dwukanalowy system aktualizacji (beta/stable) + zarzadzanie licencjami + +- Nowy modul admin\Releases: lista wersji z promocja beta→stable, + CRUD licencji z flaga beta +- versions.php czyta z DB (pp_update_licenses, pp_update_versions) + zamiast hardkodowanej tablicy $license +- Auto-discovery: nowe ZIPy automatycznie rejestrowane jako 'beta' +- Calosc wykluczona z paczek klientow przez .updateignore + +Co-Authored-By: Claude Sonnet 4.6 +EOF +)" +``` + +--- + +## Weryfikacja end-to-end + +Po wdrożeniu sprawdź ręcznie: + +1. **Nowy ZIP (beta):** Wrzuć nowy ZIP na serwer → odpytaj `versions.php` kluczem beta → pojawia się nowa wersja → w panelu admina widoczna jako `beta` +2. **Promocja:** Kliknij "Promuj →stable" → odpytaj kluczem `beta=0` → wersja pojawia się na liście +3. **Cofnięcie:** Kliknij "Cofnij →beta" → klient `beta=0` nie widzi wersji +4. **Licencja:** Dodaj nową licencję przez formularz → weryfikuj w phpMyAdmin +5. **Toggle beta:** Kliknij "stable" przy licencji → zmienia się na "beta" +6. **Zabezpieczenie:** Wgraj nową wersję CMS do klienta → sprawdź czy `admin/templates/releases/` i `autoload/admin/*/class.Releases.php` NIE znalazły się w ZIPie (build dry-run)