#!/usr/bin/env bash # Seed a fresh X OAuth2 refresh token into D1. Usage: scripts/seed-token.sh # Refreshing rotates the token, so we capture the ROTATED one and store that — never the input. set -euo pipefail cd "$(dirname "$0")/.." IN_RT="${1:?usage: seed-token.sh }" CID="${X_CLIENT_ID:?set X_CLIENT_ID}" CSEC="${X_CLIENT_SECRET:?set X_CLIENT_SECRET}" DB="x-bookmarks-db" echo "== refreshing (validates token + rotates it) ==" RESP=$(curl -sS -X POST https://api.x.com/2/oauth2/token -u "$CID:$CSEC" \ -H "Content-Type: application/x-www-form-urlencoded" \ --data-urlencode "grant_type=refresh_token" \ --data-urlencode "refresh_token=$IN_RT" \ --data-urlencode "client_id=$CID") AT=$(echo "$RESP" | python3 -c 'import sys,json;print(json.load(sys.stdin).get("access_token",""))') RT=$(echo "$RESP" | python3 -c 'import sys,json;print(json.load(sys.stdin).get("refresh_token",""))') SCOPE=$(echo "$RESP" | python3 -c 'import sys,json;print(json.load(sys.stdin).get("scope",""))') if [ -z "$AT" ] || [ -z "$RT" ]; then echo "!! refresh failed: $RESP"; exit 1 fi # Back up the rotated token immediately — single-use, must survive any later check. printf '%s' "$RT" > ./.rotated_refresh_token.txt echo "(rotated token backed up to .rotated_refresh_token.txt)" echo "scope: $SCOPE" case "$SCOPE" in *bookmark.read*) ;; *) echo "!! token lacks bookmark.read scope, aborting"; exit 1;; esac echo "== resolving user id ==" USERID=$(curl -sS https://api.x.com/2/users/me -H "Authorization: Bearer $AT" \ | python3 -c 'import sys,json;print(json.load(sys.stdin).get("data",{}).get("id",""))') [ -n "$USERID" ] || { echo "!! could not resolve user id"; exit 1; } echo "user id: $USERID" echo "== seeding D1 meta ==" SQL="INSERT INTO meta (key,value) VALUES ('refresh_token','$RT') ON CONFLICT(key) DO UPDATE SET value=excluded.value; INSERT INTO meta (key,value) VALUES ('user_id','$USERID') ON CONFLICT(key) DO UPDATE SET value=excluded.value; DELETE FROM meta WHERE key='last_sync';" npx wrangler d1 execute "$DB" --remote --command "$SQL" >/dev/null echo "== done: refresh_token + user_id seeded, last_sync cleared =="