AssetLibrary
Generic content asset upload, security inspection, R2 storage, presigned URLs, and image lightbox UI
Statusβ
| Key | Value |
|---|---|
| Layer | foundation |
| Tier | L0 |
| Status | wip |
| Version | 0.1.0 |
| Price | Free (free) |
| Category | Content |
Overviewβ
AssetLibrary is a foundation plugin for storing and reusing images, uploaded audio, PDFs, and attachments across SaaS/Tenant boundaries. It validates MIME, extension, and file signatures before storage, supports private/public R2 disks, and resolves private assets through presigned URLs. It also provides reusable Blade partials for full-screen image lightbox previews.
Image Lightbox UIβ
SaaS screens that need full-size image inspection should use the shared AssetLibrary partial.
Render the lightbox dialog once near the page root. Do not place it inside another <dialog>, because nested dialogs can behave inconsistently across browsers.
@include('asset-library::components.image-lightbox')
<figure class="relative overflow-hidden rounded-md border bg-white">
<img class="max-h-72 w-full object-contain"
src="{{ $imageUrl }}"
alt="{{ $imageTitle }}">
@include('asset-library::components.zoom-button', [
'imageSrc' => $imageUrl,
'imageTitle' => $imageTitle,
])
</figure>
For upload previews where img.src is set by JavaScript, add data-asset-image-lightbox-scope to the wrapper and omit imageSrc.
<div class="relative" data-asset-image-lightbox-scope>
<img data-preview-image alt="Upload preview image">
@include('asset-library::components.zoom-button', [
'label' => 'Open preview image',
])
</div>
The lightbox opens as a full-screen dialog. On mobile, users can use browser-native pinch zoom and scrolling.
Public / Private Policyβ
Decide the public/private visibility policy before implementing uploads. If the business model is unclear, the AI should ask the user before implementation. The default is private.
| Asset type | Recommended visibility |
|---|---|
| Files tied to users, customers, permissions, payments, or learning history | private |
| Original uploads, internal review assets, user-specific attachments | private |
| Files whose image URL itself must be shared externally | public |
| SEO/OG, landing, brand, and public content images | public |
Private presigned URLs expire. Page sharing can work because the application can issue a new URL on each request, but long-lived direct image sharing requires a public/CDN/custom domain URL.
Three Access Patternsβ
| Pattern | Security meaning | Suitable examples | Caution |
|---|---|---|---|
| Public domain URL | Anyone who knows the object key can access it | scripture.how color meaning images, public content, SEO/OG images | Once the URL is shared, access does not expire |
| Public bucket + random key | Bucket listing is unavailable, so the object is hard to find without the URL | Low-sensitivity content assets where accidental exposure is acceptable | This is path obscurity, not authorization. If the URL leaks through logs, referer headers, or sharing, it remains accessible |
| Private bucket + temporary URL | Access is granted through an expiring URL after application-side authorization | User files, customer materials, payment/learning/personal data | Not suitable for long-lived direct image sharing |
For scripture.how, where the content is public learning material and direct image sharing is expected, operating with one public bucket is acceptable. If original user uploads, customer-private materials, or permission-bound assets are added later, do not mix them into the public bucket; use a private bucket or explicit access control.
Demosβ
View on Plugin Store: store.codebase.how/plugins/asset-library