Commit Graph

217 Commits (a193c5a4c643071bf3587e3a20ed6d1903c01737)

Author SHA1 Message Date
mirivlad a193c5a4c6 fix: Firefox extension 1.0.4 — secret handling, force ping, queue flush bugs
- withAuth skips check when server secret is empty
- flushQueue doesn't send X-Verstak-Secret header when secret is empty/undefined
- popup force-pings server on open (not relying on cached bridgeReachable)
- flushQueue updates bridgeReachable on every result
- Triple-state status: true/false/undefined→'Проверка...'
- Tests: verify events POST works without auth header, with empty header, full ping→events flow
2026-06-09 01:15:12 +08:00
mirivlad e1505e1334 test(bridge): add integration tests for empty/undefined secret, full flow 2026-06-09 01:11:51 +08:00
mirivlad b002005a42 fix(browser-bridge): don't send X-Verstak-Secret when empty/undefined; add integration tests 2026-06-09 01:02:37 +08:00
mirivlad fa5001341e fix(browser-bridge): auth bypass when secret empty, popup status fix, force ping on open 2026-06-09 00:44:51 +08:00
mirivlad 58751945eb fix(firefox): v1.0.2 signed XPI, proper contents without node_modules 2026-06-08 21:55:06 +08:00
mirivlad b4de2dec7a fix(release): RPM spec use %{_topdir}/SOURCES; fix Firefox manifest for FF115 2026-06-08 19:36:04 +08:00
mirivlad c1434a0b61 fix(release): use %{_sourcedir} in RPM spec instead of absolute paths
rpmbuild runs %install from its own working directory where
 is undefined. Copy artifacts into SOURCES and use
rpmbuild's %{_sourcedir} macro to locate them.
2026-06-08 18:27:58 +08:00
mirivlad 1dbb1f8c68 feat(calendar): restore full calendar UI in iframe panel
Replaced debug placeholder in render() with real calendar rendering:
header (nav + view tabs), categories bar with filter, and month/week/day
views. Full CRUD modals were already present in the HTML.
2026-06-08 16:36:53 +08:00
mirivlad 7a95943ad7 fix(plugins): empty Lua tables now serialize as [] instead of {} in luaValueToGo
Empty Lua tables from DB queries (e.g. get_events with no results)
are ambiguous — they could be [] or {}. Frontend expects arrays
(with .length), so we default empty tables to [] instead of {}.
2026-06-08 14:15:37 +08:00
mirivlad 21130c6f1e debug: log typeof events and JSON.stringify in both parent and iframe 2026-06-08 13:57:45 +08:00
mirivlad 82c2588449 debug: replace render() with minimal test to verify innerHTML works 2026-06-08 13:46:11 +08:00
mirivlad 4a5dab49b5 debug: add try-catch around iframe render() to catch any JS errors 2026-06-08 13:43:13 +08:00
mirivlad f4a25128ae debug: add WriteDebugLog inside iframe to confirm calendar-data delivery + log.Printf to WriteDebugLog 2026-06-08 12:34:14 +08:00
mirivlad c03e2e2961 debug: add WriteDebugLog after postToIframe to confirm delivery 2026-06-08 12:31:45 +08:00
mirivlad f892d377a0 debug: add log.Printf in CallPluginFunction Go binding to trace calls 2026-06-08 12:26:43 +08:00
mirivlad 35e23d75fa debug: add step-by-step logging in loadCalendarData to trace where error occurs 2026-06-08 12:23:51 +08:00
mirivlad 5069472e19 debug: add logging to get_categories + better error details in CalendarPluginPage 2026-06-08 12:01:29 +08:00
mirivlad a202c5d079 fix(calendar): add type guards and debug logging for get_events SQL error
- Added debug prints to log params type in get_events
- Added type guard in backward compat path (reject non-string end_date)
- Added type guard for start_date/end_date before passing to SQL
2026-06-08 11:54:00 +08:00
mirivlad d6e28d7b1f fix(plugins): restore plugin runtimes on startup + graceful shutdown
Root cause: initVault() called Discover() but never called
SyncConfig(), InitRuntimes(), CallInitHooks(), or StartSchedulers().
On restart, plugins were discovered from disk but their config state
(Installed/Enabled) was not restored and no Lua VMs were created.

This caused:
- Settings → Plugins showing plugins as 'not installed' after restart
- Sidebar showing calendar item (from enabledSet config) but
  GetPluginPanelHTML failing (only checked p.Active)
- Toggle working in-session but state lost on restart
- closeVault() not stopping plugin schedulers/shutdown/VM (leak)

