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
This commit is contained in:
2026-06-07 14:59:46 +08:00
parent 8cbc87cdad
commit b80941f908
40 changed files with 4366 additions and 233 deletions
@@ -0,0 +1,10 @@
CREATE TABLE IF NOT EXISTS calendar_events (
id TEXT PRIMARY KEY,
title TEXT NOT NULL,
date TEXT NOT NULL,
time TEXT,
location TEXT,
node_id TEXT,
color TEXT DEFAULT 'blue',
created_at TEXT DEFAULT (datetime('now'))
);
@@ -0,0 +1,36 @@
CREATE TABLE IF NOT EXISTS categories (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
color TEXT NOT NULL DEFAULT '#6b7280',
icon TEXT NOT NULL DEFAULT '📌',
sort_order INTEGER NOT NULL DEFAULT 0,
deleted INTEGER NOT NULL DEFAULT 0,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS events (
id TEXT PRIMARY KEY,
title TEXT NOT NULL,
description TEXT NOT NULL DEFAULT '',
start TEXT NOT NULL,
end TEXT NOT NULL,
all_day INTEGER NOT NULL DEFAULT 0,
category_id TEXT REFERENCES categories(id),
color TEXT NOT NULL DEFAULT '#6b7280',
node_id TEXT,
link_type TEXT DEFAULT 'node',
recurring_rule TEXT,
reminder_minutes TEXT DEFAULT '[]',
completed INTEGER NOT NULL DEFAULT 0,
completed_at TEXT,
source_series TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_events_start ON events(start);
CREATE INDEX IF NOT EXISTS idx_events_end ON events(end);
CREATE INDEX IF NOT EXISTS idx_events_node_id ON events(node_id);
CREATE INDEX IF NOT EXISTS idx_events_category_id ON events(category_id);
CREATE INDEX IF NOT EXISTS idx_categories_deleted ON categories(deleted);