Looking to hire Laravel developers? Try LaraJobs

cdn-laravel-adapter maintained by snowsoft

Description
Laravel Storage adapter for CDN Services Node.js backend
Author
Last update
2026/03/18 11:48 (dev-main)
License
Downloads
127

Comments
comments powered by Disqus

CDN Services Laravel Adapter

Latest Version on Packagist Total Downloads PHP from Packagist

Laravel Storage adapter for CDN Services Node.js backend. Use CDN Services as a Laravel filesystem disk (Laravel 9, 10, 11, 12). Domain Driven Design (DDD) uyumlu yapı: Domain (value objects, port interfaces), Application (use case services), Infrastructure (HTTP gateway).

Özellikler

Özellik Açıklama
Storage disk Storage::disk('cdn-services') ile put/get/delete/url, caption, tags, folder, visibility
CdnServicesAuth Kayıt (registration token), giriş, tokenForUser ile JWT
Depolama kotası QuotaExceededException, getQuotaRemaining(), usage() ile quotaBytes/quotaMB
CdnServicesPdf PDF yükleme, HTML'den PDF oluşturma (generateFromHtml), session ile süreli erişim, DDD (PdfDocument, PdfSession)
CdnServicesApi Meta, list, usage, import, placeholder, signed URL, processedUrl, Cloudflare cloudflareProcessedUrl
CdnApi / CdnBulk Toplu işlem: CdnApi::upload, importBatch, bulkDelete; CdnBulk::uploadMany; Artisan: cdn:bulk-upload, cdn:import-urls, cdn:bulk-delete
CdnMinify JS/CSS sıkıştırma (minify), publish (ledger + kalıcı asset), ledger doğrulama; DDD (MinifyPublishResult, LedgerVerification).

DDD yapısı

