Skip to content

Commit f50abe4

Browse files
feat: v1.3.0 — Workflows backfill, KV caching, security hardening, 49 tests
- Add BackfillWorkflow (Cloudflare Workflows) for durable, retryable backfill - Add /backfill/status endpoint to poll workflow progress - Add KV SQL query caching with SHA-256 keys and 6-hour TTL - Add Analytics Engine integration for query and auth metrics - Add 49 tests (auth, SQL injection, param validation, CORS, etc.) - Harden security: timing-safe comparison, SQL injection prevention, LIMIT capping - Fix syncData to throw on majority failure (retry logic was dead code) - Fix isReadOnlySql false positive on REPLACE() string function - Drop unused tables, add covering indexes, fill migration gap - Bump version to 1.3.0, rewrite README, update CHANGELOG
1 parent af7dc39 commit f50abe4

File tree

12 files changed

+1117
-299
lines changed

12 files changed

+1117
-299
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ dist
168168
.env*
169169
!.env.example
170170
.wrangler/
171+
worker-configuration.d.ts
171172
.ruff_cache/
172173
# OpenCode session context (local only)
173174
.opencode/

CHANGELOG.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,55 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [1.3.0] - 2026-03-01
9+
10+
### Added
11+
12+
- **Cloudflare Workflows for Backfill**: `/backfill` now dispatches a durable `BackfillWorkflow` instead of running inline. Each Oura resource syncs as an isolated step with its own retry budget (3 retries, exponential backoff, 5-minute timeout). Eliminates CPU/subrequest limit concerns for large backfills.
13+
- **`/backfill/status` Endpoint**: Poll workflow progress via `GET /backfill/status?id=<instanceId>`. Returns status (`queued`, `running`, `complete`, `errored`), error details, and structured output with per-resource results.
14+
- **KV SQL Query Caching**: `/api/sql` responses cached in KV with SHA-256 hash keys and 6-hour TTL. Returns `X-Cache: HIT/MISS` header. Cache automatically flushed after cron sync and backfill workflow completion.
15+
- **Analytics Engine Integration**: `OURA_ANALYTICS` binding logs SQL query metrics (execution time, row count, cache hit/miss) and auth attempts (success/failure, IP, country) to Cloudflare Analytics Engine.
16+
- **49 Tests**: Comprehensive test suite covering auth (valid/invalid tokens), SQL injection prevention (INSERT, DELETE, DROP, UPDATE, ALTER, PRAGMA, VACUUM, ATTACH, multi-statement, comment-obfuscated, CTE-wrapped writes), REPLACE() function vs REPLACE INTO, parameter validation (objects, arrays, null, boolean), LIMIT capping (inject/preserve/cap), `/api/daily_summaries` (valid range, invalid dates, defaults), CORS origin rejection, 404 handling, root endpoint.
17+
- **CORS Origin Configuration**: `ALLOWED_ORIGINS` env var for comma-separated CORS origins (default: `https://oura.keith20.dev`, `http://localhost:3000`, `http://localhost:8787`).
18+
- **Date Param Validation**: `/api/daily_summaries` validates `start` and `end` params against `YYYY-MM-DD` regex.
19+
- **SQL Param Validation**: Rejects objects and arrays in SQL params — only primitives (string, number, boolean, null) accepted.
20+
- **Unknown Endpoint Logging**: `saveToD1` logs unknown endpoint names via `KNOWN_ENDPOINTS` set.
21+
- **Stale OAuth State Cleanup**: Cron job deletes OAuth states older than 24 hours.
22+
- **Database Migration `0008_covering_indexes.sql`**: 7 covering indexes for Grafana dashboard queries.
23+
- **Database Migration `0009_drop_unused_tables.sql`**: Drops unused `oura_raw_documents` and `oura_sync_state` tables.
24+
- **Database Migration `0003_placeholder.sql`**: No-op to fill migration numbering gap (0002 → 0004).
25+
26+
### Security
27+
28+
- **Timing-Safe Token Comparison**: Replaced hand-rolled HMAC comparison with `crypto.subtle.timingSafeEqual` (SHA-256 hashes both sides first).
29+
- **`isReadOnlySql()` Fix**: `REPLACE()` string function no longer blocked — only `REPLACE INTO` (write operation) is rejected.
30+
- **LIMIT Capping**: Injects `LIMIT maxRows+1` when absent, caps user-provided LIMIT when it exceeds `maxRows`. Strips trailing SQL comments before LIMIT detection.
31+
- **`MAX_QUERY_ROWS`/`QUERY_TIMEOUT_MS` Validation**: NaN-safe with `Number.isFinite()` checks.
32+
- **Security Headers**: All responses include `X-Content-Type-Options`, `X-Frame-Options`, `X-XSS-Protection`, `Referrer-Policy`, `Content-Security-Policy`.
33+
34+
### Fixed
35+
36+
- **`syncData` Now Throws on Majority Failure**: When >50% of resources fail, `syncData` throws so `retryWithBackoff` in the cron handler can actually retry. Previously it swallowed all errors, making the retry logic dead code.
37+
- **`ingestResource` Error Propagation**: No longer silently returns on token acquisition failure — re-throws so failures are reported.
38+
- **`loadOuraResourcesFromOpenApi` Error Handling**: Now throws on failure instead of returning empty array.
39+
- **`flushSqlCache` Isolated from Cron Retry**: KV cache flush failure can no longer trigger unnecessary data re-sync.
40+
- **`request.cf` Type**: Cast from `any` to `IncomingRequestCfProperties`.
41+
42+
### Changed
43+
44+
- **`/backfill` Endpoint**: Now dispatches to Cloudflare Workflow (returns 202 with instance ID and status URL) instead of running sync inline. All backfill sizes use the Workflow — no more waitUntil/synchronous split.
45+
- **Build-Time Version**: Hardcoded `version: '1.1.0'` in `/health` response replaced with `__APP_VERSION__` injected via wrangler `define` (mirrors `package.json`).
46+
- **`package.json` Name**: Fixed `oura-vault``oura-cf` to match wrangler config.
47+
- **KV `remote: true` Removed**: Local dev no longer hits production KV namespace.
48+
- **`worker-configuration.d.ts`**: Added to `.gitignore` (stale generated file).
49+
- **`saveToD1` Type**: `data` param typed `Record<string, any>[]` instead of `any[]`.
50+
- **Dead Code Cleanup**: Removed unused `ALLOWED_SQL_KEYWORDS` and `ALLOWED_SQL_FUNCTIONS` sets, removed 19 redundant `timestamp: new Date().toISOString()` from log calls.
51+
52+
### Documentation
53+
54+
- **README.md**: Rewritten with Workflows section, security section, backfill status polling examples, updated architecture diagram, updated tables and file listing.
55+
- **Grafana Dashboard**: Removed aggressive auto-refresh intervals (minimum 15 minutes).
56+
857
## [1.2.0] - 2026-02-24
958

1059
### Performance
@@ -294,6 +343,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
294343
- Security review documentation
295344
- Performance optimization guides
296345

346+
[1.3.0]: https://github.com/xxKeith20xx/oura-cf/compare/v1.2.0...v1.3.0
347+
[1.2.0]: https://github.com/xxKeith20xx/oura-cf/compare/v1.1.0...v1.2.0
297348
[1.1.0]: https://github.com/xxKeith20xx/oura-cf/compare/v1.0.5...v1.1.0
298349
[1.0.5]: https://github.com/xxKeith20xx/oura-cf/compare/v1.0.4...v1.0.5
299350
[1.0.4]: https://github.com/xxKeith20xx/oura-cf/compare/v1.0.3...v1.0.4

0 commit comments

Comments
 (0)