When Google refuses the stored grant (invalid_grant on refresh, or HTTP 401 from the Calendar API to the access token just refreshed) on any Calendar call - listing, choosing or pushing - the connection becomes reconnect required under the account lock: the refresh token is cleared and last_sync_error set to "Google access was revoked or expired — reconnect". The call answers 409 google_reconnect_required, and later calls answer the same without contacting Google until a reconnect or a disconnect. The chosen calendar and stored calendar event ids are kept: the pushed events are still on that calendar, so forgetEvents is not run. HTTP 429 and 5xx from the token endpoint or the Calendar API are retried at most twice with backoff, then answered 503 google_unavailable, retryable, without clearing or disconnecting anything. Add GET /api/v1/integrations/google, reporting not_connected, connected or reconnect_required with the chosen calendar and last_sync_error. Migration 0015 rebuilds integrations_google so refresh_token_ct can be cleared, with a CHECK that a row without a token carries the reason. Maestro task b8d8da5a-0450-4571-963e-b3dfead24283. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
149 lines
4.6 KiB
SQL
149 lines
4.6 KiB
SQL
-- name: GetHouseholdSettings :one
|
|
-- Returns sql.ErrNoRows until the row exists.
|
|
SELECT * FROM household_settings
|
|
WHERE id = 1;
|
|
|
|
-- name: UpsertHouseholdSettings :one
|
|
-- Creates the single row (id 1) or replaces every column except id and
|
|
-- created_at.
|
|
INSERT INTO household_settings (
|
|
id, timezone, dinner_time, reminder_time, review_time, plan_draft_dow,
|
|
plan_draft_time, suggestions_dow, suggestions_time, chat_autosend_dow,
|
|
chat_autosend_time, onboarding_completed_at, updates_seen_at,
|
|
updated_by, created_at, updated_at
|
|
) VALUES (
|
|
1, ?, ?, ?, ?, ?,
|
|
?, ?, ?, ?,
|
|
?, ?, ?,
|
|
?, ?, ?
|
|
)
|
|
ON CONFLICT (id) DO UPDATE SET
|
|
timezone = excluded.timezone,
|
|
dinner_time = excluded.dinner_time,
|
|
reminder_time = excluded.reminder_time,
|
|
review_time = excluded.review_time,
|
|
plan_draft_dow = excluded.plan_draft_dow,
|
|
plan_draft_time = excluded.plan_draft_time,
|
|
suggestions_dow = excluded.suggestions_dow,
|
|
suggestions_time = excluded.suggestions_time,
|
|
chat_autosend_dow = excluded.chat_autosend_dow,
|
|
chat_autosend_time = excluded.chat_autosend_time,
|
|
onboarding_completed_at = excluded.onboarding_completed_at,
|
|
updates_seen_at = excluded.updates_seen_at,
|
|
updated_by = excluded.updated_by,
|
|
updated_at = excluded.updated_at
|
|
RETURNING *;
|
|
|
|
-- name: GetAISettings :one
|
|
-- Returns sql.ErrNoRows until the row exists.
|
|
SELECT * FROM ai_settings
|
|
WHERE id = 1;
|
|
|
|
-- name: UpsertAISettings :one
|
|
-- Creates the single row (id 1) or replaces every column except id and
|
|
-- created_at.
|
|
INSERT INTO ai_settings (
|
|
id, provider, base_url, model, temperature, system_prompt, api_key_ct,
|
|
api_key_last4, key_verified_at, created_at, updated_at
|
|
) VALUES (
|
|
1, ?, ?, ?, ?, ?, ?,
|
|
?, ?, ?, ?
|
|
)
|
|
ON CONFLICT (id) DO UPDATE SET
|
|
provider = excluded.provider,
|
|
base_url = excluded.base_url,
|
|
model = excluded.model,
|
|
temperature = excluded.temperature,
|
|
system_prompt = excluded.system_prompt,
|
|
api_key_ct = excluded.api_key_ct,
|
|
api_key_last4 = excluded.api_key_last4,
|
|
key_verified_at = excluded.key_verified_at,
|
|
updated_at = excluded.updated_at
|
|
RETURNING *;
|
|
|
|
-- name: GetRecipeSearchSettings :one
|
|
-- Returns sql.ErrNoRows until the row exists.
|
|
SELECT * FROM recipe_search_settings
|
|
WHERE id = 1;
|
|
|
|
-- name: UpsertRecipeSearchSettings :one
|
|
-- Creates the single row (id 1) or replaces every column except id and
|
|
-- created_at.
|
|
INSERT INTO recipe_search_settings (
|
|
id, provider, base_url, api_key_ct, api_key_last4, created_at, updated_at
|
|
) VALUES (
|
|
1, ?, ?, ?, ?, ?, ?
|
|
)
|
|
ON CONFLICT (id) DO UPDATE SET
|
|
provider = excluded.provider,
|
|
base_url = excluded.base_url,
|
|
api_key_ct = excluded.api_key_ct,
|
|
api_key_last4 = excluded.api_key_last4,
|
|
updated_at = excluded.updated_at
|
|
RETURNING *;
|
|
|
|
-- name: GetIntegrationsGoogle :one
|
|
-- Returns sql.ErrNoRows until the row exists.
|
|
SELECT * FROM integrations_google
|
|
WHERE id = 1;
|
|
|
|
-- name: UpsertIntegrationsGoogle :one
|
|
-- Creates the single row (id 1) or replaces every column except id and
|
|
-- created_at.
|
|
INSERT INTO integrations_google (
|
|
id, refresh_token_ct, calendar_id, calendar_name, connected_at,
|
|
last_sync_error, created_at, updated_at
|
|
) VALUES (
|
|
1, ?, ?, ?, ?,
|
|
?, ?, ?
|
|
)
|
|
ON CONFLICT (id) DO UPDATE SET
|
|
refresh_token_ct = excluded.refresh_token_ct,
|
|
calendar_id = excluded.calendar_id,
|
|
calendar_name = excluded.calendar_name,
|
|
connected_at = excluded.connected_at,
|
|
last_sync_error = excluded.last_sync_error,
|
|
updated_at = excluded.updated_at
|
|
RETURNING *;
|
|
|
|
-- name: SetIntegrationsGoogleCalendar :execrows
|
|
-- Sets the chosen calendar; affects no row when nothing is connected.
|
|
UPDATE integrations_google
|
|
SET calendar_id = ?, calendar_name = ?, updated_at = ?
|
|
WHERE id = 1;
|
|
|
|
-- name: ClearIntegrationsGoogleToken :execrows
|
|
-- Records that Google refuses the stored grant: clears the refresh token and
|
|
-- sets last_sync_error, keeping the chosen calendar. Affects no row when
|
|
-- nothing is connected.
|
|
UPDATE integrations_google
|
|
SET refresh_token_ct = NULL, last_sync_error = ?, updated_at = ?
|
|
WHERE id = 1;
|
|
|
|
-- name: DeleteIntegrationsGoogle :exec
|
|
DELETE FROM integrations_google
|
|
WHERE id = 1;
|
|
|
|
-- name: GetChatSettings :one
|
|
-- Returns sql.ErrNoRows until the row exists.
|
|
SELECT * FROM chat_settings
|
|
WHERE id = 1;
|
|
|
|
-- name: UpsertChatSettings :one
|
|
-- Creates the single row (id 1) or replaces every column except id and
|
|
-- created_at.
|
|
INSERT INTO chat_settings (
|
|
id, webhook_url_ct, space_label, last_test_at, last_test_status,
|
|
created_at, updated_at
|
|
) VALUES (
|
|
1, ?, ?, ?, ?,
|
|
?, ?
|
|
)
|
|
ON CONFLICT (id) DO UPDATE SET
|
|
webhook_url_ct = excluded.webhook_url_ct,
|
|
space_label = excluded.space_label,
|
|
last_test_at = excluded.last_test_at,
|
|
last_test_status = excluded.last_test_status,
|
|
updated_at = excluded.updated_at
|
|
RETURNING *;
|