jeffersongoncalves/laravel-knowledge-base
A Laravel package for adding a searchable knowledge base or help center to your app. Organize articles into categories, publish content, and help users find answers fast, with a ready-to-use structure for managing docs without leaving Laravel.
Architecture fit. v2.1.0 moves the package from "files in public/storage + hardcoded URL" to a mediated-access model: assets live on a configured disk and are served through a package-owned route (knowledge-base.assets.show) that re-evaluates article visibility on every request. This is the correct architectural direction for a knowledge base, where the dominant real-world requirement is "draft/internal articles must not leak." It uses standard Laravel primitives — filesystem disks, signed URLs, config publishing, route prefix/middleware from config — so it does not introduce a parallel abstraction the platform team has to learn or maintain.
Integration feasibility. High. The new attachments.route block (enabled, prefix, middleware, signed_url_minutes, cache_max_age) plus attachments.disk gives the two knobs that matter: which disk, and which middleware stack the route sits behind. Slotting it behind web, auth, or a tenant-scoping middleware is a config change, not a code change. The kb:move-assets command with --dry-run is the right shape for a storage migration and makes the change reversible in staging. The dual-mode switch (attachments.route.enabled = false preserves 2.0 behavior) means we are not forced into a big-bang cutover.
Technical risk.
public disk remain reachable at /storage/.... Until the old files are deleted, the security property the release advertises is not actually delivered. This is the single highest-risk item and it is an operator action, not a package behavior.cache_max_age seconds after an article becomes internal, previously cached public responses can still be served. The package can't fix this; it can only be mitigated by lowering cache_max_age and purging. Any org with a CDN in front needs to treat this as a documented residual risk, not a footnote.Cache-Control: private, no-store and that cache_max_age applies only to the public branch.Key questions.
kb:move-assets move (delete source) or copy? If it copies, we need a documented deletion step for the old public files.composer.json constraints (Laravel/PHP versions) and is there a rollback path if the disk move fails mid-run (idempotency, partial-failure behavior)?Content-Disposition / X-Content-Type-Options, and does it support non-image types (PDF, office docs) safely?signed_url_minutes, and what happens on expiry for a user with a long-lived open page?Stack fit. Native Laravel package: service provider, publishable config, artisan command, route registered by the package. Fits any Laravel app already using this package; no new runtime dependency implied by the release notes. Disk abstraction means local today, S3/private cloud later, without code changes.
Migration path.
attachments.disk to local (or a private cloud disk); leave attachments.route.enabled = true.php artisan kb:move-assets --dry-run, review the manifest, then run for real in staging and verify a sample of public, internal, draft, and archived articles./storage/ asset URLs — WYSIWYG-embedded links will not be rewritten by the move command and will 404 once the public copies are removed.img-src/media-src) if the assets route is served from a different host than the media it replaces.Compatibility. Watch for: API/headless consumers that read asset URLs from a JSON payload (URL shape changes, and internal URLs become time-limited); sitemap generators and search indexers that follow asset URLs; email templates referencing /storage/ paths; any external system that was given a public URL directly.
Sequencing. Ship config + route with the public disk still serving (dual-serve) → migrate and verify → remove public copies → purge CDN → then lower cache_max_age for any tenant with genuinely sensitive content. Do not combine the disk migration with unrelated package upgrades in the same change window.
Maintenance. Adds a config surface and a storage lifecycle the team now owns: the private disk must be included in backups, retention, and restore drills — it is no longer just a symlinked public directory. The move command is a one-time tool but becomes a recurring one if attachments.disk ever changes.
Support. Expect a new class of ticket: broken images/attachments from expired signed URLs on long-open tabs, and from content referencing old /storage/ paths after cleanup. Mitigate with a generous signed_url_minutes, a documented "refresh the page" resolution, and a pre-emptive content sweep.
Scaling. Private asset delivery shifts load from CDN/web-server to the PHP application tier. Public content stays cacheable (cache_max_age), internal content does not. For large internal audiences or heavy PDF usage, budget for X-Sendfile/X-Accel-Redirect or S3 presigned-redirect handling, or accept the added app-server bandwidth. Storage cost is roughly neutral; egress cost may move.
Failure modes.
storage/app/private missing for the web user or queue worker → move command or serving fails.Cache-Control → private assets served past expiry or past a visibility change.route.enabled = false) leaves private-disk assets unreachable — the flag is not a safe revert once files have moved.Ramp-up. Low for a competent Laravel developer: config change plus one artisan command, roughly half a day in staging. The real effort is non-code — content remediation for hardcoded paths, CDN purge/header review, and updating the internal security posture doc to reflect that "private" only holds after old public copies are deleted. Budget 1–2 days of cross-team coordination for a production cutover.
How can I help you explore Laravel packages today?