Fixes:
- bindings_config.go: add SyncConfig(appCfg) after Discover()
- bindings_config.go: add InitRuntimes() + CallInitHooks() + StartSchedulers()
  after watcher start
- bindings_config.go: add StopSchedulers + CallShutdownHooks + CloseRuntimes
  to closeVault() (before db.Close since plugins use DB)
- bindings_plugins.go: GetPluginPanelHTML now checks enabledSet || p.Active,
  consistent with ListSystemViewsWithPlugins and ListPlugins
2026-06-08 11:45:35 +08:00
mirivlad f769daa617 fix(plugins): JSON-serialize CallFunctionJSON return values + backward compat Lua args
Root cause: CallFunctionJSON used .String() on Lua return values, which
for tables produces 'table: 0x...' — not valid JSON. Frontend does
JSON.parse() on the result and silently caught the parse error.

Fix:
- runtime.go: convert Lua return value to JSON via luaValueToGo +
  json.Marshal so tables become proper JSON arrays/objects
- main.lua: add backward compat in get_events() and update_event()
  to accept both positional args (start, end) and table params
- CalendarPluginPage.svelte: show errors in UI instead of silent catch;
  restructure template to always show iframe + error overlay
2026-06-08 11:31:18 +08:00
mirivlad fddbd3a98a fix calendar: table params for Lua functions + remove duplicate header
- CalendarPluginPage.svelte: removed <h2>pluginName — pageLabel</h2> (AppHeader already shows title)
- main.lua: all API functions now accept a single table parameter:
  - get_events(params) — reads params.start_date or params.start
  - get_event(params) — reads params.id
  - update_event(params) — reads params.id + mutable fields
  - delete_event(params) — reads params.id
  - get_expanded_events(params), get_calendar_events(params) — same
  - get_events_day(params) — reads params.date
  - create_event(opts) — already worked, no change
- Backward compatible: get_events accepts both start/start_date and end/end_date keys
2026-06-08 11:18:28 +08:00
mirivlad b1d1defebe release infra: build scripts, Firefox signing, plugin fixes
- .gitignore: release/, .env, *.xpi, node_modules/
- .env.example: template for AMO credentials
- extension-firefox/package.json: web-ext scripts (lint, sign)
- extension-firefox/manifest.json: gecko.id + update_url + data_collection_permissions
- scripts/build.sh: renamed binaries (verstak, verstak-server), release target
- scripts/sign-firefox-xpi.sh: AMO signing with --self-hosted
- scripts/release-firefox-xpi.sh: signed XPI + updates.json generation
- scripts/release.sh: full release pipeline (DEB/RPM/checksums/git tag)
- VERSION: 0.1.0
- README.md: Firefox extension & release sections

Plugin fixes:
- internal/core/plugins/manager.go: auto-create .verstak/plugins/ dir
- frontend/src/lib/SettingsSidebar.svelte: remove 'plugins' from disabled + active left border
- frontend/src/lib/SettingsPlugins.svelte: force ListPlugins refresh on toggle
- frontend/src/lib/SettingsWindow.svelte: onPluginToggle callback
- frontend/src/App.svelte: refreshSystemViews on plugin toggle
- frontend/src/lib/CalendarPluginPage.svelte: visible error icon
2026-06-08 11:07:29 +08:00
mirivlad 3b79754f45 fix: rollback Enabled on activation failure + fatal on_init + rollback test
1. SetPluginEnabled(true): after DeactivatePlugin, also call Disable(name)
   to rollback in-memory Enabled state (not just config).
2. on_init failure is now fatal for ActivatePlugin — returns error
   and rolls back scheduler + VM (was incorrectly non-fatal).
3. TestSetPluginEnabled_BrokenPlugin_Rollback: end-to-end test with
   broken plugin (invalid interval), verifies error + not Active +
   not Enabled + not in config.
2026-06-08 00:14:49 +08:00
mirivlad 45cfe1b0a6 fix: финальный cleanup Lua plugin lifecycle
1. ActivatePlugin → error return:
   - Возвращает ошибки при создании VM, загрузке main.lua, scheduler setup
   - on_init failure = non-fatal (logged, activation continues)
   - SetPluginEnabled сохраняет EnabledPlugins в config ТОЛЬКО после успешной активации
   - При ошибке активации — rollback (deactivate + не сохраняем в config)

2. CallPluginFunction fully thread-safe:
   - Новый метод LuaVM.CallFunctionJSON(segments, paramsJSON)
   - JSON→Lua conversion происходит под vm.mu (внутри lock)
   - Убраны parseParamsToLua/goToLua из bindings_plugins.go
   - goToLua перенесён в runtime.go (под lock)

3. PluginPage → CalendarPluginPage:
   - Компонент явно календарный (get-events/create-event/update-event/delete-event)
   - Переименован для ясности
   - Console log префиксы обновлены

4. Тесты:
   - TestSetPluginEnabled_ActivateFails_NoConfigSave: проверяет что при ошибке
     активации плагин НЕ сохраняется в EnabledPlugins
   - TestActivatePlugin_ErrorReturn: проверяет все режимы ошибок
   - TestCallFunctionJSON_ThreadSafe: JSON object/array/empty params
   - TestDeactivatePlugin_Idempotent: двойная деактивация = no-op
   - TestInitRuntimes_SkipsDisabled: только Enabled плагины активируются
2026-06-07 22:58:26 +08:00
mirivlad d83c8c80e1 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
2026-06-07 20:49:43 +08:00
mirivlad 4df83cd361 security: стабилизационный аудит Lua plugin system
Исправления:
- Install: идемпотентность (no duplicates in InstalledPlugins)
- ReloadPlugins: StopSchedulers + CallShutdownHooks перед CloseRuntimes
- StopSchedulers: обнуление scheduler=nil после остановки
- Scheduler.Stop: обнуление tasks после wg.Wait
- Lua sandbox: блокировка package.loadlib/seeall/preload/loaders/loaded/path/cpath/config/searchpath
- Lua sandbox: блокировка load (глобальная функция)
- CallPluginFunction: валидация funcName (regex [a-zA-Z_][a-zA-Z0-9_]*, max 3 segments)
- CallPluginFunction: убрана строковая сборка Lua-кодa, вызов через PCall напрямую
- PluginPage.svelte: проверка e.source === iframeEl.contentWindow
- PluginPage.svelte: type checking для msg.source, msg.action

Тесты:
- security_test.go: 18 новых тестов (sandbox, lifecycle, validation)
- Все существующие тесты проходят

Документация:
- docs/plugins-security.md: модель безопасности, sandbox, протокол, lifecycle
2026-06-07 19:19:44 +08:00
mirivlad c443ca23c5 fix: PluginPage.svelte — замена CallPluginAction на CallPluginFunction с dotted path
PluginPage.svelte использовал несуществующий Wails binding CallPluginAction.
Заменён на CallPluginFunction с правильным dotted path (calendar.get_events и т.д.),
что соответствует сигнатуре bindings_plugins.go.

Frontend пересобран, go build + go test ./... — всё зелёное.
2026-06-07 16:56:28 +08:00
mirivlad 7b9c9647ac test: add TestCallPluginFunction + final run - all 13 tests pass
- TestCallPluginFunction: verifies DoString calls Lua functions correctly
  - add(3,4) → 7, greet('World') → 'Hello, World', get_table() → table
- Full suite: 13 tests, 0 failures
- go build ./... clean
2026-06-07 16:41:47 +08:00
mirivlad a1d7c7b88b feat: PluginPage iframe bridge + CallPluginFunction binding
- PluginPage.svelte: bidirectional postMessage bridge with iframe
  - Handles: ready, get-events, create-event, update-event, delete-event
  - Queues messages until iframe is ready
  - Exports handleDrop() for drag-and-drop from parent
- CallPluginFunction binding: calls arbitrary Lua functions on active plugins
  - Supports dotted paths: 'calendar.create_event' → _G.calendar.create_event
  - JSON params → Lua table conversion
- LuaVM: added DoString(), LState(), VM() public methods
- Plugin: added VM() getter for external access
2026-06-07 16:37:32 +08:00
mirivlad 308772dee8 fix: simplify ReloadPlugins - remove redundant deactivate loop
- SyncConfig already sets Active correctly from config
- InitRuntimes only processes Active plugins
- No need for separate deactivate pass
2026-06-07 16:01:28 +08:00
mirivlad 3f787ec66d fix: only plugins with on_install are managed - skip others entirely
- Discover(): skip plugins without on_install hook (not shown in UI)
- Enable(): simplified - just check Installed flag
- SyncConfig(): simplified - no HasInstall special case
- Frontend: simplified toggle logic (only installed/uninstalled states)
- Tests: updated all test fixtures to include on_install hook
2026-06-07 15:55:04 +08:00
mirivlad 5c769c92a0 fix: simplify plugin lifecycle - no install/uninstall = just toggle
- Plugins without on_install hook: always 'installed', toggle works directly
- Plugins with on_install: must Install first, then toggle, then Uninstall available
- No data cleanup on Disable (only on Uninstall via on_uninstall hook)
- Old plugins without lifecycle hooks simply don't get install/uninstall UI
2026-06-07 15:38:04 +08:00
mirivlad e99ff984b1 feat: plugin install/uninstall lifecycle + UI buttons
- AppConfig: add InstalledPlugins []string
- Manager.Discover(): no config dependency, all plugins start inactive
- Manager.SyncConfig(): apply installed/enabled state from AppConfig
- Manager.Enable(): works for plugins without on_install hook
- Manager.Install/Uninstall(): run on_install/on_uninstall hooks
- ActivatePlugin: skip if HasInstall && !Installed
- ReloadPlugins: Discover → SyncConfig → InitRuntimes
- Bindings: InstallPlugin, UninstallPlugin
- SettingsPlugins: install/uninstall buttons, toggle only after install
- Calendar: migration moved from on_init to on_install, on_uninstall drops tables
- Tests: all 12 pass (manager + runtime + calendar)
2026-06-07 15:28:37 +08:00
mirivlad b80941f908 feat: плагин-система Lua + Calendar reference plugin
- Lua VM runtime: gopher-lua с песочницей, хуки on_init/on_tick/on_shutdown
- API: verstak.node.* / verstak.db.* / verstak.config.* / verstak.state.*
- API: verstak.worklog.* / verstak.activity.* / verstak.file.*
- API: verstak.schedule.* / verstak.http.* / verstak.ui.*
- Менеджер плагинов: жизненный цикл, инициализация, шаблоны
- Scheduler: фоновые задачи с интервалами
- PluginPage.svelte: контейнер для iframe-панелей плагинов
- Calendar plugin: миграция, категории CRUD, события CRUD
- Calendar: расширенный рекарренс (daily/weekly/monthly/yearly)
- Calendar: связь с узлами Верстака, напоминания, HTTP-праздники
- Calendar: Lua-тест-сьют (15 тестов), Go-интеграционный тест
- fix: query_row использует реальные Column() вместо guessColumns
2026-06-07 14:59:46 +08:00
mirivlad 8cbc87cdad feat: настройки Browser Bridge в Verstak и extension
Verstak (GUI):
- SettingsBrowserBridge.svelte — новая секция в Settings:
  toggle вкл/выкл сервера, поле порта (default 9786),
  статус (Запущен/Остановлен), кнопки Сохранить/Перезапустить
- SettingsWindow + SettingsSidebar — подключена секция browserBridge
- BridgeConfig: добавлено поле Enabled (default true)
- RestartBridge() — новый биндинг для перезапуска сервера
- initVault: проверяет bc.Enabled перед запуском bridge

Extension (Chrome + Firefox):
- popup: панель настроек с полем порта (default 9786)
- кнопка «Проверить» — fetch /api/ping с таймаутом 3с
- кнопка «Сохранить» — сохраняет port в chrome.storage.local
- статус соединения: ✓ Сервер отвечает / ✗ Недоступен / ✗ Таймаут
- оба расширения работают только с 127.0.0.1
2026-06-07 01:03:35 +08:00
mirivlad 1cc0c407b1 fix: исправление 6 пунктов из ревью
Critical:
- bridge: AutoGenPort=false по умолчанию, не генерируем secret если пустой
  → extension и bridge совпадают на port 9786 и empty secret
- bridgeConfig: убрана авто-генерация secret, убран secret из BridgeInfo

High:
- extension/background.js + extension-firefox/background.js:
  все chrome.* listeners вынесены в global scope (не внутри onInstalled/onStartup)
  → MV3 service worker корректно перезапускается
- UI: acceptBrowserEvent вызывает AcceptBrowserEvent, attachBrowserEvent вызывает
  AttachBrowserEventToNode (к текущему selectedNode), а не DismissBrowserEvent
- watcher: при Create проверяется isUnderVault(absPath, vaultRoot) —
  если файл уже в vault, используется AddExternal вместо CopyIntoVault
  → нет дублирования файлов с timestamp-суффиксом

Medium:
- bridge.Event: добавлено поле DeviceID, handleEvents обогащает events из batch.DeviceID
  → device_id сохраняется в DB как chrome-*/firefox-*, а не evt_*
- config: FileWatcher изменён на *bool — nil означает default true,
  false = явно выключено → старые config.json без поля file_watcher получают true
