readest/apps/readest-app/worker.ts
Huang Xin 4a9cb331a7
Some checks are pending
CodeQL Advanced / Analyze (actions) (push) Waiting to run
CodeQL Advanced / Analyze (javascript-typescript) (push) Waiting to run
CodeQL Advanced / Analyze (rust) (push) Waiting to run
Publish Docker image / build (linux/amd64, ubuntu-latest) (push) Waiting to run
Publish Docker image / build (linux/arm64, ubuntu-24.04-arm) (push) Waiting to run
Publish Docker image / merge (push) Blocked by required conditions
Build with Nix / tests (push) Waiting to run
PR checks / test_web_app (2) (push) Waiting to run
PR checks / test_extensions (push) Waiting to run
PR checks / nix_flake_check (push) Waiting to run
PR checks / rust_lint (push) Waiting to run
PR checks / koplugin_rust_lint (push) Waiting to run
PR checks / build_web_app (push) Waiting to run
PR checks / test_web_app (1) (push) Waiting to run
PR checks / build_tauri_app (push) Waiting to run
Scorecard supply-chain security / Scorecard analysis (push) Waiting to run
Deploy to vercel on merge / build_and_deploy (push) Waiting to run
feat(tts): move Readest Voice into its own service (#6732)
* feat(tts): move Readest Voice into its own service

The dispatcher, the RunPod client, the worker API and the download result
bucket move to the readest-voice Worker. readest-web reaches it through a
service binding (READEST_VOICE, entrypoint ReadestVoice) and keeps only
sign-in, plan and voice gating, quotas, the catalog and usage charging.

- libs/readestVoice/service.ts: the contract the service implements and
  the binding; a contract version mismatch switches Readest Voice off.
- stream route: admits through the service (quota checked alongside, as
  before), relays its lines, counts and charges what was delivered, and
  ends a window it gives up on with releaseWindow.
- jobs routes: submit, wait, take and cancel through the service; download
  usage is charged when the result is fetched.
- Removed: tts-dispatcher.ts, the workers route, the scheduling and RunPod
  modules. wrangler.toml swaps the Durable Object and R2 bindings for the
  service binding and deletes the old Durable Object class (migration v2).
- readNDJSON takes an AbortSignal that cancels the stream mid-read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(tts): answer service failures in the job and warmup routes

- GET and DELETE /jobs/:id answer JOBS_UNAVAILABLE when a service call
  fails, as submit does, instead of an unhandled error.
- Warmup stays best effort: a failed service call is logged.
- readNDJSON cancels at once when its signal has already aborted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 15:10:19 +02:00

51 lines
2.1 KiB
TypeScript

// Custom Cloudflare Worker entry (wrangler.toml `main`). Wraps the handler that
// `opennextjs-cloudflare build` generates and adds a `scheduled()` handler so
// the Cron Trigger can drive the reading-statistics compaction endpoint
// (POST /api/stats/compact). Pattern from the OpenNext "custom worker" how-to:
// https://opennext.js.org/cloudflare/howtos/custom-worker
//
// `.open-next/worker.js` only exists after the OpenNext build step that
// `pnpm preview` / `pnpm deploy` run first; wrangler bundles this file with it.
// The file sits outside tsconfig's `include`, so it carries its own minimal
// types (the project does not depend on @cloudflare/workers-types).
// biome-ignore lint/suspicious/noTsIgnore: `.open-next/worker.js` only exists after the OpenNext build
// @ts-ignore `.open-next/worker.js` is generated at build time
import { default as handler } from './.open-next/worker.js';
interface CompactEnv {
STATS_COMPACT_TOKEN?: string;
}
interface ExecutionContextLike {
waitUntil(promise: Promise<unknown>): void;
}
const COMPACT_URL = 'https://web.readest.com/api/stats/compact';
export default {
fetch: handler.fetch,
async scheduled(_event: unknown, env: CompactEnv, ctx: ExecutionContextLike) {
// Same code path as a manual run: the route applies the 503/401 guard, so
// a disabled or unconfigured deployment just logs a 503 here.
const req = new Request(COMPACT_URL, {
method: 'POST',
headers: { 'x-compact-token': env.STATS_COMPACT_TOKEN ?? '' },
});
ctx.waitUntil(
handler
.fetch(req, env, ctx)
.then((res: Response) => {
if (!res.ok) console.warn('stats compact cron: status', res.status);
})
.catch((e: unknown) => {
console.error('stats compact cron: failed', e instanceof Error ? e.message : e);
}),
);
},
};
// OpenNext's DO queue / DO tag cache classes (DOQueueHandler, DOShardedTagCache)
// would have to be re-exported here if open-next.config.ts ever enabled them;
// today it only sets the R2 incremental cache, so there is nothing to re-export.