fix(sync): auth and health UX improvements (#75)
Problem: Failed token exchange left credential files on disk, trapping users in a broken auth loop with no way back to setup. The `auth` prompt used raw backend names and a terse prompt string. The `health` action appeared in `:Pending gcal health` tab completion but silently no-oped outside `:checkhealth`. gcal health omitted the token check that gtasks had. Solution: `_exchange_code` now calls `_wipe()` on both failure paths, clearing the token and credentials files so the next `:Pending auth` routes back through `setup()`. Prompt uses full service names and "Authenticate with:". `health` is filtered from sync subcommand completion and dispatch — its home is `:checkhealth pending`. gcal health now checks for tokens.
This commit is contained in:
parent
84437155bc
commit
829afd25b4
4 changed files with 19 additions and 5 deletions
|
|
@ -550,7 +550,7 @@ local function run_sync(backend_name, action)
|
||||||
if not action or action == '' then
|
if not action or action == '' then
|
||||||
local actions = {}
|
local actions = {}
|
||||||
for k, v in pairs(backend) do
|
for k, v in pairs(backend) do
|
||||||
if type(v) == 'function' and k:sub(1, 1) ~= '_' then
|
if type(v) == 'function' and k:sub(1, 1) ~= '_' and k ~= 'health' then
|
||||||
table.insert(actions, k)
|
table.insert(actions, k)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -558,7 +558,7 @@ local function run_sync(backend_name, action)
|
||||||
log.info(backend_name .. ' actions: ' .. table.concat(actions, ', '))
|
log.info(backend_name .. ' actions: ' .. table.concat(actions, ', '))
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if type(backend[action]) ~= 'function' then
|
if action == 'health' or type(backend[action]) ~= 'function' then
|
||||||
log.error(backend_name .. " backend has no '" .. action .. "' action")
|
log.error(backend_name .. " backend has no '" .. action .. "' action")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
@ -831,8 +831,8 @@ end
|
||||||
---@return nil
|
---@return nil
|
||||||
function M.auth()
|
function M.auth()
|
||||||
local oauth = require('pending.sync.oauth')
|
local oauth = require('pending.sync.oauth')
|
||||||
vim.ui.select({ 'gtasks', 'gcal', 'both' }, {
|
vim.ui.select({ 'Google Tasks', 'Google Calendar', 'Google Tasks and Google Calendar' }, {
|
||||||
prompt = 'Authenticate:',
|
prompt = 'Authenticate with:',
|
||||||
}, function(choice)
|
}, function(choice)
|
||||||
if not choice then
|
if not choice then
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -234,6 +234,12 @@ end
|
||||||
---@return nil
|
---@return nil
|
||||||
function M.health()
|
function M.health()
|
||||||
oauth.health(M.name)
|
oauth.health(M.name)
|
||||||
|
local tokens = oauth.google_client:load_tokens()
|
||||||
|
if tokens and tokens.refresh_token then
|
||||||
|
vim.health.ok('gcal tokens found')
|
||||||
|
else
|
||||||
|
vim.health.info('no gcal tokens — run :Pending auth')
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
|
|
||||||
|
|
@ -470,12 +470,14 @@ function OAuthClient:_exchange_code(creds, code, code_verifier, port, on_complet
|
||||||
}, { text = true })
|
}, { text = true })
|
||||||
|
|
||||||
if result.code ~= 0 then
|
if result.code ~= 0 then
|
||||||
|
self:_wipe()
|
||||||
log.error('Token exchange failed.')
|
log.error('Token exchange failed.')
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local ok, decoded = pcall(vim.json.decode, result.stdout or '')
|
local ok, decoded = pcall(vim.json.decode, result.stdout or '')
|
||||||
if not ok or not decoded.access_token then
|
if not ok or not decoded.access_token then
|
||||||
|
self:_wipe()
|
||||||
log.error('Invalid token response.')
|
log.error('Invalid token response.')
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
@ -488,6 +490,12 @@ function OAuthClient:_exchange_code(creds, code, code_verifier, port, on_complet
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@return nil
|
||||||
|
function OAuthClient:_wipe()
|
||||||
|
os.remove(self:token_path())
|
||||||
|
os.remove(vim.fn.stdpath('data') .. '/pending/google_credentials.json')
|
||||||
|
end
|
||||||
|
|
||||||
---@param opts { name: string, scope: string, port: integer, config_key: string }
|
---@param opts { name: string, scope: string, port: integer, config_key: string }
|
||||||
---@return pending.OAuthClient
|
---@return pending.OAuthClient
|
||||||
function M.new(opts)
|
function M.new(opts)
|
||||||
|
|
|
||||||
|
|
@ -216,7 +216,7 @@ end, {
|
||||||
end
|
end
|
||||||
local actions = {}
|
local actions = {}
|
||||||
for k, v in pairs(mod) do
|
for k, v in pairs(mod) do
|
||||||
if type(v) == 'function' and k:sub(1, 1) ~= '_' then
|
if type(v) == 'function' and k:sub(1, 1) ~= '_' and k ~= 'health' then
|
||||||
table.insert(actions, k)
|
table.insert(actions, k)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue