feat(sync): add opt-in remote deletion for gcal and gtasks (#85)
Problem: push/sync permanently deleted remote Google Calendar events and Google Tasks entries whenever a local task was marked deleted, done, or de-due'd. There was no opt-out, so a misfire could silently cause irreversible data loss on the remote side. Solution: add a `remote_delete` boolean to the config (default `false`). A unified flag at `sync.remote_delete` sets the base; per-backend overrides at `sync.gcal.remote_delete` / `sync.gtasks.remote_delete` take precedence when non-nil. When disabled, `_extra` remote IDs are cleared silently (unlinking) so stale IDs don't accumulate.
This commit is contained in:
parent
25ad5a6d88
commit
9de53f2bb3
3 changed files with 78 additions and 26 deletions
|
|
@ -7,16 +7,19 @@
|
|||
---@field category string
|
||||
|
||||
---@class pending.GcalConfig
|
||||
---@field remote_delete? boolean
|
||||
---@field credentials_path? string
|
||||
---@field client_id? string
|
||||
---@field client_secret? string
|
||||
|
||||
---@class pending.GtasksConfig
|
||||
---@field remote_delete? boolean
|
||||
---@field credentials_path? string
|
||||
---@field client_id? string
|
||||
---@field client_secret? string
|
||||
|
||||
---@class pending.SyncConfig
|
||||
---@field remote_delete? boolean
|
||||
---@field gcal? pending.GcalConfig
|
||||
---@field gtasks? pending.GtasksConfig
|
||||
|
||||
|
|
|
|||
|
|
@ -129,6 +129,31 @@ local function delete_event(access_token, calendar_id, event_id)
|
|||
return err
|
||||
end
|
||||
|
||||
---@return boolean
|
||||
local function allow_remote_delete()
|
||||
local cfg = config.get()
|
||||
local sync = cfg.sync or {}
|
||||
local per = (sync.gcal or {}) --[[@as pending.GcalConfig]]
|
||||
if per.remote_delete ~= nil then
|
||||
return per.remote_delete == true
|
||||
end
|
||||
return sync.remote_delete == true
|
||||
end
|
||||
|
||||
---@param task pending.Task
|
||||
---@param extra table
|
||||
---@param now_ts string
|
||||
local function unlink_remote(task, extra, now_ts)
|
||||
extra['_gcal_event_id'] = nil
|
||||
extra['_gcal_calendar_id'] = nil
|
||||
if next(extra) == nil then
|
||||
task._extra = nil
|
||||
else
|
||||
task._extra = extra
|
||||
end
|
||||
task.modified = now_ts
|
||||
end
|
||||
|
||||
---@param callback fun(access_token: string): nil
|
||||
local function with_token(callback)
|
||||
oauth.async(function()
|
||||
|
|
@ -150,6 +175,7 @@ function M.push()
|
|||
end
|
||||
|
||||
local s = require('pending').store()
|
||||
local now_ts = os.date('!%Y-%m-%dT%H:%M:%SZ') --[[@as string]]
|
||||
local created, updated, deleted = 0, 0, 0
|
||||
|
||||
for _, task in ipairs(s:tasks()) do
|
||||
|
|
@ -166,19 +192,20 @@ function M.push()
|
|||
)
|
||||
|
||||
if should_delete then
|
||||
local del_err =
|
||||
delete_event(access_token, cal_id --[[@as string]], event_id --[[@as string]])
|
||||
if del_err then
|
||||
log.warn('gcal delete failed: ' .. del_err)
|
||||
else
|
||||
extra['_gcal_event_id'] = nil
|
||||
extra['_gcal_calendar_id'] = nil
|
||||
if next(extra) == nil then
|
||||
task._extra = nil
|
||||
if allow_remote_delete() then
|
||||
local del_err =
|
||||
delete_event(access_token, cal_id --[[@as string]], event_id --[[@as string]])
|
||||
if del_err then
|
||||
log.warn('gcal delete failed: ' .. del_err)
|
||||
else
|
||||
task._extra = extra
|
||||
unlink_remote(task, extra, now_ts)
|
||||
deleted = deleted + 1
|
||||
end
|
||||
task.modified = os.date('!%Y-%m-%dT%H:%M:%SZ') --[[@as string]]
|
||||
else
|
||||
log.debug(
|
||||
'gcal: remote delete skipped (remote_delete disabled) — unlinked task #' .. task.id
|
||||
)
|
||||
unlink_remote(task, extra, now_ts)
|
||||
deleted = deleted + 1
|
||||
end
|
||||
elseif task.status == 'pending' and task.due then
|
||||
|
|
@ -204,7 +231,7 @@ function M.push()
|
|||
end
|
||||
task._extra['_gcal_event_id'] = new_id
|
||||
task._extra['_gcal_calendar_id'] = lid
|
||||
task.modified = os.date('!%Y-%m-%dT%H:%M:%SZ') --[[@as string]]
|
||||
task.modified = now_ts
|
||||
created = created + 1
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -172,6 +172,29 @@ local function parse_notes(notes)
|
|||
return priority, recur, recur_mode
|
||||
end
|
||||
|
||||
---@return boolean
|
||||
local function allow_remote_delete()
|
||||
local cfg = config.get()
|
||||
local sync = cfg.sync or {}
|
||||
local per = (sync.gtasks or {}) --[[@as pending.GtasksConfig]]
|
||||
if per.remote_delete ~= nil then
|
||||
return per.remote_delete == true
|
||||
end
|
||||
return sync.remote_delete == true
|
||||
end
|
||||
|
||||
---@param task pending.Task
|
||||
---@param now_ts string
|
||||
local function unlink_remote(task, now_ts)
|
||||
task._extra['_gtasks_task_id'] = nil
|
||||
task._extra['_gtasks_list_id'] = nil
|
||||
task._extra['_gtasks_synced_at'] = nil
|
||||
if next(task._extra) == nil then
|
||||
task._extra = nil
|
||||
end
|
||||
task.modified = now_ts
|
||||
end
|
||||
|
||||
---@param task pending.Task
|
||||
---@return table
|
||||
local function task_to_gtask(task)
|
||||
|
|
@ -240,21 +263,20 @@ local function push_pass(access_token, tasklists, s, now_ts, by_gtasks_id)
|
|||
local list_id = extra['_gtasks_list_id'] --[[@as string?]]
|
||||
|
||||
if task.status == 'deleted' and gtid and list_id then
|
||||
local err = delete_gtask(access_token, list_id, gtid)
|
||||
if err then
|
||||
log.warn('gtasks delete failed: ' .. err)
|
||||
failed = failed + 1
|
||||
if allow_remote_delete() then
|
||||
local err = delete_gtask(access_token, list_id, gtid)
|
||||
if err then
|
||||
log.warn('gtasks delete failed: ' .. err)
|
||||
failed = failed + 1
|
||||
else
|
||||
unlink_remote(task, now_ts)
|
||||
deleted = deleted + 1
|
||||
end
|
||||
else
|
||||
if not task._extra then
|
||||
task._extra = {}
|
||||
end
|
||||
task._extra['_gtasks_task_id'] = nil
|
||||
task._extra['_gtasks_list_id'] = nil
|
||||
task._extra['_gtasks_synced_at'] = nil
|
||||
if next(task._extra) == nil then
|
||||
task._extra = nil
|
||||
end
|
||||
task.modified = now_ts
|
||||
log.debug(
|
||||
'gtasks: remote delete skipped (remote_delete disabled) — unlinked task #' .. task.id
|
||||
)
|
||||
unlink_remote(task, now_ts)
|
||||
deleted = deleted + 1
|
||||
end
|
||||
elseif task.status ~= 'deleted' then
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue