snowsoft/cdn-laravel-adapter 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

snowsoft/cdn-laravel-adapter

Composer 安装命令:

composer require snowsoft/cdn-laravel-adapter

包简介

Laravel Storage adapter for CDN Services Node.js backend

README 文档

README

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

snowsoft/cdn-laravel-adapter 适用场景与选型建议

snowsoft/cdn-laravel-adapter 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 157 次下载、GitHub Stars 达 0, 最近一次更新时间为 2026 年 02 月 12 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「image」 「filesystem」 「storage」 「cdn」 「laravel」 「cdn-services」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 snowsoft/cdn-laravel-adapter 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 snowsoft/cdn-laravel-adapter 我们能提供哪些服务?
定制开发 / 二次开发

基于 snowsoft/cdn-laravel-adapter 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 157
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 16
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-02-12