Commit Graph

60 Commits (d285f9ad8b7fae2e76655bfc9ad2747d69f79a52)

Author SHA1 Message Date
mirivlad d285f9ad8b sync_apply FS-first rewrite; CreateNodeFromTemplate rollback; DeleteNodeAndChildren fail on trash errors; PLAN.md update
- applyRemoteNodeUpdate: FS-first with SafeVaultPath validation, must-fail os.Rename
- applyRemoteNodeMove: FS-first for folders and notes/files
- moveNodeFiles: rewritten FS-first with atomic DB transaction
- applyRemoteNoteMove: delegates to moveNodeFiles
- CreateNodeFromTemplate: rollbackChildren on any child creation failure
- DeleteToTrash: skip rename if source file already missing
- DeleteNodeAndChildren: fail on deleteFileRecords errors and trash move failures
- docs/PLAN.md: update step 14 status with known gaps
2026-06-03 02:22:49 +08:00
mirivlad 7e38ffed7b bindings_nodes: fix parent variable redeclaration (rename to parentVal) 2026-06-03 02:18:10 +08:00
mirivlad a31f5fd702 fix: third stabilization pass — template children as nodes, atomicity, fs_path validation, sync_apply compat, smoke test 2026-06-03 02:05:53 +08:00
mirivlad 49c0fda61c chore: add wails.json, remove wails3 artifacts, rebuild binaries
- Add wails.json for Wails v2 build
- Remove wails3 boilerplate (build/Taskfile.yml, build/config.yml, etc.)
- Add server-data/ to .gitignore
- Rebuild frontend-dist and GUI binary
2026-06-03 01:48:12 +08:00
mirivlad 7b2a1da529 fix: note/file move ops, rename/move atomicity, importDir folder creation
- importDir: create physical folder for imported directories
- RenameNode/MoveNode: os.Rename before DB updates (atomicity)
- RenameNode note/file: fail if physical file missing
- MoveNode: file renames before DB updates
- applyRemoteNoteOp: handle OpMove for notes/files
- applyRemoteNodeMove: handle notes/files with empty FsPath
- MoveNode sync payload: no fs_path for notes/files
- Add 7 tests covering all fixes
2026-06-03 01:32:47 +08:00
mirivlad 20a05569ac fix: второй стабилизационный pass vault layout — sync payload, bindings, vaultPath, tests
sync_apply.go:
- applyRemoteNodeCreate: полный payload (template_id/fs_path/sort_order/archived),
  INSERT сохраняет все поля, для folder-like создаётся физическая папка.
- applyRemoteNodeUpdate: принимает fs_path/template_id/archived,
  физическое переименование папки при изменении title/fs_path.
- applyRemoteNodeMove: принимает fs_path, обновляет parent_id+fs_path,
  физически перемещает папку (folder-like) или file record (note/file).

bindings_nodes.go:
- MoveNode: node.FsPath = newFsPath после UpdateFsPath;
  sync.RecordOp отправляет новый fs_path; note/file move to root — файл в vault root.
- RenameNode: EntityFile для file, EntityNote для note;
  коллизия → генерация уникального имени; файл переименовывается только после os.Rename.
- DeleteNode: единый вызов a.files.DeleteNodeAndChildren(), дублирование удалено.
- Исправлен deadlock с SetMaxOpenConns(1) — Query/Exec больше не конфликтуют.

files.Service.vaultPath: filepath.Rel-based проверка,
  sibling-prefix escape (/tmp/vault vs /tmp/vault_evil) отклоняется.

VaultCheck: SQL JOIN с n.deleted_at IS NULL, чтобы удалённые узлы
  не показывались как missing files.

Добавлены тесты: RenameFileNodeUsesEntityFile, MoveNoteToRoot,
  DeleteFolderLeavesVaultCheckHealthy, SyncNodeCreatePreservesFields,
  VaultPathSiblingPrefixEscape.
2026-06-02 17:03:05 +08:00
mirivlad 66c5c81f39 fix: стабилизация vault layout — rename/move/delete note/file vs folder разведены, sync apply без spaces/, VaultCheck усилен
- RenameNode/DeleteNode/MoveNode: note/file и folder-like nodes
  обрабатываются по-разному (file record vs физическая папка)
- DeleteNode: рекурсивный soft-delete всех descendants
- SafeVaultPath возвращает clean relative, filepath.Join в sync_apply.go
- Fallback spaces/ → .verstak/remote-inbox в applyRemoteNoteCreate
- VaultCheck: проверка parent_id != nil, orphan descendants,
  fs_path folder на диске