2026-06-07 00:15:34 +08:00
mirivlad b676ac675a feat: иконки для Chrome и Firefox расширений
- extension/icons/ + extension-firefox/icons/
  - icon16.png (16x16), icon48.png (48x48), icon128.png (128x128)
  - взяты из frontend/public/assets/app-icons/
  - 48x48 сгенерирован из 64x64 через ImageMagick
- Манифесты: default_icon + icons секции для обоих браузеров
- build.sh: иконки включаются в zip/xpi архивы
2026-06-06 19:21:28 +08:00
mirivlad f6c61c32e3 build: упаковка расширений в scripts/build.sh
- build.sh: новые команды extensions, chrome, firefox
- Chrome: build/verstak-bridge-chrome.zip
- Firefox: build/verstak-bridge-firefox.xpi
- Архивы содержат только нужные файлы (manifest, background, popup)
- Исключены .DS_Store, Thumbs.db, __MACOSX, .git
2026-06-06 19:15:24 +08:00
mirivlad c5505ee43c feat: Firefox-расширение Verstak Bridge
- extension-firefox/manifest.json — Manifest V3 для Firefox
  (browser_specific_settings.gecko, background.scripts)
- extension-firefox/background.js — browser.* API с chrome.* полифиллом
  - Стабильный device_id через crypto.getRandomValues (6 байт hex)
  - Фильтрация about:, moz-extension:, resource: и пр. внутренних URL
  - device_id с префиксом 'firefox-' для различения в activity
- extension-firefox/popup/ — общие popup HTML/CSS/JS (копия Chrome)
2026-06-06 19:08:31 +08:00
mirivlad fc429ac26e feat: ШАГ 4 — UI для browser events в TodayScreen
Go bindings:
- bindings_browser.go: ListBrowserEvents, CountPendingBrowserEvents,
  AcceptBrowserEvent, DismissBrowserEvent, AttachBrowserEventToNode

Frontend:
- BrowserEvents.svelte — компонент списка событий с действиями
  (принять/прикрепить/удалить), статусы, домены, длительность
- TodayScreen.svelte — новая вкладка «Браузер» с badge
- App.svelte — loadBrowserEvents, acceptBrowserEvent,
  dismissBrowserEvent, attachBrowserEvent handlers
2026-06-06 18:58:39 +08:00
mirivlad 6bd6c9c5ff feat: ШАГ 3 — Chrome-расширение Verstak Bridge 2026-06-06 18:28:52 +08:00
mirivlad 84d9725b17 feat: ШАГ 2 — Staging-таблица browser_events + Store 2026-06-06 18:27:00 +08:00
mirivlad 358c649b42 feat: ШАГ 1 — Bridge HTTP-сервер для браузерного расширения
- internal/core/bridge/ — лёгкий HTTP-сервер на 127.0.0.1
  - POST /api/events — приём батча событий от расширения
  - GET /api/ping — healthcheck для расширения
  - X-Verstak-Secret — аутентификация по shared-secret
  - AutoGenPort — случайный порт если 9786 занят
- config.BridgeConfig — порт, секрет, auto_gen_port
- App: интеграция startBridge/stopBridge при open/close vault
- bindings_bridge.go — BridgeInfo(), startBridge(), saveBridgeConfig()
- Тесты: ping, auth, success, empty batch, secret gen, auto-port
2026-06-06 18:23:47 +08:00
mirivlad f88376264d fix: reorder journal worklog sections 2026-06-06 02:53:36 +08:00
mirivlad 40c0953904 fix: skip deleted entries in navigation history 2026-06-06 02:42:20 +08:00
mirivlad 0cd8a79049 feat: restore global search in app header 2026-06-06 02:39:29 +08:00
mirivlad cf770262e5 fix: reset capture drag state reliably 2026-06-06 02:30:54 +08:00
mirivlad 6033ccffa9 Add Verstak agent project rules 2026-06-06 01:33:39 +08:00
mirivlad a37afd3b67 fix: trash integrity for TypeFile nodes — file record soft-delete, correct preview/restore 2026-06-05 17:31:18 +08:00
mirivlad 64e6c6f735 fix: trash file preview, visual CSS, virtual folder model
- Added resolveTrashPath() backend function: walks ancestor chain to find
  files inside deleted/moved folders (flat trash directory)
- Added TrashFsPath to TrashNodeDTO, computed during ListTrash via
  parent-to-child propagation of physical trash path
- Fixed visual CSS for trash rows: button reset (no white bg, transparent,
  inherit font/color), hover styles match app dark theme
- Root view filters out descendant nodes (only shows top-level items)
2026-06-05 17:05:35 +08:00