fix: harden sync backends and fix edit recompute (#66)
* refactor(oauth): async coroutine support, pure-Lua PKCE, server hardening Problem: OAuth module shelled out to openssl for PKCE, used blocking `vim.system():wait()`, had a weak `os.time()` PRNG seed, and the TCP callback server leaked on read errors with no timeout. Solution: Add `M.system()` coroutine wrapper and `M.async()` helper, replace openssl with `vim.fn.sha256` + `vim.base64.encode`, seed from `vim.uv.hrtime()`, add `close_server()` guard with 120s timeout, and close the server on read errors. * fix(gtasks): async operations, error notifications, buffer refresh Problem: Sync operations blocked the editor, `push_pass` silently dropped delete/update/create API errors, and the buffer was not re-rendered after push/pull/sync. Solution: Wrap `push`, `pull`, `sync` in `oauth.async()`, add `vim.notify` for all `push_pass` failure paths, and re-render the pending buffer after each operation. * fix(init): edit recompute, filter predicates, sync action listing Problem: `M.edit()` skipped `_recompute_counts()` after saving, `compute_hidden_ids` lacked `done`/`pending` predicates, and `run_sync` defaulted to `sync` instead of listing available actions. Solution: Replace `s:save()` with `_save_and_notify()` in `M.edit()`, add `done` and `pending` filter predicates, and list backend actions when no action is specified. * refactor(gcal): per-category calendars, async push, error notifications Problem: gcal used a single hardcoded calendar name, ran synchronously blocking the editor, and silently dropped some API errors. Solution: Fetch all calendars and map categories to calendars (creating on demand), wrap push in `oauth.async()`, notify on individual API failures, track `_gcal_calendar_id` in `_extra`, and remove the `$` anchor from `next_day` pattern. * refactor: formatting fixes, config cleanup, health simplification Problem: Formatter disagreements in `init.lua` and `gtasks.lua`, stale `calendar` field in gcal config, and redundant health checks for data directory existence. Solution: Apply stylua formatting, remove `calendar` field from `pending.GcalConfig`, drop data-dir and no-file health messages, add `done`/`pending` to filter tab-completion candidates. * docs: update vimdoc for sync refactor, remove demo scripts Problem: Docs still referenced openssl dependency, defaulting to `sync` action, and the `calendar` config field. Demo scripts used the old singleton `store` API. Solution: Update vimdoc and README to reflect explicit actions, per- category calendars, and pure-Lua PKCE. Remove stale demo scripts and update sync specs to match new behavior. * fix(types): correct LuaLS annotations in oauth and gcal
This commit is contained in:
parent
e0e3af6787
commit
b7ce1c05ec
13 changed files with 319 additions and 291 deletions
|
|
@ -247,7 +247,9 @@ local function push_pass(access_token, tasklists, s, now_ts, by_gtasks_id)
|
|||
|
||||
if task.status == 'deleted' and gtid and list_id then
|
||||
local err = delete_gtask(access_token, list_id, gtid)
|
||||
if not err then
|
||||
if err then
|
||||
vim.notify('pending.nvim: gtasks delete failed: ' .. err, vim.log.levels.WARN)
|
||||
else
|
||||
if not task._extra then
|
||||
task._extra = {}
|
||||
end
|
||||
|
|
@ -262,7 +264,9 @@ local function push_pass(access_token, tasklists, s, now_ts, by_gtasks_id)
|
|||
elseif task.status ~= 'deleted' then
|
||||
if gtid and list_id then
|
||||
local err = update_gtask(access_token, list_id, gtid, task_to_gtask(task))
|
||||
if not err then
|
||||
if err then
|
||||
vim.notify('pending.nvim: gtasks update failed: ' .. err, vim.log.levels.WARN)
|
||||
else
|
||||
updated = updated + 1
|
||||
end
|
||||
elseif task.status == 'pending' then
|
||||
|
|
@ -270,7 +274,9 @@ local function push_pass(access_token, tasklists, s, now_ts, by_gtasks_id)
|
|||
local lid, err = find_or_create_tasklist(access_token, cat, tasklists)
|
||||
if not err and lid then
|
||||
local new_id, create_err = create_gtask(access_token, lid, task_to_gtask(task))
|
||||
if not create_err and new_id then
|
||||
if create_err then
|
||||
vim.notify('pending.nvim: gtasks create failed: ' .. create_err, vim.log.levels.WARN)
|
||||
elseif new_id then
|
||||
if not task._extra then
|
||||
task._extra = {}
|
||||
end
|
||||
|
|
@ -357,61 +363,79 @@ function M.auth()
|
|||
end
|
||||
|
||||
function M.push()
|
||||
local access_token, tasklists, s, now_ts = sync_setup()
|
||||
if not access_token then
|
||||
return
|
||||
end
|
||||
---@cast tasklists table<string, string>
|
||||
---@cast s pending.Store
|
||||
---@cast now_ts string
|
||||
local by_gtasks_id = build_id_index(s)
|
||||
local created, updated, deleted = push_pass(access_token, tasklists, s, now_ts, by_gtasks_id)
|
||||
s:save()
|
||||
require('pending')._recompute_counts()
|
||||
vim.notify(
|
||||
string.format('pending.nvim: Google Tasks pushed — +%d ~%d -%d', created, updated, deleted)
|
||||
)
|
||||
oauth.async(function()
|
||||
local access_token, tasklists, s, now_ts = sync_setup()
|
||||
if not access_token then
|
||||
return
|
||||
end
|
||||
---@cast tasklists table<string, string>
|
||||
---@cast s pending.Store
|
||||
---@cast now_ts string
|
||||
local by_gtasks_id = build_id_index(s)
|
||||
local created, updated, deleted = push_pass(access_token, tasklists, s, now_ts, by_gtasks_id)
|
||||
s:save()
|
||||
require('pending')._recompute_counts()
|
||||
local buffer = require('pending.buffer')
|
||||
if buffer.bufnr() and vim.api.nvim_buf_is_valid(buffer.bufnr()) then
|
||||
buffer.render(buffer.bufnr())
|
||||
end
|
||||
vim.notify(
|
||||
string.format('pending.nvim: Google Tasks pushed — +%d ~%d -%d', created, updated, deleted)
|
||||
)
|
||||
end)
|
||||
end
|
||||
|
||||
function M.pull()
|
||||
local access_token, tasklists, s, now_ts = sync_setup()
|
||||
if not access_token then
|
||||
return
|
||||
end
|
||||
---@cast tasklists table<string, string>
|
||||
---@cast s pending.Store
|
||||
---@cast now_ts string
|
||||
local by_gtasks_id = build_id_index(s)
|
||||
local created, updated = pull_pass(access_token, tasklists, s, now_ts, by_gtasks_id)
|
||||
s:save()
|
||||
require('pending')._recompute_counts()
|
||||
vim.notify(string.format('pending.nvim: Google Tasks pulled — +%d ~%d', created, updated))
|
||||
oauth.async(function()
|
||||
local access_token, tasklists, s, now_ts = sync_setup()
|
||||
if not access_token then
|
||||
return
|
||||
end
|
||||
---@cast tasklists table<string, string>
|
||||
---@cast s pending.Store
|
||||
---@cast now_ts string
|
||||
local by_gtasks_id = build_id_index(s)
|
||||
local created, updated = pull_pass(access_token, tasklists, s, now_ts, by_gtasks_id)
|
||||
s:save()
|
||||
require('pending')._recompute_counts()
|
||||
local buffer = require('pending.buffer')
|
||||
if buffer.bufnr() and vim.api.nvim_buf_is_valid(buffer.bufnr()) then
|
||||
buffer.render(buffer.bufnr())
|
||||
end
|
||||
vim.notify(string.format('pending.nvim: Google Tasks pulled — +%d ~%d', created, updated))
|
||||
end)
|
||||
end
|
||||
|
||||
function M.sync()
|
||||
local access_token, tasklists, s, now_ts = sync_setup()
|
||||
if not access_token then
|
||||
return
|
||||
end
|
||||
---@cast tasklists table<string, string>
|
||||
---@cast s pending.Store
|
||||
---@cast now_ts string
|
||||
local by_gtasks_id = build_id_index(s)
|
||||
local pushed_create, pushed_update, pushed_delete =
|
||||
push_pass(access_token, tasklists, s, now_ts, by_gtasks_id)
|
||||
local pulled_create, pulled_update = pull_pass(access_token, tasklists, s, now_ts, by_gtasks_id)
|
||||
s:save()
|
||||
require('pending')._recompute_counts()
|
||||
vim.notify(
|
||||
string.format(
|
||||
'pending.nvim: Google Tasks synced — push: +%d ~%d -%d, pull: +%d ~%d',
|
||||
pushed_create,
|
||||
pushed_update,
|
||||
pushed_delete,
|
||||
pulled_create,
|
||||
pulled_update
|
||||
oauth.async(function()
|
||||
local access_token, tasklists, s, now_ts = sync_setup()
|
||||
if not access_token then
|
||||
return
|
||||
end
|
||||
---@cast tasklists table<string, string>
|
||||
---@cast s pending.Store
|
||||
---@cast now_ts string
|
||||
local by_gtasks_id = build_id_index(s)
|
||||
local pushed_create, pushed_update, pushed_delete =
|
||||
push_pass(access_token, tasklists, s, now_ts, by_gtasks_id)
|
||||
local pulled_create, pulled_update = pull_pass(access_token, tasklists, s, now_ts, by_gtasks_id)
|
||||
s:save()
|
||||
require('pending')._recompute_counts()
|
||||
local buffer = require('pending.buffer')
|
||||
if buffer.bufnr() and vim.api.nvim_buf_is_valid(buffer.bufnr()) then
|
||||
buffer.render(buffer.bufnr())
|
||||
end
|
||||
vim.notify(
|
||||
string.format(
|
||||
'pending.nvim: Google Tasks synced — push: +%d ~%d -%d, pull: +%d ~%d',
|
||||
pushed_create,
|
||||
pushed_update,
|
||||
pushed_delete,
|
||||
pulled_create,
|
||||
pulled_update
|
||||
)
|
||||
)
|
||||
)
|
||||
end)
|
||||
end
|
||||
|
||||
M._due_to_rfc3339 = due_to_rfc3339
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue