mirror of
https://github.com/prdlk/x-bookmarks-rss.git
synced 2026-08-02 09:31:37 +00:00
init(config): add initial configuration files
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
# Copy to .dev.vars for `wrangler dev` (gitignored). In production set via `wrangler secret put`.
|
||||
# Only these two are read by the app — they refresh the access token. The refresh token is
|
||||
# minted once (scripts/mint-token.mjs) and stored/rotated in D1, never read from here.
|
||||
X_CLIENT_ID="your-oauth2-client-id"
|
||||
X_CLIENT_SECRET="your-oauth2-client-secret"
|
||||
@@ -0,0 +1,5 @@
|
||||
.env
|
||||
node_modules/
|
||||
.dev.vars
|
||||
.wrangler/
|
||||
dist/
|
||||
@@ -0,0 +1,69 @@
|
||||
# X Bookmarks → RSS
|
||||
|
||||
A tiny [Hono](https://hono.dev) app on Cloudflare Workers that turns your X (Twitter)
|
||||
bookmarks into a live RSS 2.0 feed. Uses the official X API v2 (OAuth 2.0 user context).
|
||||
State lives in a single **D1** database.
|
||||
|
||||
Personal/single-user: the feed is unguarded (`/feed.xml`, no key). Bookmarks are stored in
|
||||
D1 and accumulate, so tweets stay in your feed even after they scroll out of X's 100-item
|
||||
bookmarks API window.
|
||||
|
||||
## How it works
|
||||
|
||||
- There is **no login route**. You seed one OAuth2 refresh token into D1 once; from then on
|
||||
the app refreshes the access token itself on every sync (refresh tokens rotate and the new
|
||||
one is written straight back to D1).
|
||||
- `GET /feed.xml` — if the last sync is older than `SYNC_TTL_SECONDS`, it refreshes the
|
||||
access token, pages through your bookmarks, and upserts them into D1; then it renders RSS
|
||||
from **all** stored rows (newest first). Sync failures are swallowed — the feed still
|
||||
serves what's already stored.
|
||||
|
||||
## Setup
|
||||
|
||||
### 1. Create the D1 database
|
||||
|
||||
```sh
|
||||
npm install
|
||||
wrangler d1 create x-bookmarks-db # paste the printed database_id into wrangler.jsonc
|
||||
npm run db:init # applies schema.sql to the remote DB
|
||||
```
|
||||
|
||||
### 2. Set secrets (used to refresh the token)
|
||||
|
||||
```sh
|
||||
wrangler secret put X_CLIENT_ID
|
||||
wrangler secret put X_CLIENT_SECRET
|
||||
```
|
||||
|
||||
`SYNC_TTL_SECONDS` (default 900) and `MAX_ITEMS` (default 100, items pulled per sync) are
|
||||
plain vars in `wrangler.jsonc`.
|
||||
|
||||
### 3. Seed your refresh token
|
||||
|
||||
You need one OAuth2 refresh token with the `tweet.read users.read bookmark.read
|
||||
offline.access` scopes (this is the only step that requires a one-time authorization — every
|
||||
sync afterward is automatic). Once you have it:
|
||||
|
||||
```sh
|
||||
export X_CLIENT_ID=... # same values as the secrets above
|
||||
export X_CLIENT_SECRET=...
|
||||
scripts/seed-token.sh "<your-refresh-token>"
|
||||
```
|
||||
|
||||
The script validates the token, captures the rotated token, and writes `refresh_token` +
|
||||
`user_id` into the D1 `meta` table.
|
||||
|
||||
### 4. Deploy & subscribe
|
||||
|
||||
```sh
|
||||
wrangler deploy
|
||||
```
|
||||
|
||||
Add `https://<your-worker>.workers.dev/feed.xml` to your RSS reader.
|
||||
|
||||
## Notes
|
||||
|
||||
- Refresh tokens rotate on every use and are written back to D1 immediately.
|
||||
- If syncs start failing (token revoked), re-run `scripts/seed-token.sh` with a fresh token.
|
||||
- The feed is unguarded by design (personal use). Don't host it where you'd mind it being
|
||||
readable, or re-add a key check.
|
||||
Generated
+1654
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "x-bookmarks-rss",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "wrangler dev",
|
||||
"deploy": "wrangler deploy",
|
||||
"db:init": "wrangler d1 execute x-bookmarks-db --remote --file=schema.sql",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"hono": "^4.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cloudflare/workers-types": "^4.20240000.0",
|
||||
"typescript": "^5.6.0",
|
||||
"wrangler": "^3.80.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"lib": ["ES2022"],
|
||||
"types": ["@cloudflare/workers-types"],
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "hono/jsx"
|
||||
},
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"$schema": "node_modules/wrangler/config-schema.json",
|
||||
"name": "x-bookmarks-rss",
|
||||
"main": "src/index.ts",
|
||||
"compatibility_date": "2024-09-01",
|
||||
// D1 holds the bookmarks plus a small meta table (OAuth tokens, user id, last_sync).
|
||||
"d1_databases": [
|
||||
{
|
||||
"binding": "DB",
|
||||
"database_name": "x-bookmarks-db",
|
||||
"database_id": "7548c661-37b4-474f-9584-28d1f6a24d55"
|
||||
}
|
||||
],
|
||||
// Non-secret tunables. Secrets (X_CLIENT_ID/SECRET, X_REFRESH_TOKEN) go via `wrangler secret put`.
|
||||
"vars": {
|
||||
"SYNC_TTL_SECONDS": "900",
|
||||
"MAX_ITEMS": "100"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user