Skip to content

Reference plugins

Choose and configure Adonia's supported production integration packages.

The reference plugins are ordinary publishable packages built only on the documented manifest and registry surfaces. They are usable integrations and examples, not hidden framework features.

Activity log

Activity log provides a panel- and tenant-scope-aware, read-only resource over core’s action-event table. It ships exact resource/action/actor/target filters, semantic change display, safe resource links, a retention helper, and the ./commands/activity_prune Ace command.

The package exports two manifest entry points:

  • activityLogManifest carries protocol, compatibility, client-entry, and migration metadata for installers.
  • activityLogPlugin({ panel, authorize, tenantScope?, ... }) returns the configured manifest whose server hook adds the resource to that panel.

Authorization is deliberately required; the package does not invent a policy or expose the log by default.

import { Panel } from '@adonia/core'
import { activityLogPlugin } from 'adonia-activity-log'
import User from '#models/user'

Panel.make('admin').plugins([
  activityLogPlugin({
    panel: 'admin',
    authorize: (ctx) => ctx.auth.user instanceof User && ctx.auth.user.role === 'admin',
  }),
])

Import and export

Import and export provides explicit CSV/XLSX mapping, bounded and queued imports, streaming/compressed exports, owned history and progress, Drive/Lucid/file persistence adapters, failure artifacts, retries, and export presets.

