Skip to content

Commit

Permalink
feat: ip detail
Browse files Browse the repository at this point in the history
  • Loading branch information
Grafikart committed Feb 24, 2025
1 parent 6c84a54 commit e9b9643
Show file tree
Hide file tree
Showing 7 changed files with 269 additions and 2 deletions.
15 changes: 15 additions & 0 deletions src/Domain/Auth/UserRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,21 @@ public function findPremiumDiscordIds(): array
->getResult());
}

/**
* Renvoie la liste des utilisateurs connectés depuis une certaine IP.
*
* @return User[]
*/
public function findByIp(string $ip): array
{
return $this->createQueryBuilder('u')
->where('u.lastLoginIp = :ip')
->setMaxResults(15)
->setParameter('ip', $ip)
->getQuery()
->getResult();
}

/**
* Liste les utilisateurs bannis.
*/
Expand Down
12 changes: 12 additions & 0 deletions src/Domain/Forum/Repository/MessageRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,18 @@ public function deleteForUser(User $user): void
->execute();
}

public function findLastByUsers(array $users, int $limit = 10): array
{
return $this->createQueryBuilder('m')
->where('m.author IN (:users)')
->orderBy('m.updatedAt', 'DESC')
->setMaxResults($limit)
->setParameter('users', $users)
->getQuery()
->getResult()
;
}

/**
* Force l'hydratation des messages (pour éviter de faire n+1 requêtes).
*/
Expand Down
14 changes: 14 additions & 0 deletions src/Domain/Forum/Repository/TopicRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,20 @@ public function findLastByUser(User $user): array
->getResult();
}

/**
* Récupère les derniers sujets créés par des utilisateurs.
*/
public function findLastByUsers(array $users, int $limit = 10): array
{
return $this->createQueryBuilder('t')
->where('t.author IN (:users)')
->orderBy('t.updatedAt', 'DESC')
->setMaxResults($limit)
->setParameter('users', $users)
->getQuery()
->getResult();
}