2026-06-02 16:36:43 +08:00
mirivlad 4f01f2de2e fix: complete vault layout transition — fs_path everywhere, no more spaces/
- notes.Create(): .md files stored in parent node's fs_path folder
- files.CopyIntoVault/CreateEmptyFile/Duplicate: use parent fs_path
- files.AddPathCopy/AddPathLink: use parent fs_path, set folder fs_path
- files.DeleteNodeAndChildren: move physical folder to .verstak/trash
- UpdateFsPathRecursive: use SafeDisplayNameToPathSegment(child.Title)
- sync_apply.go note ops: use fs_path instead of spaces/
- internal/gui/server.go file upload: use n.FsPath instead of nodeSlug
- VaultCheck diagnostic: walk nodes/files, verify paths on disk
- Tests: create/rename/move/delete/name-conflict/vault-check all pass
2026-06-02 15:43:40 +08:00
mirivlad 0b26f7e5b3 refactor: implement template-driven node tree and human-readable vault layout
Unified Node model: added template_id, fs_path, archived, sort_order fields.
Template registry: system templates embedded as JSON (folder/project/client/
document/recipe), with Registry for enabled/disabled/filtered access.
SafeDisplayNameToPathSegment: human-readable path segments with Cyrillic
support, illegal char replacement, uniqueness via numeric suffixes.
Sidebar refactored: system views (Today/Inbox/Activity) separate from
workspace tree. Creation menu built dynamically from enabled templates.
Create/Rename/Move: physical folder operations with fs_path update,
recursive descendant path updates.
DB migration 012: adds template_id, fs_path, archived columns.
Vault migration command: rebuilds fs_path for existing nodes.
Tests: safename, registry, node model, repository integration.
Docs: VAULT_LAYOUT.md, TEMPLATES.md, PLAN.md updated.
i18n: nav.system, nav.workspace, template.*, common.rename/archive,
migrate.* keys added to ru.json and en.json.
2026-06-02 12:47:06 +08:00
mirivlad 12f2916a24 followup: SafeVaultPath in note update, email i18n, strict check-i18n.sh
- applyRemoteNoteUpdate: use SafeVaultPath for vault mode, skip non-vault with log
- Email subjects/bodies moved to Go i18n (confirm + reset) in ru.json and en.json
- check-i18n.sh: ru/en key mismatch now FAIL (not WARNING)
- All builds, tests, frontend pass
2026-06-02 11:40:27 +08:00
mirivlad 7091397649 server i18n: move inline HTML to templates.go, localize all handler strings
- Admin & user dashboard HTML moved from handlers to templates.go with i18n.T()
- SafeVaultPath applied in sync_apply.go (note/file create/update, blob restore)
- DeleteNode/RenameNode/MoveNode fixed: correct activity type / entity variant
- Added TypeNoteDeleted, TypeNodeDeleted, TypeFolderMoved activity constants
- Added locale() helper on Server struct, removed hardcoded 'ru' in handlers
- Password policy loosened: 8-256 chars, any characters, machine-readable error codes
- check-i18n.sh: Go Cyrillic = FAIL with explicit exception list, Go locale key consistency check
2026-06-02 11:26:54 +08:00
mirivlad 2fa583d157 stabilization: server.go split + i18n templates + frontend localization
cmd/verstak-server/server.go (2838→127 строк): разделён на 12 файлов
- config.go, tokens.go, schema.go
- server.go (только struct + NewServer + ListenAndServe)
- routes.go, middleware.go, smtp.go
- handlers_api.go, handlers_user.go, handlers_web_user.go, handlers_admin.go
- templates.go (конвертирован в функции с i18n.T())

frontend: все русские строки заменены на t() вызовы
- App.svelte, FileTreeRow.svelte, ConfirmModal.svelte
- FilePreviewModal.svelte, fileUtils.js

core: gofmt по всему проекту

Все сборки (CLI, server, gui, frontend), go vet, go test проходят.
check-i18n.sh: frontend чист, Go-файлы с кириллицей — только тесты/легаси.
2026-06-02 11:08:29 +08:00
mirivlad 3089d777a8 refactor(gui): разделить app.go на binding-файлы по доменам, вынести sync apply
- app.go (1810→280 строк): только App struct, startup, DTOs, helpers
- bindings_{nodes,files,notes,actions,worklog,activity,sync,settings}.go
- sync_apply.go: все applyRemote* методы
- i18n: internal/i18n (Go, embed JSON) + frontend/src/lib/i18n (JS)
- core/sync/safe_path.go: SafeVaultPath
- scripts/check-i18n.sh: проверка хардкода кириллицы и bidi-символов
- build.sh: NVM loading, set -e

