mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-21 22:46:32 +00:00
feat(plugins): sandboxed page/widget frames + trekBridge (M3)
Plugins can now render UI. Page plugins appear as a nav entry and open a full-page sandboxed iframe; the frame talks to TREK only over the postMessage bridge. - Server serves plugin client assets at /plugin-frame/:id/* with a strict path guard and a locked-down, per-plugin CSP (default-src none; connect-src limited to declared outbound hosts; sandbox WITHOUT allow-same-origin -> opaque origin, so the frame can't read the session cookie or the parent DOM). Global CSP frameSrc relaxed from 'none' to 'self' for exactly these frames. - GET /api/plugins feed lists active plugins for the client. - Client: pluginStore + PluginFrame (the trekBridge host) authenticates every inbound message by SENDER WINDOW IDENTITY (event.source), not by a claimed id or origin; pushes context (theme/locale/tripId/userId), validates navigation, renders notifications as text, resizes widgets, and proxies trek:invoke to the plugin's own routes host-side (session cookie stays with the host). - Page route /plugins/:id + Navbar nav injection for page plugins. Dashboard widget slot and the trek:request core-data bridge are deferred to a later slice.
This commit is contained in:
@@ -18,6 +18,7 @@ vi.mock('../../../src/db/database', () => ({ db: testDb }));
|
||||
|
||||
import { PluginsService } from '../../../src/nest/plugins/plugins.service';
|
||||
import { PluginsController } from '../../../src/nest/plugins/plugins.controller';
|
||||
import { PluginsFeedController } from '../../../src/nest/plugins/plugins-feed.controller';
|
||||
|
||||
beforeEach(() => {
|
||||
testDb.exec('DELETE FROM plugins');
|
||||
@@ -56,6 +57,21 @@ describe('PluginsService.list', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('PluginsFeedController (client feed)', () => {
|
||||
it('returns active plugins when enabled, nothing when disabled', () => {
|
||||
testDb.prepare("INSERT INTO plugins (id, name, type, icon, status) VALUES ('w','W','widget','Box','active')").run();
|
||||
testDb.prepare("INSERT INTO plugins (id, name, type, icon, status) VALUES ('i','I','integration','Plug','inactive')").run();
|
||||
const feed = new PluginsFeedController();
|
||||
|
||||
process.env.TREK_PLUGINS_ENABLED = 'true';
|
||||
const active = feed.list();
|
||||
expect(active.plugins).toEqual([{ id: 'w', name: 'W', type: 'widget', icon: 'Box' }]);
|
||||
|
||||
process.env.TREK_PLUGINS_ENABLED = 'false';
|
||||
expect(feed.list().plugins).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('PluginsController M2 endpoints', () => {
|
||||
const svc = {
|
||||
getInstanceConfig: vi.fn(() => ({ a: 1 })),
|
||||
|
||||
Reference in New Issue
Block a user