/**
* Récupère les derniers sujets sur lesquels l'utilisateur a participé.
*
Expand Down
68 changes: 68 additions & 0 deletions src/Http/Admin/Controller/IpController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

namespace App\Http\Admin\Controller;

use App\Domain\Auth\Service\UserBanService;
use App\Domain\Auth\UserRepository;
use App\Domain\Comment\CommentRepository;
use App\Domain\Forum\Repository\MessageRepository;
use App\Domain\Forum\Repository\TopicRepository;
use App\Infrastructure\Spam\GeoIpService;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

class IpController extends BaseController
{
public function __construct(
private readonly UserRepository $userRepository,
private readonly CommentRepository $commentRepository,
private readonly GeoIpService $ipService,
private readonly TopicRepository $topicRepository,
private readonly MessageRepository $messageRepository,
private readonly UserBanService $banService,
private readonly EntityManagerInterface $em,
) {
}

#[Route(path: '/ip/{ip}', name: 'ip', methods: ['GET'])]
public function ip(
string $ip,
): Response {
$comments = $this->commentRepository
->queryByIp($ip)
->setMaxResults(15)
->getQuery()
->getResult();
$location = $this->ipService->getLocation($ip);
$users = $this->userRepository->findByIp($ip);
$topics = $this->topicRepository->findLastByUsers($users);
$messages = $this->messageRepository->findLastByUsers($users);

return $this->render('admin/spam/ip.html.twig', [
'ip' => $ip,
'comments' => $comments,
'location' => $location,
'users' => $users,
'topics' => $topics,
'messages' => $messages,
]);
}

#[Route(path: '/ip/{ip}', methods: ['DELETE'])]
public function block(string $ip): RedirectResponse
{
$this->commentRepository->queryByIp($ip)->delete()->getQuery()->execute();
$users = $this->userRepository->findByIp($ip);
foreach ($users as $user) {
if (!$user->isPremium()) {
$this->banService->ban($user);
}
}
$this->em->flush();
$this->addFlash('success', "L'ip a bien été bloquée");

return $this->redirectBack('admin_ip', ['ip' => $ip]);
}
}
2 changes: 1 addition & 1 deletion templates/admin/comment/_comment.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
</h3>
<div class="hstack" style="--gap: 1">
{% if comment.ip %}
<a href="{{ path('admin_comment_index', {ip: comment.ip}) }}" target="_blank" class="text-small text-muted">
<a href="{{ path('admin_ip', {ip: comment.ip}) }}" target="_blank" class="text-small text-muted">
{{ comment.ip }}
</a>
{% endif %}
Expand Down
158 changes: 158 additions & 0 deletions templates/admin/spam/ip.html.twig
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
{% extends 'admin/base.html.twig' %}

{% block title 'IP ' ~ ip %}

{% block body %}

<div class="stack" style="--gap:1">

<div class="flex mb1">
<h1 class="dashboard-title">
IP
<a class="text-small text-muted" href="https://ipinfo.io/{{ ip }}" target="_blank" rel="noreferrer">
{{ ip }}
</a>
{% if location %}
<span title="{{ location.country }}">
{{ location.emoji ?? '' }}
</span>
{% endif %}
</h1>
<aside class="hstack center" style="--gap:1;">
<pre style="font-size: .8rem; background-color:rgba(0,0,0,0.6); padding: 5px 10px;">sudo ufw insert 1 deny from {{ ip }}</pre>
<form action="{{ path('admin_ip', {ip: ip}) }}" method="post">
<input type="hidden" name="_method" value="DELETE">
<button class="btn-danger">
{{ icon('eye') }} Bloquer
</button>
</form>

</aside>
</div>

{% include 'partials/flash.html.twig' %}

<div class="grid2" style="--gap: 4">

<div>
<h2 class="h3 mb2">
{{ icon('user') }}
Inscriptions
</h2>
<div class="dashboard-card">
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>Pseudo</th>
<th>Email</th>
<th>Inscription</th>
<th>Premium</th>
</tr>
</thead>
<tbody>
{% for user in users %}
<tr>
<td>
{{ user.id }}
</td>
<td>
{{ user.username }}
</td>
<td>
{{ user.email }}
</td>
<td>
{{ user.createdAt | format_datetime(pattern="dd MMM. yyyy à kk:mm") }}
</td>
<td>
{{ user.premium ? user.premiumEnd | ago('Pour') : '' }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>

<div>
<h2 class="h3 mb2">
{{ icon('comments') }}
Commentaires
</h2>
<div class="dashboard-card stack-large">
{% for comment in comments %}
<article class="dashboard-comment stack" style="--gap:1">
<div class="flex">
<h3>
<strong>
{%- if comment.author -%}
<a href="{{ path('admin_user_index', {q: comment.author ? comment.author.id : '#'}) }}">{{ icon('user') }} {{ comment.username }}</a>
{%- else -%}
{{- comment.username -}}
{%- endif -%}
</strong>,
<a href="{{ path(comment.target) }}">
{{ comment.target.title }}
</a>,
<span class="text-small text-muted">
{{ comment.createdAt | ago }}
</span>
<a class="text-small text-muted" href="{{ path('admin_comment_edit', {id: comment.id}) }}">Editer</a>
</h3>
</div>
<p>{{ comment.content | nl2br | highlight_spamwords(true) }}</p>
</article>
{% endfor %}
</div>
</div>

<div>
<h2 class="h3 mb2">
{{ icon('edit') }}
Sujets créés
</h2>
<div class="dashboard-card stack-separated">
{% for topic in topics %}
<article class="dashboard-comment stack" style="--gap:1">
<div class="flex">
<h3>
<a href="{{ path(topic) }}">
{{ topic.name }}
</a>, {{ topic.author.username }}
</h3>
</div>
<p>{{ topic.content | markdown_excerpt }}</p>
</article>
{% endfor %}
</div>
</div>

<div>
<h2 class="h3 mb2">
{{ icon('edit') }}
Sujets créés
</h2>
<div class="dashboard-card stack-separated">
{% for message in messages %}
<article class="dashboard-comment stack" style="--gap:1">
<div class="flex">
<h3>
<a href="{{ path(message) }}">
{{ message.topic.name }}
</a>, {{ message.author.username }}
</h3>
</div>
<p>{{ message.content | markdown_excerpt }}</p>
</article>
{% endfor %}
</div>
</div>
</div>

{% block bottom '' %}

</div>

{% endblock %}

2 changes: 1 addition & 1 deletion templates/admin/user/index.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@
{{ user.premium ? user.premiumEnd | ago('Pour') : '' }}
</td>
<td>
<a href="https://ipinfo.io/{{ user.lastLoginIp }}" title="" target="_blank">{{ user.lastLoginIp }}</a>
{% if user.lastLoginIp %}
<a href="{{ path('admin_ip', {ip: user.lastLoginIp}) }}" title="" target="_blank">{{ user.lastLoginIp }}</a>
{% set location = ipService.getLocation(user.lastLoginIp) %}
{% if location %}
<span title="{{ location.country }}">
Expand Down

0 comments on commit e9b9643

Please sign in to comment.