From a77bc849c31457aa374855958c1ec4644fa17aef Mon Sep 17 00:00:00 2001 From: blued_gear Date: Wed, 3 Jun 2026 21:43:41 +0000 Subject: [PATCH 01/15] add ability to store instance blocks --- config/mbin_routes/federation.yaml | 14 +++++ config/mbin_routes/page.yaml | 5 -- migrations/Version20260603205601.php | 35 ++++++++++++ src/Controller/FederationController.php | 60 ++++++++++++++++++-- src/Entity/InstanceBlock.php | 35 ++++++++++++ src/Repository/InstanceBlockRepository.php | 37 ++++++++++++ src/Service/InstanceManager.php | 17 ++++++ templates/components/instance_list.html.twig | 24 ++++++++ templates/page/federation.html.twig | 6 +- translations/messages.en.yaml | 3 + 10 files changed, 223 insertions(+), 13 deletions(-) create mode 100644 config/mbin_routes/federation.yaml create mode 100644 migrations/Version20260603205601.php create mode 100644 src/Entity/InstanceBlock.php create mode 100644 src/Repository/InstanceBlockRepository.php diff --git a/config/mbin_routes/federation.yaml b/config/mbin_routes/federation.yaml new file mode 100644 index 0000000000..7edec9a525 --- /dev/null +++ b/config/mbin_routes/federation.yaml @@ -0,0 +1,14 @@ +page_federation: + controller: App\Controller\FederationController + path: /federation + methods: [ GET ] + +federation_user_block_instance: + controller: App\Controller\FederationController::userBlockInstance + path: /federation/user/block + methods: [GET] + +federation_user_unblock_instance: + controller: App\Controller\FederationController::userUnblockInstance + path: /federation/user/unblock + methods: [GET] diff --git a/config/mbin_routes/page.yaml b/config/mbin_routes/page.yaml index 760d6b9518..21c796fb3c 100644 --- a/config/mbin_routes/page.yaml +++ b/config/mbin_routes/page.yaml @@ -24,11 +24,6 @@ stats: path: /stats/{statsType}/{statsPeriod}/{withFederated} methods: [ GET ] -page_federation: - controller: App\Controller\FederationController - path: /federation - methods: [ GET ] - redirect_instances: controller: Symfony\Bundle\FrameworkBundle\Controller\RedirectController path: /instances diff --git a/migrations/Version20260603205601.php b/migrations/Version20260603205601.php new file mode 100644 index 0000000000..d1523a4cfe --- /dev/null +++ b/migrations/Version20260603205601.php @@ -0,0 +1,35 @@ +addSql('CREATE SEQUENCE instance_block_id_seq INCREMENT BY 1 MINVALUE 1 START 1'); + $this->addSql('CREATE TABLE instance_block (id INT NOT NULL, user_id INT NOT NULL, instance_id INT NOT NULL, PRIMARY KEY (id))'); + $this->addSql('CREATE INDEX IDX_8F85864AA76ED395 ON instance_block (user_id)'); + $this->addSql('CREATE INDEX IDX_8F85864A3A51721D ON instance_block (instance_id)'); + $this->addSql('CREATE UNIQUE INDEX instance_block_idx ON instance_block (user_id, instance_id)'); + $this->addSql('ALTER TABLE instance_block ADD CONSTRAINT FK_8F85864AA76ED395 FOREIGN KEY (user_id) REFERENCES "user" (id) ON DELETE CASCADE NOT DEFERRABLE'); + $this->addSql('ALTER TABLE instance_block ADD CONSTRAINT FK_8F85864A3A51721D FOREIGN KEY (instance_id) REFERENCES instance (id) ON DELETE CASCADE NOT DEFERRABLE'); + } + + public function down(Schema $schema): void + { + $this->addSql('DROP SEQUENCE instance_block_id_seq CASCADE'); + $this->addSql('ALTER TABLE instance_block DROP CONSTRAINT FK_8F85864AA76ED395'); + $this->addSql('ALTER TABLE instance_block DROP CONSTRAINT FK_8F85864A3A51721D'); + $this->addSql('DROP TABLE instance_block'); + } +} diff --git a/src/Controller/FederationController.php b/src/Controller/FederationController.php index 8c7beb8399..041c634590 100644 --- a/src/Controller/FederationController.php +++ b/src/Controller/FederationController.php @@ -4,22 +4,43 @@ namespace App\Controller; +use App\Repository\InstanceBlockRepository; use App\Repository\InstanceRepository; +use App\Service\InstanceManager; use App\Service\SettingsManager; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\Attribute\MapQueryParameter; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; class FederationController extends AbstractController { - public function __invoke(InstanceRepository $instanceRepository, SettingsManager $settings, Request $request): Response + + public function __construct( + private readonly InstanceRepository $instanceRepository, + private readonly InstanceBlockRepository $instanceBlockRepository, + private readonly InstanceManager $instanceManager, + private readonly SettingsManager $settings, + ){} + + public function __invoke( + Request $request + ): Response { - if (!$settings->get('KBIN_FEDERATION_PAGE_ENABLED')) { + if (!$this->settings->get('KBIN_FEDERATION_PAGE_ENABLED')) { return $this->redirectToRoute('front'); } - $allowedInstances = $instanceRepository->getAllowedInstances($settings->getUseAllowList()); - $defederatedInstances = $instanceRepository->getBannedInstances(); - $deadInstances = $instanceRepository->getDeadInstances(); + $allowedInstances = $this->instanceRepository->getAllowedInstances($this->settings->getUseAllowList()); + $defederatedInstances = $this->instanceRepository->getBannedInstances(); + $deadInstances = $this->instanceRepository->getDeadInstances(); + + $user = $this->getUser(); + if(null !== $user) { + $userInstanceBlocks = $this->instanceBlockRepository->findBlocksForUser($user); + } else { + $userInstanceBlocks = null; + } return $this->render( 'page/federation.html.twig', @@ -27,7 +48,36 @@ public function __invoke(InstanceRepository $instanceRepository, SettingsManager 'allowedInstances' => $allowedInstances, 'defederatedInstances' => $defederatedInstances, 'deadInstances' => $deadInstances, + 'userInstanceBlocks' => $userInstanceBlocks, ] ); } + + public function userBlockInstance(#[MapQueryParameter] string $instanceDomain): Response + { + $user = $this->getUserOrThrow(); + $instance = $this->instanceRepository->findOneBy(['domain' => $instanceDomain]); + + if(null === $instance) { + throw new NotFoundHttpException('instance '.$instanceDomain.' not found'); + } + + $this->instanceManager->blockInstance($instance, $user); + + return $this->redirectToRoute('page_federation'); + } + + public function userUnblockInstance(#[MapQueryParameter] string $instanceDomain): Response + { + $user = $this->getUserOrThrow(); + $instance = $this->instanceRepository->findOneBy(['domain' => $instanceDomain]); + + if(null === $instance) { + throw new NotFoundHttpException('instance '.$instanceDomain.' not found'); + } + + $this->instanceManager->unblockInstance($instance, $user); + + return $this->redirectToRoute('page_federation'); + } } diff --git a/src/Entity/InstanceBlock.php b/src/Entity/InstanceBlock.php new file mode 100644 index 0000000000..3510319ffb --- /dev/null +++ b/src/Entity/InstanceBlock.php @@ -0,0 +1,35 @@ +user = $user; + $this->instance = $instance; + } + + #[Column, Id, GeneratedValue] + private int $id; + + #[ManyToOne(targetEntity: User::class)] + #[JoinColumn(nullable: false, onDelete: 'CASCADE')] + public User $user; + + #[ManyToOne(targetEntity: Instance::class)] + #[JoinColumn(nullable: false, onDelete: 'CASCADE')] + public Instance $instance; + +} \ No newline at end of file diff --git a/src/Repository/InstanceBlockRepository.php b/src/Repository/InstanceBlockRepository.php new file mode 100644 index 0000000000..ac3eed82c8 --- /dev/null +++ b/src/Repository/InstanceBlockRepository.php @@ -0,0 +1,37 @@ +createQueryBuilder('b') + ->where('b.user = :user') + ->setParameter('user', $user) + ->getQuery()->getResult(); + } + + public function findByUserAndInstance(User $user, Instance $instance): ?InstanceBlock { + return $this->createQueryBuilder('b') + ->where('b.user = :user')->andWhere('b.instance = :instance') + ->setParameter('user', $user)->setParameter('instance', $instance) + ->getQuery()->getOneOrNullResult(); + } +} diff --git a/src/Service/InstanceManager.php b/src/Service/InstanceManager.php index e3053df493..7a730d708b 100644 --- a/src/Service/InstanceManager.php +++ b/src/Service/InstanceManager.php @@ -6,7 +6,9 @@ use App\DTO\ModeratorDto; use App\Entity\Instance; +use App\Entity\InstanceBlock; use App\Entity\User; +use App\Repository\InstanceBlockRepository; use App\Repository\InstanceRepository; use Doctrine\ORM\EntityManagerInterface; @@ -16,6 +18,7 @@ public function __construct( private EntityManagerInterface $entityManager, private SettingsManager $settingsManager, private InstanceRepository $instanceRepository, + private InstanceBlockRepository $instanceBlockRepository, ) { } @@ -93,4 +96,18 @@ public function denyInstanceFederation(Instance $instance): void $this->entityManager->flush(); } + + public function blockInstance(Instance $instance, User $user): void { + $block = new InstanceBlock($user, $instance); + $this->entityManager->persist($block); + $this->entityManager->flush(); + } + + public function unblockInstance(Instance $instance, User $user): void { + $block = $this->instanceBlockRepository->findByUserAndInstance($user, $instance); + if(null !== $block) { + $this->entityManager->remove($block); + $this->entityManager->flush(); + } + } } diff --git a/templates/components/instance_list.html.twig b/templates/components/instance_list.html.twig index 0994a67ab2..95eec63557 100644 --- a/templates/components/instance_list.html.twig +++ b/templates/components/instance_list.html.twig @@ -4,6 +4,9 @@ {{ 'domain'|trans }} {{ 'server_software'|trans }} {{ 'version'|trans }} + {% if blockedInstances is defined and blockedInstances is not same as null %} + + {% endif %} {% if app.user is defined and app.user is not same as null and app.user.admin %} {{ 'last_successful_deliver'|trans }} {{ 'last_successful_receive'|trans }} @@ -19,6 +22,27 @@ {{instance.domain}} {{ instance.software ?? '' }} {{ instance.version ?? '' }} + + {% if blockedInstances is defined and blockedInstances is not same as null %} + + {% if blockedInstances|find(b => b.instance === instance) %} +
+ + +
+ {% else %} +
+ + +
+ {% endif %} + + {% endif %} + {% if app.user is defined and app.user is not same as null and app.user.admin %} {% if instance.lastSuccessfulDeliver is not same as null %} diff --git a/templates/page/federation.html.twig b/templates/page/federation.html.twig index 437d574dad..4d950918e8 100644 --- a/templates/page/federation.html.twig +++ b/templates/page/federation.html.twig @@ -32,7 +32,7 @@

{{'federation_page_allowed_description'|trans}}

{% if allowedInstances is not empty %} - {{ component('instance_list', {'instances': allowedInstances}) }} + {{ component('instance_list', {'instances': allowedInstances, 'blockedInstances': userInstanceBlocks}) }} {% else %}