162 lines
5.9 KiB
PHP
162 lines
5.9 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\Post;
|
|
use App\Models\Thread;
|
|
use App\Models\User;
|
|
use App\Models\Attachment;
|
|
use App\Models\Setting;
|
|
use Illuminate\Support\Carbon;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use Illuminate\Http\JsonResponse;
|
|
|
|
class StatsController extends Controller
|
|
{
|
|
public function __invoke(): JsonResponse
|
|
{
|
|
$threadsCount = Thread::query()->withoutTrashed()->count();
|
|
$postsCount = Post::query()->withoutTrashed()->count();
|
|
$usersCount = User::query()->count();
|
|
$attachmentsCount = Attachment::query()->withoutTrashed()->count();
|
|
$attachmentsSizeBytes = (int) Attachment::query()->withoutTrashed()->sum('size_bytes');
|
|
|
|
$boardStartedAt = $this->resolveBoardStartedAt();
|
|
$daysSinceStart = $boardStartedAt
|
|
? max(1, Carbon::parse($boardStartedAt)->diffInSeconds(now()) / 86400)
|
|
: null;
|
|
|
|
$dbSizeBytes = $this->resolveDatabaseSize();
|
|
$dbServer = $this->resolveDatabaseServer();
|
|
$avatarSizeBytes = $this->resolveAvatarDirectorySize();
|
|
$orphanAttachments = $this->resolveOrphanAttachments();
|
|
|
|
$version = Setting::query()->where('key', 'version')->value('value');
|
|
$build = Setting::query()->where('key', 'build')->value('value');
|
|
$boardVersion = $version
|
|
? ($build ? "{$version} (build {$build})" : $version)
|
|
: null;
|
|
|
|
return response()->json([
|
|
'threads' => $threadsCount,
|
|
'posts' => $postsCount + $threadsCount,
|
|
'users' => $usersCount,
|
|
'attachments' => $attachmentsCount,
|
|
'board_started_at' => $boardStartedAt,
|
|
'attachments_size_bytes' => $attachmentsSizeBytes,
|
|
'avatar_directory_size_bytes' => $avatarSizeBytes,
|
|
'database_size_bytes' => $dbSizeBytes,
|
|
'database_server' => $dbServer,
|
|
'gzip_compression' => $this->resolveGzipCompression(),
|
|
'php_version' => PHP_VERSION,
|
|
'orphan_attachments' => $orphanAttachments,
|
|
'board_version' => $boardVersion,
|
|
'posts_per_day' => $daysSinceStart ? ($postsCount + $threadsCount) / $daysSinceStart : null,
|
|
'topics_per_day' => $daysSinceStart ? $threadsCount / $daysSinceStart : null,
|
|
'users_per_day' => $daysSinceStart ? $usersCount / $daysSinceStart : null,
|
|
'attachments_per_day' => $daysSinceStart ? $attachmentsCount / $daysSinceStart : null,
|
|
]);
|
|
}
|
|
|
|
private function resolveBoardStartedAt(): ?string
|
|
{
|
|
$timestamps = [
|
|
User::query()->min('created_at'),
|
|
Thread::query()->min('created_at'),
|
|
Post::query()->min('created_at'),
|
|
];
|
|
|
|
$min = null;
|
|
foreach ($timestamps as $value) {
|
|
if (!$value) {
|
|
continue;
|
|
}
|
|
$time = Carbon::parse($value)->timestamp;
|
|
if ($min === null || $time < $min) {
|
|
$min = $time;
|
|
}
|
|
}
|
|
|
|
return $min !== null ? Carbon::createFromTimestamp($min)->toIso8601String() : null;
|
|
}
|
|
|
|
private function resolveDatabaseSize(): ?int
|
|
{
|
|
try {
|
|
$driver = DB::connection()->getDriverName();
|
|
if ($driver === 'mysql') {
|
|
$row = DB::selectOne('SELECT SUM(data_length + index_length) AS size FROM information_schema.tables WHERE table_schema = DATABASE()');
|
|
return $row && isset($row->size) ? (int) $row->size : null;
|
|
}
|
|
} catch (\Throwable) {
|
|
return null;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private function resolveDatabaseServer(): ?string
|
|
{
|
|
try {
|
|
$row = DB::selectOne('SELECT VERSION() AS version');
|
|
return $row && isset($row->version) ? (string) $row->version : null;
|
|
} catch (\Throwable) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private function resolveAvatarDirectorySize(): ?int
|
|
{
|
|
try {
|
|
$disk = Storage::disk('public');
|
|
$files = $disk->allFiles('avatars');
|
|
$total = 0;
|
|
foreach ($files as $file) {
|
|
$total += $disk->size($file);
|
|
}
|
|
return $total;
|
|
} catch (\Throwable) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private function resolveOrphanAttachments(): int
|
|
{
|
|
try {
|
|
return (int) DB::table('attachments')
|
|
->leftJoin('threads', 'attachments.thread_id', '=', 'threads.id')
|
|
->leftJoin('posts', 'attachments.post_id', '=', 'posts.id')
|
|
->whereNull('attachments.deleted_at')
|
|
->where(function ($query) {
|
|
$query
|
|
->whereNull('attachments.thread_id')
|
|
->whereNull('attachments.post_id')
|
|
->orWhere(function ($inner) {
|
|
$inner->whereNotNull('attachments.thread_id')
|
|
->where(function ($inner2) {
|
|
$inner2->whereNull('threads.id')
|
|
->orWhereNotNull('threads.deleted_at');
|
|
});
|
|
})
|
|
->orWhere(function ($inner) {
|
|
$inner->whereNotNull('attachments.post_id')
|
|
->where(function ($inner2) {
|
|
$inner2->whereNull('posts.id')
|
|
->orWhereNotNull('posts.deleted_at');
|
|
});
|
|
});
|
|
})
|
|
->count();
|
|
} catch (\Throwable) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
private function resolveGzipCompression(): bool
|
|
{
|
|
$value = ini_get('zlib.output_compression');
|
|
return in_array(strtolower((string) $value), ['1', 'on', 'true'], true);
|
|
}
|
|
}
|