feat: 外链中转 /go——点击统计 + 拦截 + 隐藏真实链接
- redirects 表 + UrlRedirector:go_url() 站外链接改写为 /go/{hash}(确定性 hash、同 URL 复用、并发安全、缓存)
- /go/{hash} 302 跳转 + 点击计数 + 最后点击时间;停用状态 410(二次拦截);后台「外链中转」资源可查看点击量/启停/删除
- 接入点:正文外链(渲染器统一改写)、评论正文自动链接(comment_body)、评论作者网址、友链(侧边栏+友链页);站内/相对/mailto/tel/锚点不中转
- 测试 6 个(改写/计数/拦截/复用/渲染/评论正文);README 补充
This commit is contained in:
@@ -55,6 +55,7 @@ class AdminPagesTest extends TestCase
|
||||
$this->actingAs($this->admin)->get('/admin/plugins')->assertOk();
|
||||
$this->actingAs($this->admin)->get('/admin/payments')->assertOk();
|
||||
$this->actingAs($this->admin)->get('/admin/ai-settings')->assertOk();
|
||||
$this->actingAs($this->admin)->get('/admin/redirects')->assertOk();
|
||||
}
|
||||
|
||||
public function test_guest_is_redirected_to_login(): void
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Blog\Services\PostContentRenderer;
|
||||
use App\Blog\Services\UrlRedirector;
|
||||
use App\Models\Post;
|
||||
use App\Models\Redirect;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class GoRedirectTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_go_url_rewrites_external_but_keeps_internal(): void
|
||||
{
|
||||
$go = app(UrlRedirector::class);
|
||||
|
||||
$this->assertStringContainsString('/go/', $go->goUrl('https://example.com/path'));
|
||||
$this->assertSame('http://laralog.test/posts/1', $go->goUrl('http://laralog.test/posts/1'));
|
||||
$this->assertSame('/posts/1', $go->goUrl('/posts/1'));
|
||||
$this->assertSame('mailto:a@b.com', $go->goUrl('mailto:a@b.com'));
|
||||
$this->assertSame('#anchor', $go->goUrl('#anchor'));
|
||||
}
|
||||
|
||||
public function test_go_redirect_counts_clicks(): void
|
||||
{
|
||||
$redirect = Redirect::create([
|
||||
'hash' => 'abc123def0',
|
||||
'target_url' => 'https://example.com/target',
|
||||
]);
|
||||
|
||||
$this->get('/go/abc123def0')
|
||||
->assertRedirect('https://example.com/target');
|
||||
|
||||
$redirect->refresh();
|
||||
$this->assertSame(1, $redirect->clicks);
|
||||
$this->assertNotNull($redirect->last_clicked_at);
|
||||
}
|
||||
|
||||
public function test_disabled_redirect_returns_410(): void
|
||||
{
|
||||
Redirect::create([
|
||||
'hash' => 'blocked0000',
|
||||
'target_url' => 'https://example.com/bad',
|
||||
'status' => 'disabled',
|
||||
]);
|
||||
|
||||
$this->get('/go/blocked0000')->assertStatus(410);
|
||||
}
|
||||
|
||||
public function test_same_url_reuses_same_hash(): void
|
||||
{
|
||||
$a = app(UrlRedirector::class)->goUrl('https://example.com/dup');
|
||||
$b = app(UrlRedirector::class)->goUrl('https://example.com/dup');
|
||||
|
||||
$this->assertSame($a, $b);
|
||||
$this->assertSame(1, Redirect::query()->where('target_url', 'https://example.com/dup')->count());
|
||||
}
|
||||
|
||||
public function test_post_render_rewrites_external_links(): void
|
||||
{
|
||||
$post = Post::create([
|
||||
'title' => '外链测试',
|
||||
'slug' => 'go-test',
|
||||
'content' => "正文 <a href=\"https://example.com/x\" target=\"_blank\">外链</a> 和 <a href=\"http://laralog.test/posts/1\">站内</a>",
|
||||
'content_format' => 'html',
|
||||
'status' => 'published',
|
||||
'published_at' => now(),
|
||||
]);
|
||||
|
||||
$html = app(PostContentRenderer::class)->render($post);
|
||||
|
||||
$this->assertStringContainsString('href="http://laralog.test/go/', $html);
|
||||
$this->assertStringContainsString('href="http://laralog.test/posts/1"', $html);
|
||||
$this->assertStringNotContainsString('href="https://example.com/x"', $html);
|
||||
}
|
||||
|
||||
public function test_comment_body_autolinks_and_redirects(): void
|
||||
{
|
||||
$html = comment_body('看看 https://example.com/note 这个链接');
|
||||
|
||||
$this->assertStringContainsString('href="http://laralog.test/go/', $html);
|
||||
$this->assertStringContainsString('rel="nofollow noopener"', $html);
|
||||
$this->assertStringNotContainsString('href="https://example.com/note"', $html);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user