Все сборки (CLI, server, gui, frontend), go vet, go test проходят.
2026-06-02 10:47:38 +08:00
mirivlad 4a96aa3468 fix(sync): expand payloads, implement ApplyRemoteOp, fix SyncTestConnection and auto sync
- Expand all RecordOp payloads with full entity data needed for reconstruction
- Add applyRemoteOp with handlers for node/note/file/action/worklog CRUD
- SyncNow() now applies pulled ops to real DB tables + sets LastSeenServerSeq
- SyncTestConnection uses /api/auth/test instead of PairDevice
- autoSyncLoop respects configured interval with lastSync timing
- Add helper functions: nodePayload, notePayload, filePayload, actionPayload, worklogPayload
2026-06-02 08:02:15 +08:00
mirivlad f8f9510e2a fix(sync): add /api/auth/test endpoint, fix CSS %& vet warnings
- Add handleAuthTest endpoint that validates credentials without creating a device
- Fix resetPasswordHTML to use {TOKEN} placeholder instead of %s
- Remove fmt.Sprintf from admin dashboard (no format args needed)
2026-06-02 08:02:11 +08:00
mirivlad 852d6d373c fix(sync): send LastSeenServerSeq from CLI push, report conflicts 2026-06-02 08:02:07 +08:00
mirivlad 87c8dfcbea sync: overhaul sync system — device pairing, server_sequence, auto-sync, dashboards
BREAKING: replace legacy API keys with device tokens via pairing flow.
- Server: /api/client/pair, revoke, me endpoints; server_sequence + tombstones + idempotency
- Desktop client: PairDevice, GetMe, RevokeCurrent; auto-sync loop every 60s
- Config: device_token stored in separate file (0600), not config.yml
- Client DB: last_pull_seq migration for incremental pull
- Frontend (Svelte): settings modal with connect/disconnect/interval
- User dashboard (/dashboard): device list with status, revoke with password
- Admin dashboard (/admin/dashboard): devices table from /admin/api/devices
- CLI (cmd/verstak): updated for ServerSequence/GetState changes
- Fix: autoSyncLoop falls back to SQLite sync_state for server URL
- Fix: SyncSetInterval preserves server_url/device_id from SQLite
2026-06-02 02:26:05 +08:00
mirivlad 7fe02fc8df feat: forgot/reset password pages, login link, consistent error page helper, fix reset URL bug 2026-06-02 00:43:28 +08:00
mirivlad b0d992b0d6 fix: rebuild GUI with login/password sync fields, make sync buttons more visible 2026-06-02 00:37:31 +08:00
mirivlad e5860ca076 feat: styled registration/confirm pages with login link, consistent theme 2026-06-02 00:31:53 +08:00
mirivlad daed8e0aba feat: SMTP security selector (none/STARTTLS/TLS) instead of port-based detection 2026-06-02 00:18:04 +08:00
mirivlad fa6f988368 fix: SMTP test send JSON instead of multipart FormData (ParseForm can't read multipart) 2026-06-02 00:14:52 +08:00
mirivlad c8cdb089a6 feat: SMTP test button in admin modal — sends real test email, shows result 2026-06-02 00:12:41 +08:00
mirivlad 4afcc0e135 feat: add SMTP/logging — log.Printf for smtpSend errors, fix confirm URL logic 2026-06-02 00:10:04 +08:00
mirivlad 61928cf28e fix: restore side-by-side layout for stat counters 2026-06-02 00:04:56 +08:00
mirivlad 04af88940b refactor: SMTP form and health check into modals with toolbar buttons 2026-06-02 00:03:35 +08:00
mirivlad 0f5c584c50 fix: admin dashboard format errors — use JS for stats, string concat for SMTP values, fix layout overlap 2026-06-01 23:59:15 +08:00
mirivlad 99e47fcb17 feat: add user registration web form at /register 2026-06-01 23:46:25 +08:00
mirivlad 0ef54c31f8 feat: user web GUI — login, dashboard with devices/keys, logout 2026-06-01 23:40:48 +08:00
mirivlad f8dc436709 feat: client auth — login/password flow, auto device reg, sync interval + improved sync UI 2026-06-01 23:36:19 +08:00
mirivlad 241a9d8c06 feat: user registration, email confirmation, login, device management + SMTP config UI in admin panel 2026-06-01 23:33:58 +08:00
mirivlad 5db3da3618 fix: protect device register with admin auth; improve admin UI (full API key, copy button, styling) 2026-06-01 23:22:19 +08:00
mirivlad 1abe8c4fa0 cli: add sync push/pull/status commands 2026-06-01 22:56:05 +08:00
mirivlad 5b2cec5bcc sync: fix SyncStatus binding — remove invalid type assertion, use config for device ID 2026-06-01 22:55:50 +08:00
mirivlad 1a20edac44 feat: sync — client ops recording in core services
- internal/core/sync/: Service, Client, Blob packages

- RecordOp creates sync_ops entries for all mutations

- Client for push/pull/blob HTTP to server

- Blob SHA-256 hashing and local storage

- Wired into app.go alongside activity recording

- Device ID from config or fallback
2026-06-01 22:54:23 +08:00
mirivlad ad684eb118 feat: sync — push/pull API endpoints
- POST /api/v1/sync/push — accepts ops, assigns revisions, returns accepted list

- POST /api/v1/sync/pull — returns ops since given revision with server_revision
2026-06-01 22:51:30 +08:00
mirivlad 10c6d06e38 feat: sync — blob upload/download with SHA-256 storage
- POST /api/v1/blobs/ — multipart upload, stored as blobs/ab/cd/sha256

- GET /api/v1/blobs/{sha256} — download by hash

- server_blobs table for tracking stored blobs
2026-06-01 22:50:38 +08:00
mirivlad ec928e3be6 feat: sync — systemd unit and install.sh for server deployment
- verstak-server.service — systemd unit with sandboxing, configurable port via env

- install.sh — creates user, installs binary, sets up admin, enables service

  Usage: sudo ./install.sh --admin-user admin --admin-pass secret [--port 47732]
2026-06-01 22:49:40 +08:00
mirivlad 834b5ef0d4 feat: sync — server skeleton with health, admin login/dashboard, device registration
- cmd/verstak-server/main.go — flags: --port, --data, --admin-user, --admin-pass

- Server DB schema: server_devices, server_revisions, server_ops

- Health endpoint GET /api/v1/health

- Admin login page + session cookie auth

- Admin dashboard with device stats and API key management

- Device registration POST /api/v1/device/register

- Stub push/pull/blob endpoints
2026-06-01 22:49:02 +08:00
mirivlad 996322f3a9 gui: actions CRUD + FromTemplate bindings + UI
- CreateAction / DeleteAction Wails bindings

- FromTemplate / ListTemplates bindings with recursive tree creation

- Plugin manager stored in App struct for template access

- Action creation modal (title, kind, data) in Overview and Actions tabs

- Delete action button on action cards

- Template selector in new-node dialog
2026-06-01 22:17:18 +08:00
mirivlad a098cf721c add missing Wails bindings for ListActivityFeed, ListActivityByNode, CountActivityByNode 2026-06-01 22:00:58 +08:00
mirivlad 3672e3133b activity: global feed, per-case log, sidebar section, today UX
- migration 009: target_type, target_id, target_path columns
- new Event fields: TargetType, TargetID, TargetPath
- ListActivityFeed (paginated global), ListActivityByNode (per-case)
- all Record() callsites pass target info
- frontend: Активность sidebar section with chronological feed
- per-case Активность tab with real data (was placeholder)
- today events: clickable, target-type badges, event counts
2026-06-01 02:53:56 +08:00
mirivlad 5a1c4c6d7f simplify ListTodayView: remove fallback table queries, pure activity_events + ListTodayNodes
TodayView now uses only activity_events (source of truth) + ListTodayNodes (ensure changed cases appear). Removed direct queries to nodes (notes) and files tables — those will come from activity_events going forward.
2026-06-01 02:45:55 +08:00
mirivlad c74fa3ad43 today dashboard: activity_events, ListTodayView with events timeline, frontend TodayDashboard separated from sidebar 2026-06-01 02:16:13 +08:00
mirivlad 69891e395c Today view and Inbox section: dynamic query, not stored sections
- ListTodayView() on backend: queries nodes created or updated today
  using local timezone day boundaries
- todayBoundaries() helper returns start/end of current day in RFC3339
- Section validation: Create() rejects today and inbox as node sections
- validSections moved from repository.go to types.go with IsValidSection
  and IsServiceSection helpers
- Frontend selectSection('today') calls ListTodayView instead of
  ListNodesBySection('today')
- FAB (create node) hidden in today and inbox sections
- CreateNode in app.go guarded against today/inbox sections
- types.go: today/inbox defined as service sections (sidebar only)
2026-06-01 01:39:02 +08:00
mirivlad a4ae22c445 File manager: selection, keyboard shortcuts, rename modal, security, localization
- Selection model: click=select, dblclick=open, Ctrl+click toggle,
  Shift+click range select, Ctrl+A, Esc to clear
- Keyboard shortcuts: Enter, Ctrl+Enter, F2, Backspace (navigate up),
  Delete/Backspace (delete with confirm)
- Rename modal replaces prompt() with validation via backend ValidateName
- Context menu: Open, Open External, Show in Folder, Rename, Duplicate,
  Copy, Cut, Delete — all with SVG icons and Russian labels
- Backend security: vaultPath/absPathSafe helpers prevent path traversal,
  validateName rejects .. / \ null bytes empty overlong names
- MoveNode auto-renames on name conflict (copy style)
- Duplicate uses (copy) (copy 2) suffix pattern
- Russian localization: all file type labels, preview messages, tooltips
- FilePreviewModal: fixed broken {/if} tag
2026-06-01 01:35:45 +08:00
mirivlad 2487d3bbaa Files tab: multi-selection, drag-and-drop, keyboard shortcuts, custom confirm modal, SVG icons 2026-06-01 01:16:51 +08:00
mirivlad 645d8878cc gui: complete Wails v2 vertical MVP — fixes, search, polished UI
Backend fixes:
- Wire search service in App struct, implement Search() bindings
- Fix OpenFile to use files.Service.Open() instead of stub
- Fix OpenFolder to open spaces/<slug>/ instead of vault root
- Remove unused imports and dead code in app.go

Frontend fixes:
- Add missing Svelte plugin to vite.config.js (blocking build error)
- Fix optional catch binding for compatibility
- Fix select dropdown rendering on Linux (appearance: none + custom arrow)
- Switch api/verstak.js to use generated Wails v2 bindings
- Include hand-written wailsjs bindings in repository
- Add build.sh to repository

Build:
  cd frontend && npm run build
  rm -rf cmd/verstak-gui/frontend-dist && cp -r frontend/dist cmd/verstak-gui/frontend-dist
  go build -tags 'gui production webkit2_41' -o verstak-gui ./cmd/verstak-gui
2026-05-31 23:48:38 +08:00
mirivlad b4010a5a24 gui: Wails v2 vertical MVP — bindings + UI
Go bindings (cmd/verstak-gui/app.go):
- Wire notes.Service, files.Service, actions.Service, worklog.Service
- CreateNote, ListNotes, ReadNote, SaveNote
- ListFiles, ListActions, RunAction
- ListWorklog, CreateWorklog
- Fix DTO mappings for core types

Frontend (frontend/src/):
- api/verstak.js: Wails v2 API wrapper (window.go.main.App)
- App.svelte: full rewrite with sidebar + 6 tabs + notes editor
- Section filtering → nodes by section
- Notes: create, open textarea editor, save, dirty tracking
- Quick actions, worklog entries, empty states
- Node creation modal with section select

Build:
  cd frontend && npm run build
  rm -rf cmd/verstak-gui/frontend-dist && cp -r frontend/dist cmd/verstak-gui/frontend-dist
  go build -tags 'gui production webkit2_41' -o verstak-gui ./cmd/verstak-gui
  ./verstak-gui
2026-05-31 20:37:46 +08:00
mirivlad ee503c338f gui: fix layout — full viewport, dark theme, sidebar+main
Problem: UI appeared as narrow dark panel on white background with scrollbar.

Root causes:
- html/body had no reset — default browser margin/padding = white borders
- index.html referenced non-existent /style.css
- .app used height:100vh but no width:100vw or overflow:hidden
- sidebar used fixed width instead of flexbox

Fixed:
- index.html: added critical CSS reset (html/body/#app = 100%, margin:0, overflow:hidden, dark bg)
- index.html: removed /style.css ref, changed lang to ru, title to Верстак
- App.svelte: complete layout rewrite
  - .app = flex, 100vw x 100vh, overflow:hidden
  - sidebar = 260px width, full height, flex column
  - main = flex:1, full height, flex column (header + content)
  - sidebar nav with sections + nodes using <button> elements
  - content area fills remaining space
  - proper dark theme colors throughout
- Rebuilt frontend/dist and cmd/verstak-gui/frontend-dist
2026-05-31 19:54:07 +08:00