23 changed files with 682 additions and 43 deletions
@ -0,0 +1,29 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace DoctrineMigrations; |
||||||
|
|
||||||
|
use Doctrine\DBAL\Schema\Schema; |
||||||
|
use Doctrine\Migrations\AbstractMigration; |
||||||
|
|
||||||
|
/** |
||||||
|
* Remove deprecated home-curation kind-1 cache rows (no longer written or read). |
||||||
|
*/ |
||||||
|
final class Version20260428103000 extends AbstractMigration |
||||||
|
{ |
||||||
|
public function getDescription(): string |
||||||
|
{ |
||||||
|
return 'Delete event rows with storage_role magazine_curation_kind1_ref (curation strip is 30023-only)'; |
||||||
|
} |
||||||
|
|
||||||
|
public function up(Schema $schema): void |
||||||
|
{ |
||||||
|
$this->addSql("DELETE FROM event WHERE storage_role = 'magazine_curation_kind1_ref'"); |
||||||
|
} |
||||||
|
|
||||||
|
public function down(Schema $schema): void |
||||||
|
{ |
||||||
|
// Irreversible data purge; rows were ephemeral cache copies of relay notes. |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,58 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace App\Util; |
||||||
|
|
||||||
|
use App\Enum\KindsEnum; |
||||||
|
|
||||||
|
/** |
||||||
|
* NIP-51 kind 30004 (curation set) for the home strip: ordered `a` tags for kind **30023** only. |
||||||
|
* Other address kinds and `e` tags are ignored. |
||||||
|
*/ |
||||||
|
final class CurationSet30004Home |
||||||
|
{ |
||||||
|
/** |
||||||
|
* @param iterable<int, mixed> $tags Event tag rows (Nostr JSON shape) |
||||||
|
* |
||||||
|
* @return array{title: string, items: list<array{type: 'article', pk: string, slug: string}>} |
||||||
|
*/ |
||||||
|
public static function parseTitleAndOrderedRefs(iterable $tags): array |
||||||
|
{ |
||||||
|
$title = ''; |
||||||
|
$items = []; |
||||||
|
foreach ($tags as $tag) { |
||||||
|
if (!\is_array($tag) || $tag === []) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
$name = isset($tag[0]) ? strtolower((string) $tag[0]) : ''; |
||||||
|
$v = isset($tag[1]) ? (string) $tag[1] : ''; |
||||||
|
if ($v === '') { |
||||||
|
continue; |
||||||
|
} |
||||||
|
if ($name === 'title' && $title === '') { |
||||||
|
$title = trim($v); |
||||||
|
continue; |
||||||
|
} |
||||||
|
if ($name !== 'a') { |
||||||
|
continue; |
||||||
|
} |
||||||
|
$parts = explode(':', $v, 3); |
||||||
|
if (\count($parts) < 3) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
$kind = (int) $parts[0]; |
||||||
|
if ($kind !== KindsEnum::LONGFORM->value) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
$pk = strtolower(trim($parts[1])); |
||||||
|
$slug = trim($parts[2]); |
||||||
|
if (64 !== \strlen($pk) || !ctype_xdigit($pk) || $slug === '') { |
||||||
|
continue; |
||||||
|
} |
||||||
|
$items[] = ['type' => 'article', 'pk' => $pk, 'slug' => $slug]; |
||||||
|
} |
||||||
|
|
||||||
|
return ['title' => $title, 'items' => $items]; |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,44 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace App\Tests\Util; |
||||||
|
|
||||||
|
use App\Util\CurationSet30004Home; |
||||||
|
use PHPUnit\Framework\TestCase; |
||||||
|
|
||||||
|
final class CurationSet30004HomeTest extends TestCase |
||||||
|
{ |
||||||
|
public function testParsesTitleAndOrdered30023RefsOnly(): void |
||||||
|
{ |
||||||
|
$eid = 'd78ba0d5dce22bfff9db0a9e996c9ef27e2c91051de0c4e1da340e0326b4941e'; |
||||||
|
$tags = [ |
||||||
|
['d', 'jvdy9i4'], |
||||||
|
['title', 'Yaks'], |
||||||
|
['image', 'https://example.com/y.jpg'], |
||||||
|
['a', '30023:26dc95542e18b8b7aec2f14610f55c335abebec76f3db9e58c254661d0593a0c:slug-one'], |
||||||
|
['e', $eid], |
||||||
|
['a', '30024:26dc95542e18b8b7aec2f14610f55c335abebec76f3db9e58c254661d0593a0c:draft-only'], |
||||||
|
['a', '30023:26dc95542e18b8b7aec2f14610f55c335abebec76f3db9e58c254661d0593a0c:slug-two'], |
||||||
|
]; |
||||||
|
$out = CurationSet30004Home::parseTitleAndOrderedRefs($tags); |
||||||
|
self::assertSame('Yaks', $out['title']); |
||||||
|
self::assertSame([ |
||||||
|
['type' => 'article', 'pk' => '26dc95542e18b8b7aec2f14610f55c335abebec76f3db9e58c254661d0593a0c', 'slug' => 'slug-one'], |
||||||
|
['type' => 'article', 'pk' => '26dc95542e18b8b7aec2f14610f55c335abebec76f3db9e58c254661d0593a0c', 'slug' => 'slug-two'], |
||||||
|
], $out['items']); |
||||||
|
} |
||||||
|
|
||||||
|
public function testSkipsNon30023ATags(): void |
||||||
|
{ |
||||||
|
$tags = [ |
||||||
|
['a', '30040:26dc95542e18b8b7aec2f14610f55c335abebec76f3db9e58c254661d0593a0c:mag'], |
||||||
|
['a', '30023:26dc95542e18b8b7aec2f14610f55c335abebec76f3db9e58c254661d0593a0c:ok'], |
||||||
|
]; |
||||||
|
$out = CurationSet30004Home::parseTitleAndOrderedRefs($tags); |
||||||
|
self::assertSame('', $out['title']); |
||||||
|
self::assertCount(1, $out['items']); |
||||||
|
self::assertSame('article', $out['items'][0]['type']); |
||||||
|
self::assertSame('ok', $out['items'][0]['slug']); |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue