apps/lina-plugins/ is the first-party plugin workspace for LinaPro.
LinaPro positions apps/lina-core as the stable full-stack framework host. The host keeps universal framework capabilities, governance, and plugin extension surfaces, while business modules, operational pages, demo capabilities, and optional domain features live here as plugins. This keeps the host reusable and avoids binding core contracts to a specific management workspace page.
The workspace currently contains source plugins compiled into the host, plus one dynamic WASM plugin example for runtime delivery.
| Plugin | Type | Scope | Install mode | Capability |
|---|---|---|---|---|
linapro-tenant-core |
source |
platform_only |
global |
Tenant entities, member relationships, tenant resolution, and tenant lifecycle governance |
linapro-org-core |
source |
tenant_aware |
global |
Department management and post management |
linapro-content-notice |
source |
tenant_aware |
tenant_scoped |
Notice announcement management |
linapro-monitor-online |
source |
tenant_aware |
tenant_scoped |
Online user query and forced logout governance |
linapro-monitor-server |
source |
platform_only |
global |
Server monitor collection, cleanup, and query |
linapro-monitor-operlog |
source |
tenant_aware |
tenant_scoped |
Operation log persistence and governance pages |
linapro-monitor-loginlog |
source |
tenant_aware |
tenant_scoped |
Login log persistence and governance pages |
linapro-ops-demo-guard |
source |
tenant_aware |
global |
Demo-environment read-only protection and global write-operation interception |
linapro-demo-source |
source |
tenant_aware |
tenant_scoped |
Source plugin example for menu pages, public routes, and protected routes |
linapro-demo-dynamic |
dynamic |
tenant_aware |
tenant_scoped |
Dynamic WASM plugin example for embedded menu pages, plugin-owned SQL table CRUD, and standalone static pages |
The root go.mod and lina-plugins.go wire the source plugins that are compiled with the host. linapro-demo-dynamic is intentionally not wired as a source plugin; it is the runtime plugin reference used by the WASM build and lifecycle flow.
| Path | Purpose |
|---|---|
go.mod |
Local Go workspace module for source plugin compilation checks |
lina-plugins.go |
Explicit source plugin import registry for host compilation |
Makefile |
Plugin workspace build and code generation entry; make wasm p=<plugin-id>, make ctrl p=<plugin-id>, and make dao p=<plugin-id> delegate to linactl |
package.json |
Frontend workspace metadata for source plugin packages |
<plugin-id>/plugin.yaml |
Plugin manifest, metadata, menus, install mode, i18n, assets, dependencies, and host service declarations |
<plugin-id>/Makefile |
Plugin-local code generation wrapper that includes the shared root hack/makefiles/plugin.codegen.mk target fragment |
<plugin-id>/README.md |
English plugin-level guide |
<plugin-id>/README.zh-CN.md |
Chinese plugin-level guide |
The main linapro repository mounts this workspace at apps/lina-plugins as a Git submodule:
git submodule update --init --recursiveThe configured SSH remote is:
git@github.com:linaproai/official-plugins.git
Each plugin directory is owned by the plugin. Lifecycle resources, frontend pages, backend code, SQL assets, i18n resources, and tests should stay inside apps/lina-plugins/<plugin-id>/.
apps/lina-plugins/<plugin-id>/
backend/
api/ API DTOs, route contracts, and metadata
internal/
controller/ Plugin request handling and response projection
service/ Plugin business orchestration and domain logic
dao/ Plugin-local generated DAO objects when database access exists
model/do/ Plugin-local generated DO objects when database access exists
model/entity/ Plugin-local generated entity objects when database access exists
hack/config.yaml Plugin-local GoFrame code generation config when DAO generation exists
plugin.go Backend registration, route registration, lifecycle entry, or dynamic bridge entry
frontend/pages/ Plugin-owned pages or public static assets
manifest/
sql/ Install SQL assets
sql/mock-data/ Optional mock or demo SQL assets
sql/uninstall/ Optional uninstall SQL assets
i18n/<locale>/ Plugin i18n resources
hack/tests/ Optional plugin-owned E2E tests, page objects, and helpers
go.mod Plugin-local Go module
Makefile Plugin-local code generation wrapper
plugin.yaml Plugin manifest
plugin_embed.go Embedded asset registration entry
README.md English guide
README.zh-CN.md Chinese guide
Plugin root Makefile files are thin wrappers around the repository-level
hack/makefiles/plugin.codegen.mk fragment. The shared fragment derives the
target backend from the including plugin directory, so plugin Makefile files
must not hard-code apps/lina-plugins/<plugin-id>/backend. Run make ctrl or
make dao inside a plugin directory to use that plugin's
backend/hack/config.yaml, or run make ctrl p=<plugin-id> and
make dao p=<plugin-id> from apps/lina-plugins/.
backend/internal/service/ is the only valid location for plugin business services. Do not create backend/service/. Dynamic plugins keep the same backend/api/, backend/plugin.go, backend/internal/controller/, and backend/internal/service/ shape; their bridge files only adapt WASM and pluginbridge protocols. Guest business capability clients must come from lina-core/pkg/plugin/capability/guest, not from the pluginbridge root package.
Source plugins are compiled with apps/lina-core through explicit registration. They are suitable for first-party framework capabilities that should be delivered with the host build but still remain outside the host core domain.
Source plugin development rules:
- Create or update
apps/lina-plugins/<plugin-id>/. - Keep plugin metadata, menus, page mounts, lifecycle resources,
SQLassets, andi18nassets inplugin.yamlandmanifest/. - Keep backend implementation under
backend/, with business logic inbackend/internal/service/. - Keep frontend pages under
frontend/pages/or declare public asset directories throughplugin.yamlpublic_assets. - Register source plugins explicitly in
apps/lina-plugins/lina-plugins.gowhen they must be compiled into the host.
Dynamic plugins are delivered as runtime-managed WASM artifacts. Use linapro-demo-dynamic/ as the reference for upload, install, enable, disable, uninstall, hostServices, public static assets, and plugin-owned data access through governed host services.
Build all dynamic plugins, or one plugin with p=<plugin-id>:
make -C apps/lina-plugins wasm
make -C apps/lina-plugins wasm p=linapro-demo-dynamicDynamic plugins must declare type: dynamic in plugin.yaml, keep main.go and go.mod as the guest build entry, use hostServices to describe runtime capabilities and resource boundaries, and import runtime, storage, data, cache, config, notify, cron, and related business host-service clients from lina-core/pkg/plugin/capability/guest.
The host owns stable framework surfaces and top-level catalogs such as dashboard, platform, org, content, monitor, setting, scheduler, extension, and developer. Plugins choose their own mount points through plugin.yaml parent_key; the host only resolves declared parents during sync and rejects missing parents to avoid orphaned menu trees.
Plugin-owned tables, menus, pages, hooks, cron jobs, public assets, and lifecycle resources stay in the plugin directory. Host code should depend on stable plugin service seams and published packages rather than hard-coding plugin-specific page structures or menu composition details.
Source plugin HTTP routes are registered by plugin backend code. Do not declare public routes, portal routes, workspace API routes, or route groups in plugin.yaml.
Use registrar.Routes().APIPrefix() for source plugin APIs. The returned prefix is /x/{plugin-id}. Segments after that prefix are plugin-defined route content, such as /api/v1, /api/v2, or /interface/m1.
plugin.yaml menus is the source of truth for management workspace navigation and permissions. Registering an HTTP route does not create menus, permission nodes, OpenAPI entries, or workspace route metadata.
Plugins may declare public static assets through plugin.yaml public_assets. The host serves declared assets from /x-assets/{plugin-id}/{version}/... and treats each declaration as the plugin author's publication boundary.
Plugin-owned Playwright coverage belongs under:
apps/lina-plugins/<plugin-id>/hack/tests/e2e/
apps/lina-plugins/<plugin-id>/hack/tests/pages/
apps/lina-plugins/<plugin-id>/hack/tests/support/
Run a single plugin test scope through the host test runner:
pnpm -C hack/tests test:module -- plugin:<plugin-id>Use plugin-local api_contract_test.go and Go package tests for backend contract checks where applicable.
When an installed source plugin bumps plugin.yaml version, discovery does not silently replace the effective host version.
- The current effective version remains pinned in
sys_plugin.versionandrelease_id. - The higher discovered source version is stored as a prepared
sys_plugin_release. - The host must process the source plugin through the supported plugin workspace update flow before startup is allowed to continue.
- If the update is skipped, startup fails fast and reports the plugins that still require attention.
apps/lina-plugins/linapro-demo-source/README.mdapps/lina-plugins/linapro-demo-dynamic/README.md