importExportManifest (also returned by the source-compatible importExportPlugin() function) declares:

  • guarded panel routes under transfers/:resource/*;
  • reserved export and import action-job handlers;
  • the import-export-worker doctor check;
  • its persistence migration directory; and
  • the adonia-import-export/client browser entry.

The routes resolve the host’s authenticated controller from adonia.importExportController. Worker handlers resolve the host’s transfer runtime from adonia.importExportWorker. Attaching the manifest therefore replaces hand-written route tables and action-name switches, but the host still binds those two application-specific services, starts a queue consumer, chooses storage credentials, and enforces resource/actor/tenant ownership. The low-level services, stores, endpoint contracts, actions, and routeImportExportWorker() remain exported for advanced composition.

Roles and permissions

Roles provides tenant-aware Lucid role, permission, and assignment storage plus a panel-scoped role-management resource and a Bouncer-compatible PermissionPolicy base.

pnpm add adonia-roles @adonisjs/lucid
node ace configure adonia-roles
node ace migration:run

Attach rolesPlugin() to every panel that manages roles. Configuration names the panel, declares the permission catalogue shown in the resource’s checkbox-list field, and requires a server-side authorize callback:

import { rolesPlugin } from 'adonia-roles'

rolesPlugin({
  panel: 'admin',
  permissions: [
    { slug: 'posts.view', label: 'View posts' },
    { slug: 'posts.*', label: 'Manage posts' },
  ],
  authorize: (ctx) => ctx.auth.user?.isSuperAdmin === true,
})

Queries and assignments are scoped to the configured panel and active tenant. Use tenantScope: false only for a genuinely non-tenant panel. Permission slugs support exact <resource>.<ability>, <resource>.*, and * grants; extend PermissionPolicy on host resources rather than bypassing core’s normal authorization pipeline. A bare role slug must be unique within its tenant, so pass a persisted Role or numeric id when multiple panels reuse a slug. rolesManifest supplies migrations and the roles-tables doctor check; this package has no separate browser entry because its management UI is a normal Adonia resource.

Media library

Media library adds a tenant-aware MediaAsset model, browsable resource, protected picker/upload routes, named collection rules, optional Sharp variants, and F.media() for storing an asset id on another resource.

pnpm add adonia-media-library @adonisjs/drive @adonisjs/lucid
node ace configure adonia-media-library
node ace migration:run

Configure an @adonisjs/drive disk, then attach a manifest with explicit collections, storage, and authorization:

import { defineMediaCollections, mediaLibraryPlugin } from 'adonia-media-library'

mediaLibraryPlugin({
  collections: defineMediaCollections([
    { name: 'images', acceptedMime: ['image/*'], maxSizeMb: 12 },
  ]),
  disk: 'media',
  visibility: 'private',
  authorize: (ctx) => ctx.auth.user !== null,
})

Register mediaLibraryUiPlugin from adonia-media-library/client in the application ComponentRegistry; it supplies the lazy picker used by F.media('coverAssetId', { collection: 'images' }). The picker browses, searches, uploads, and selects only through panel-guarded routes. Uploads are sniffed from stored bytes rather than trusting MIME headers, and private disk keys stay out of descriptors. Install sharp only when collections declare image variants: without it the original succeeds but conversion status is sharp-unavailable. Treat the stored numeric asset id as a host-model foreign key, not as a Drive object key.

Two-factor authentication

Two factor provides RFC 6238 TOTP credentials, single-use recovery codes, panel middleware, protected setup/challenge routes, and lazy setup and challenge pages.

pnpm add adonia-two-factor
node ace configure adonia-two-factor
node ace migration:run

The host must configure APP_KEY. Attach twoFactorPlugin({ issuer, window, recoveryCodeCount, stepUpSeconds }), and place requireTwoFactor({ stepUpSeconds }) after the host authentication middleware. The middleware permits users without a confirmed credential and redirects an enabled user without a recent panel-bound proof to the challenge route.

Register twoFactorUiPlugin from adonia-two-factor/client, then let the Inertia resolver ask the registry for adonia-two-factor/setup and adonia-two-factor/challenge before resolving application pages. Setup shows the base32 secret and otpauth:// URI; both pages are lazy chunks. Request bodies never choose the user or panel. Secrets are encrypted with APP_KEY, plaintext recovery codes are returned only once, regeneration invalidates all old recovery codes, and disabling 2FA requires the current password plus a current TOTP or recovery code.

Backups

Backups supplies PostgreSQL, MySQL, and file-backed SQLite backup/restore engines, private gzip artifacts, checksum verification, retention, signed downloads, a read-only panel resource, and Ace commands.

pnpm add adonia-backups @adonisjs/drive @adonisjs/lucid
node ace configure adonia-backups
node ace migration:run

Create a private Drive disk, LucidBackupRecordRepository, BackupEngine, DefaultBackupsRuntime, and DefaultBackupsController. Bind the runtime at ADONIA_BACKUPS_RUNTIME_BINDING, bind the controller at ADONIA_BACKUPS_CONTROLLER_BINDING, and attach backupsPlugin({ panel, disk, diskName, authorize }). The separately booted queue worker needs the runtime binding too: the manifest’s reserved adonia-backups.run handler dispatches server-created jobs to DefaultBackupsRuntime.work.

The resource exposes server-authorized signed downloads without emitting opaque Drive keys. Register the exported backup/run and backup/prune commands in adonisrc.ts for operator use. PostgreSQL images need pg_dump and psql; MySQL images need mysqldump and mysql; in-memory SQLite is unsupported. Restore is destructive, requires the exact record-specific restoreConfirmationToken(record), verifies size and SHA-256 before changing the target, and has no implicit rollback.

Notifications

Notifications provides viewer-scoped database notifications, guarded JSON routes, queued-action completion alerts, an optional mail channel, and a lazy bell in topbar.right.

pnpm add adonia-notifications
node ace configure adonia-notifications
node ace migration:run

Attach notificationsPlugin() to each panel. notify(recipient, payload) requires the panel and normalized tenant, uses the database channel by default, and accepts root-relative or HTTP(S) href values. For mail, inject an AdonisMailChannelAdapter into NotificationService and request channels: ['database', 'mail']; recipients without an email are skipped.

Add adonia-notifications to the Tailwind v4 sources and register notificationsUiPlugin from adonia-notifications/client. The bell polls its guarded panel endpoint every 30 seconds, pauses while the document is hidden, and supports mark-one and mark-all-read; use createNotificationsUiPlugin({ pollIntervalMs: 0 }) to disable polling. Routes scope every query by viewer, panel, and tenant, and deliberately omit notification data from responses. Queued completion alerts are emitted only for actions declared with isQueued: true, and failure copy never includes the thrown exception.

Browser registration and installation

Activity log, import/export, media library, two-factor, and notifications publish named UI plugins from ./client. A manifest’s client string is metadata rather than an automatic import: register the matching UI plugin in the application’s ComponentRegistry and keep its server/client package versions equal. Roles and backups render through core resource surfaces and do not require a separate browser plugin.

Each package’s node ace configure <package> hook uses configureAdoniaApp(..., { plugins }) from @adonia/devtools/configure to publish manifest migrations and collect doctor checks. Run migrations after configuration. Reference-plugin routes are never public escape hatches: manifest routes inherit the panel’s access and tenant middleware, and package handlers add their documented actor/tenant/authorization scopes.

Complete exported surfaces for all reference packages are inventoried in the generated API reference.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close