fix pictures 4

This commit is contained in:
2026-06-29 20:11:39 +03:30
parent b4585dd4f3
commit 9c7f44024d
3 changed files with 98 additions and 27 deletions
@@ -34,6 +34,9 @@ class FileController extends Controller
if (! $uploaded || ! $uploaded->isValid()) {
return response()->json(['message' => 'فایل بارگذاری نشد یا خالی است'], 422);
}
if ($uploaded->getSize() < 1) {
return response()->json(['message' => 'فایل خالی است'], 422);
}
$path = $this->createDir();
$data = $request->all();
@@ -53,6 +56,7 @@ class FileController extends Controller
$data['user_id'] = auth()->id();
$data['sts'] = 1;
$file_create = File::create($data);
$file_create->setAttribute('full_src', File::publicUrl($data['src']));
return response()->json($file_create->fresh());
}
+66 -16
View File
@@ -2,7 +2,6 @@
namespace App\Models;
use App\Models\Setting;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
@@ -52,7 +51,45 @@ class File extends Model
$trimmed = trim($path);
if (preg_match('/^https?:\/\//i', $trimmed)) {
return $trimmed;
return self::rewriteWrongPublicUrl($trimmed) ?? $trimmed;
}
$relative = self::normalizeRelativeStoragePath($trimmed);
$base = self::resolvePublicBaseUrl();
if ($base === '' || $relative === '') {
return null;
}
return $base.'/'.$relative;
}
/**
* مسیر نسبی داخل public/storage — حتی اگر قبلاً URL کامل اشتباه در DB ذخیره شده باشد.
*/
public static function normalizeRelativeStoragePath(string $path): string
{
$path = trim($path);
if (preg_match('#/storage/(.+)$#i', $path, $matches)) {
return 'storage/'.ltrim($matches[1], '/');
}
$relative = ltrim($path, '/');
if (! str_starts_with($relative, 'storage/')) {
$relative = 'storage/'.$relative;
}
return $relative;
}
/**
* اصلاح URLهای قدیمی: https://site.ir/storage/... → https://site.ir/server/storage/...
*/
public static function rewriteWrongPublicUrl(string $url): ?string
{
if (! preg_match('#^(https?://[^/]+)/storage/(.+)$#i', $url, $matches)) {
return null;
}
$base = self::resolvePublicBaseUrl();
@@ -60,36 +97,45 @@ class File extends Model
return null;
}
$relative = ltrim($trimmed, '/');
if (! str_starts_with($relative, 'storage/')) {
$relative = 'storage/'.$relative;
if (str_contains($base, '/server') && ! str_contains($matches[1].'/storage/', '/server/storage/')) {
return $base.'/storage/'.$matches[2];
}
return $base.'/'.$relative;
return null;
}
/**
* پایهٔ URL فایل‌های storage — cache-safe و مستقل از env() خام.
* پایهٔ URL فایل‌های storage.
*/
public static function resolvePublicBaseUrl(): string
{
$fromSettings = self::baseUrlFromSettings();
if ($fromSettings !== '') {
return $fromSettings;
}
$domain = config('app.domain');
if (is_string($domain) && $domain !== '' && ! str_contains($domain, 'localhost')) {
return rtrim($domain, '/');
return self::ensureServerPublicBase(rtrim($domain, '/'));
}
$appUrl = config('app.url');
if (is_string($appUrl) && $appUrl !== '' && ! str_contains($appUrl, 'localhost') && str_contains($appUrl, '/server')) {
return rtrim($appUrl, '/');
if (is_string($appUrl) && $appUrl !== '' && ! str_contains($appUrl, 'localhost')) {
return self::ensureServerPublicBase(rtrim($appUrl, '/'));
}
return '';
}
private static function baseUrlFromSettings(): string
{
try {
$rows = Setting::query()
->whereIn('key', ['bace_url', 'server_url', 'admin_url', 'app_url'])
->where('is_active', 1)
->pluck('value', 'key');
foreach (['bace_url', 'server_url'] as $key) {
foreach (['server_url', 'bace_url'] as $key) {
$value = trim((string) ($rows[$key] ?? ''));
if ($value !== '' && preg_match('/^https?:\/\//i', $value)) {
return rtrim($value, '/');
@@ -106,13 +152,17 @@ class File extends Model
return rtrim($siteUrl, '/').'/server';
}
} catch (\Throwable) {
// DB unavailable during deploy/migrate
}
if (is_string($appUrl) && $appUrl !== '' && ! str_contains($appUrl, 'localhost')) {
return rtrim($appUrl, '/');
}
return '';
}
private static function ensureServerPublicBase(string $base): string
{
if (preg_match('#/(server|api)(/|$)#i', $base)) {
return rtrim($base, '/');
}
return rtrim($base, '/').'/server';
}
}
+28 -11
View File
@@ -3,14 +3,24 @@
namespace Tests\Unit;
use App\Models\File;
use App\Models\Setting;
use Illuminate\Support\Facades\Config;
use Tests\TestCase;
class FilePublicUrlTest extends TestCase
{
public function test_public_url_uses_config_domain_with_storage_path(): void
private function seedGaroosiAdminUrl(): void
{
Config::set('app.domain', 'https://garoosigholaktala.ir/server');
Setting::query()->updateOrCreate(
['key' => 'admin_url'],
['value' => 'https://garoosigholaktala.ir/admin/', 'is_active' => 1, 'title_fa' => 'admin']
);
}
public function test_public_url_uses_server_storage_path_from_site_domain(): void
{
$this->seedGaroosiAdminUrl();
Config::set('app.domain', 'https://garoosigholaktala.ir');
$url = File::publicUrl('storage/1405/02/27/sample.jpg');
@@ -20,18 +30,25 @@ class FilePublicUrlTest extends TestCase
);
}
public function test_public_url_derives_server_base_from_admin_url_setting(): void
public function test_public_url_rewrites_legacy_wrong_absolute_url(): void
{
$this->seedGaroosiAdminUrl();
Config::set('app.domain', 'https://garoosigholaktala.ir');
$url = File::publicUrl('https://garoosigholaktala.ir/storage/1405/02/27/sample.jpg');
$this->assertSame(
'https://garoosigholaktala.ir/server/storage/1405/02/27/sample.jpg',
$url
);
}
public function test_resolve_base_from_admin_url_setting(): void
{
Config::set('app.domain', null);
Config::set('app.url', 'http://localhost');
$this->seedGaroosiAdminUrl();
\App\Models\Setting::query()->updateOrCreate(
['key' => 'admin_url'],
['value' => 'https://garoosigholaktala.ir/admin/', 'is_active' => 1, 'title_fa' => 'admin']
);
$base = File::resolvePublicBaseUrl();
$this->assertSame('https://garoosigholaktala.ir/server', $base);
$this->assertSame('https://garoosigholaktala.ir/server', File::resolvePublicBaseUrl());
}
}