fix: второй стабилизационный проход Lua plugin lifecycle
1. Enabled/Active state separation: - Enable() sets Enabled=true (persisted in config), does NOT create runtime - ActivatePlugin() checks Enabled && !Active, creates VM + scheduler - DeactivatePlugin() stops runtime, keeps Enabled=true - InitRuntimes() iterates Enabled plugins, sets Active=true after creation - SyncConfig() restores Enabled from config, does NOT touch Active 2. ActivatePlugin: добавлен vm.SetServices(m.Services) 3. Discover: атомарная замена списка (newPlugins slice), нет дублирования 4. CallPluginFunction: thread-safe через LuaVM.CallFunction (vm.mu + callWithTimeout) 5. Uninstall активного плагина: полная деактивация (StopScheduler → on_shutdown → CloseVM → Active=false) 6. GetPluginPanelHTML: валидация panel path (no absolute, no .., must be .html, must be within plugin dir) 7. PluginPage: убран hardcoded 'calendar-plugin', используется funcPrefix из pluginName Тесты: - security_test.go: +8 тестов (FullLifecycle, ActivatePlugin_Services, Discover_Idempotent, ReloadPlugins_NoDuplicates, CallPluginFunction_Timeout, Uninstall_ActivePlugin, GetPluginPanelHTML_PathTraversal, FullLifecycle_EndToEnd) - manager_test.go: обновлены тесты под новую семантику Enabled/Active
This commit is contained in:
@@ -36,13 +36,15 @@
|
||||
}
|
||||
|
||||
// Handle messages from iframe — only accept from our own iframeEl
|
||||
// The iframe identifies itself via msg.source (set by the panel HTML).
|
||||
// We accept any source that starts with "plugin:" to support generic plugin panels.
|
||||
function handleIframeMessage(e) {
|
||||
// Verify the message comes from our iframe (srcdoc = same origin)
|
||||
if (!iframeEl || !iframeEl.contentWindow || e.source !== iframeEl.contentWindow) return
|
||||
|
||||
const msg = e.data
|
||||
if (!msg || typeof msg !== 'object') return
|
||||
if (!msg.source || msg.source !== 'calendar-plugin') return
|
||||
if (!msg.source || typeof msg.source !== 'string') return
|
||||
if (!msg.action || typeof msg.action !== 'string') return
|
||||
|
||||
switch (msg.action) {
|
||||
@@ -77,10 +79,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Build the Lua function prefix from plugin name (e.g. "calendar" → "calendar.")
|
||||
$: funcPrefix = pluginName ? pluginName + '.' : ''
|
||||
|
||||
// Load events + categories from Lua backend and send to iframe
|
||||
async function loadCalendarData() {
|
||||
try {
|
||||
// Get current month range
|
||||
const now = new Date()
|
||||
const year = now.getFullYear()
|
||||
const month = now.getMonth()
|
||||
@@ -88,8 +92,8 @@
|
||||
const end = new Date(year, month + 1, 0).toISOString().slice(0, 10) + 'T23:59:59'
|
||||
|
||||
const [eventsRaw, categoriesRaw] = await Promise.all([
|
||||
wailsCall('CallPluginFunction', pluginName, 'calendar.get_events', JSON.stringify({ start, end })),
|
||||
wailsCall('CallPluginFunction', pluginName, 'calendar.get_categories', '{}'),
|
||||
wailsCall('CallPluginFunction', pluginName, funcPrefix + 'get_events', JSON.stringify({ start, end })),
|
||||
wailsCall('CallPluginFunction', pluginName, funcPrefix + 'get_categories', '{}'),
|
||||
])
|
||||
|
||||
const events = eventsRaw ? JSON.parse(eventsRaw) : []
|
||||
@@ -109,7 +113,7 @@
|
||||
async function handleGetEvents(data) {
|
||||
try {
|
||||
const params = JSON.stringify({ start: data.start, end: data.end })
|
||||
const raw = await wailsCall('CallPluginFunction', pluginName, 'calendar.get_events', params)
|
||||
const raw = await wailsCall('CallPluginFunction', pluginName, funcPrefix + 'get_events', params)
|
||||
const events = raw ? JSON.parse(raw) : []
|
||||
postToIframe({
|
||||
source: 'verstak',
|
||||
@@ -124,14 +128,13 @@
|
||||
async function handleCreateEvent(data) {
|
||||
try {
|
||||
const params = JSON.stringify(data)
|
||||
const raw = await wailsCall('CallPluginFunction', pluginName, 'calendar.create_event', params)
|
||||
const raw = await wailsCall('CallPluginFunction', pluginName, funcPrefix + 'create_event', params)
|
||||
const result = raw ? JSON.parse(raw) : {}
|
||||
postToIframe({
|
||||
source: 'verstak',
|
||||
type: 'event-created',
|
||||
event: result,
|
||||
})
|
||||
// Refresh data
|
||||
loadCalendarData()
|
||||
} catch (e) {
|
||||
console.error('[PluginPage] create-event:', e)
|
||||
@@ -146,7 +149,7 @@
|
||||
async function handleUpdateEvent(data) {
|
||||
try {
|
||||
const params = JSON.stringify(data)
|
||||
await wailsCall('CallPluginFunction', pluginName, 'calendar.update_event', params)
|
||||
await wailsCall('CallPluginFunction', pluginName, funcPrefix + 'update_event', params)
|
||||
postToIframe({
|
||||
source: 'verstak',
|
||||
type: 'event-updated',
|
||||
@@ -161,7 +164,7 @@
|
||||
async function handleDeleteEvent(data) {
|
||||
try {
|
||||
const params = JSON.stringify({ id: data.id })
|
||||
await wailsCall('CallPluginFunction', pluginName, 'calendar.delete_event', params)
|
||||
await wailsCall('CallPluginFunction', pluginName, funcPrefix + 'delete_event', params)
|
||||
postToIframe({
|
||||
source: 'verstak',
|
||||
type: 'event-deleted',
|
||||
|
||||
Reference in New Issue
Block a user