Katman Açıklama
Domain Domain\Pdf\PdfDocument, PdfSession (value objects); PdfStorageGatewayInterface (port). Minify: Domain\Minify\MinifyPublishResult, LedgerVerification; MinifyGatewayInterface (port).
Application Application\Pdf\PdfStorageService – PDF use case'leri. Minify: Application\Minify\MinifyService – minifyJs, minifyCss, publishJs, publishCss, assetUrl, verifyLedger.
Infrastructure Infrastructure\Http\PdfStorageGateway – backend /api/pdf/*. Minify: Infrastructure\Http\MinifyGateway – backend /api/minify/*.
Bulk (Contracts/Services) Contracts\CdnApiClientInterface, CdnBulkUploadServiceInterface; Services\CdnApiClient, CdnBulkUploadService; DTOs: BulkUploadResult, BatchImportResult, BulkDeleteResult.

Bağımlılık Domain → Application → Infrastructure yönünde; uygulama PdfStorageGatewayInterface / MinifyGatewayInterface ile tip bağımlılığı kurar, implementasyon ServiceProvider'da bağlanır.

Minify (DDD)

Backend’de Minify servisi açıksa (MINIFY_SERVICE_URL), CdnMinify facade ile JS/CSS sıkıştırma, yayınlama (ledger + asset) ve ledger doğrulama kullanılır:

use CdnServices\Facades\CdnMinify;
use CdnServices\Domain\Minify\MinifyPublishResult;

// Sadece sıkıştır (kaydetmez)
$minifiedJs = CdnMinify::minifyJs($rawJs);
$minifiedCss = CdnMinify::minifyCss($rawCss);

// Yayınla: sıkıştır + ledger'a yaz + kalıcı asset URL
$result = CdnMinify::publishJs($rawJs);  // MinifyPublishResult: assetId, url, kind, size
$result = CdnMinify::publishCss($rawCss);

// Asset erişim URL'i (script/link ile kullanılır)
$url = CdnMinify::assetUrl($result->assetId);

// Ledger zincir doğrulama
$verification = CdnMinify::verifyLedger();  // LedgerVerification: valid, message, entries

if (CdnMinify::isAvailable()) {
    // Minify servisi kullanılabilir
}

Toplu işlem (DDD: CdnApi, CdnBulk, Artisan)

Servisler: CdnApiClientInterface (upload, importBatch, bulkDelete), CdnBulkUploadServiceInterface (uploadMany). Facades: CdnApi, CdnBulk (alias'ları eklemek için config/app.php veya composer extra.laravel.aliases içine CdnApiCdnServices\Facades\CdnApi, CdnBulkCdnServices\Facades\CdnBulk ekleyin).

Artisan komutları:

# Toplu dosya yükleme (dizin veya tek dosya)
php artisan cdn:bulk-upload /path/to/products --bucket=local --folder=ürünler --tags=ürün,yeni

# URL listesinden import (--file= veya argüman)
php artisan cdn:import-urls --file=urls.txt --bucket=local

# Toplu silme
php artisan cdn:bulk-delete --file=ids.txt

Kod örneği:

use CdnServices\Contracts\CdnBulkUploadServiceInterface;
use CdnServices\Facades\CdnApi;
use CdnServices\DTOs\BatchImportResult;
use CdnServices\DTOs\BulkDeleteResult;

// Toplu yükleme (controller'da inject)
$result = app(CdnBulkUploadServiceInterface::class)->uploadMany(
    ['/path/to/1.jpg', '/path/to/2.png'],
    ['bucket' => 'local', 'folder' => 'ürünler']
);
$result->uploadedIds();

// URL import
$res = CdnApi::importBatch(['https://example.com/1.jpg'], ['bucket' => 'local']);
$result = BatchImportResult::fromApiResponse($res);

// Toplu silme
$res = CdnApi::bulkDelete(['id1', 'id2']);
$result = BulkDeleteResult::fromApiResponse($res);

Requirements

  • PHP 8.0+
  • Laravel 9.x, 10.x, 11.x or 12.x
  • CDN Services Node.js backend

Installation

Install via Composer (Packagist):

composer require snowsoft/cdn-laravel-adapter

Service provider and aliases are auto-discovered. Publish config:

php artisan vendor:publish --tag=cdn-services-config

Install from GitHub (development)

Add the VCS repository to composer.json, then require the package:

{
    "repositories": [
        {
            "type": "vcs",
            "url": "https://github.com/snowsoft/cdn-laravel-adapter"
        }
    ]
}
composer require snowsoft/cdn-laravel-adapter

Manual install

  1. Clone the repo into your project (e.g. packages/cdn-laravel-adapter).
  2. Add PSR-4 autoload: "CdnServices\\": "packages/cdn-laravel-adapter/src/".
  3. Register CdnServices\CdnServicesServiceProvider in config/app.php or bootstrap/providers.php.
  4. Run php artisan vendor:publish --tag=cdn-services-config.

Configuration

Environment (.env)

CDN_SERVICES_BASE_URL=http://localhost:3012
CDN_SERVICES_TOKEN=your-jwt-token-here
# Backend'de REGISTRATION_TOKEN zorunluysa; kayıt için (CdnServicesAuth::register)
CDN_SERVICES_REGISTRATION_TOKEN=
CDN_SERVICES_DISK=local
CDN_SERVICES_DEFAULT_DISK=local
CDN_SERVICES_TIMEOUT=30

Filesystem (config/filesystems.php)

'disks' => [
    // ...
    'cdn-services' => [
        'driver' => 'cdn-services',
        'base_url' => env('CDN_SERVICES_BASE_URL', 'http://localhost:3012'),
        'token' => env('CDN_SERVICES_TOKEN'),
        'disk' => env('CDN_SERVICES_DISK', 'local'),
    ],
],

Usage

Storage facade

use Illuminate\Support\Facades\Storage;

// Upload (optional: caption, tags, folder, visibility)
Storage::disk('cdn-services')->put('images/photo.jpg', $fileContents, [
    'caption' => 'Açıklama',
    'tags' => ['ürün', 'kampanya'],
    'folder' => 'galeri',
    'visibility' => 'public', // public|private|unlisted
]);

// Read, exists, delete, url, copy, move
$contents = Storage::disk('cdn-services')->get('images/photo.jpg');
$exists = Storage::disk('cdn-services')->exists('images/photo.jpg');
Storage::disk('cdn-services')->delete('images/photo.jpg');
$url = Storage::disk('cdn-services')->url('images/photo.jpg');
Storage::disk('cdn-services')->copy('images/photo.jpg', 'images/photo-copy.jpg');
Storage::disk('cdn-services')->move('images/photo.jpg', 'images/new-photo.jpg');

CdnServicesAuth – kayıt, giriş, token

Backend'de kullanıcı kaydı (registration token zorunlu olabilir), giriş ve CDN işlemleri için JWT almak:

use CdnServices\Facades\CdnServicesAuth;

// Kayıt (registration token config'ten veya 2. parametre ile)
$result = CdnServicesAuth::register([
    'email' => 'user@example.com',
    'password' => 'secret123',
    'name' => 'Ad Soyad',
]);
// $result['token'] → JWT (CDN_SERVICES_TOKEN olarak kullanılabilir)
// $result['user'] → id, email, name, role, createdAt

// Giriş
$result = CdnServicesAuth::login('user@example.com', 'secret123');
if ($result) {
    $jwt = $result['token'];
}

// Sunucu tarafında bir kullanıcı için JWT üret (CDN işlemleri için)
$tokenPayload = CdnServicesAuth::tokenForUser($userId, 'user@example.com', 'user');
$jwt = $tokenPayload['token'] ?? null;

// Kayıt için token zorunlu mu?
if (CdnServicesAuth::requiresRegistrationToken()) {
    // Config'te CDN_SERVICES_REGISTRATION_TOKEN tanımlı
}

CdnServices facade

use CdnServices\Facades\CdnServices;

CdnServices::put('images/photo.jpg', $fileContents);
$contents = CdnServices::get('images/photo.jpg');
$url = CdnServices::url('images/photo.jpg');

Upload with UploadedFile

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;

public function upload(Request $request)
{
    $request->validate(['image' => 'required|image|max:51200']); // 50MB

    $path = Storage::disk('cdn-services')->put('images', $request->file('image'));
    return response()->json([
        'success' => true,
        'path' => $path,
        'url' => Storage::disk('cdn-services')->url($path),
    ]);
}

CdnServicesApi – meta, list, usage (kota), import, placeholder, signed URL, processed URL

use CdnServices\Facades\CdnServicesApi;

$info = CdnServicesApi::getInfo($imageId);
$images = CdnServicesApi::listImages(['tag' => 'ürün', 'favorite' => true]);
CdnServicesApi::updateMeta($imageId, ['caption' => 'Yeni başlık', 'visibility' => 'private']);
CdnServicesApi::replace($imageId, $request->file('image'));
$usage = CdnServicesApi::usage(); // fileCount, totalSize, totalSizeMB, viewCountTotal, quotaBytes?, quotaMB?
$quotaRemaining = CdnServicesApi::getQuotaRemaining(); // null if no quota
$file = CdnServicesApi::importFromUrl('https://example.com/photo.jpg');
$file = CdnServicesApi::createPlaceholder(['width' => 800, 'height' => 600, 'text' => 'Placeholder']);
$result = CdnServicesApi::bulkDelete(['id1', 'id2']);
$signed = CdnServicesApi::getSignedUrl($imageId, 3600);
$url = CdnServicesApi::processedUrl($imageId, '800x600', 'webp', [
    'quality' => 80, 'fit' => 'cover', 'filter' => 'sepia', 'crop' => 'smart', 'watermark' => true,
]);

Different backend disks (S3, Azure, etc.)

Storage::disk('cdn-services')->put('images/photo.jpg', $fileContents, ['disk' => 's3']);
Storage::disk('cdn-services')->put('images/photo.jpg', $fileContents, ['disk' => 'azure']);

Processed image URLs

$originalUrl = Storage::disk('cdn-services')->url($imageId);
$thumb = CdnServicesApi::processedUrl($imageId, 'thumbnail', 'jpeg');
$custom = CdnServicesApi::processedUrl($imageId, '800x600', 'webp', ['quality' => 75, 'watermark' => true]);

Cloudflare Image Resizing

Backend’de Cloudflare Image Resizing açıksa, edge’de resize/format/quality için URL alınır:

$url = CdnServicesApi::cloudflareProcessedUrl($imageId, '800x600', 'webp', [
    'quality' => 80, 'fit' => 'cover', 'crop' => 'smart',
]);
// Backend GET /api/image/{id}/cloudflare-url ile aynı parametreler

Detay: CDN Services docs/CLOUDFLARE_IMAGE_RESIZING.md.

CdnServicesPdf – PDF depolama (blockchain, süreli session)

Backend'de PDF_STORAGE_ENABLED=true ise kullanılır; resim alanından bağımsızdır.

use CdnServices\Facades\CdnServicesPdf;
use CdnServices\Domain\Pdf\PdfDocument;
use CdnServices\Domain\Pdf\PdfSession;

if (!CdnServicesPdf::isEnabled()) {
    return; // PDF storage kapalı
}

// Yükle (value object döner)
// Yükleme veya HTML'den oluşturma (backend'de PDF_GENERATE_SERVICE_URL gerekir)
$doc = CdnServicesPdf::upload($request->file('pdf'));
// veya: $doc = CdnServicesPdf::generateFromHtml('<html><body><h1>Rapor</h1></body></html>', 'rapor.pdf');
if ($doc instanceof PdfDocument) {
    $id = $doc->id;
}

// Listele
$documents = CdnServicesPdf::list(); // list<PdfDocument>

// Süreli erişim session'ı al
$session = CdnServicesPdf::createSession($doc->id);
if ($session instanceof PdfSession) {
    $url = CdnServicesPdf::sessionUrl($session); // GET ile PDF açılır
    // veya: $session->url(config('cdn-services.base_url'));
}

// Sil
CdnServicesPdf::delete($doc->id);

Constructor injection (DDD): Uygulama sınıflarında port kullanmak için:

use CdnServices\Domain\Pdf\PdfStorageGatewayInterface;

class MyController extends Controller
{
    public function __construct(
        private PdfStorageGatewayInterface $pdfStorage
    ) {}
}

Customization

Custom base URL: set base_url in config/filesystems.php for the cdn-services disk.

Dynamic token: in your service provider, when extending the disk, get config and call $adapter->setToken(auth()->user()->cdn_token) (or your logic) before returning the adapter.


Storage quota and exceptions

When the backend has USER_STORAGE_QUOTA_BYTES set, uploads (Storage put, API replace, importFromUrl, createPlaceholder) may return 413. The adapter throws CdnServices\Exceptions\QuotaExceededException so you can catch and show a friendly message:

use CdnServices\Exceptions\QuotaExceededException;

try {
    Storage::disk('cdn-services')->put('images/photo.jpg', $contents);
} catch (QuotaExceededException $e) {
    return back()->with('error', 'Depolama limitiniz doldu.');
}

CdnServicesApi::usage() includes quotaBytes and quotaMB when a quota is set; use getQuotaRemaining() to show remaining space.


Troubleshooting

Problem Check
Connection refused Backend running: curl http://localhost:3012/health
Unauthorized Valid JWT in CDN_SERVICES_TOKEN; create token via backend /api/auth/token
File not found Use image ID (not path) when calling URL/meta APIs
QuotaExceededException Backend USER_STORAGE_QUOTA_BYTES limit reached; free space or increase quota

API quick reference

Storage put() options: disk, caption, tags, folder, visibility

CdnServicesAuth: register, login, tokenForUser, getRegistrationToken, requiresRegistrationToken

CdnServicesPdf (DDD): upload(UploadedFile), generateFromHtml(html, filename), list(), createSession(documentId), sessionUrl(PdfSession), delete(documentId), isEnabled()

CdnServicesApi: getInfo, listImages, updateMeta, replace, usage, getQuotaBytes, getQuotaRemaining, importFromUrl, createPlaceholder, bulkDelete, getSignedUrl, processedUrl, cloudflareProcessedUrl

Disk methods: exists, get, put, delete, copy, move, size, lastModified, mimeType, url, temporaryUrl, readStream, writeStream, files


License

MIT