Feat : Setup cloudflare worker for cloud endpoint

This commit is contained in:
decolua 2026-02-09 11:27:41 +07:00
parent c68b875a36
commit 102c193112
25 changed files with 1739 additions and 82 deletions

View file

@ -0,0 +1,27 @@
/**
* Landing Page Service
* Simple health check page for self-hosted worker
*/
/**
* Create landing page response
* @returns {Response} HTML response
*/
export function createLandingPageResponse() {
const html = `<!DOCTYPE html>
<html><head><title>9Router Worker</title></head>
<body style="font-family:system-ui;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;background:#0a0a0a;color:#fff">
<div style="text-align:center">
<h1>9Router Worker</h1>
<p style="color:#888">Worker is running. Configure this URL in your 9Router dashboard.</p>
</div>
</body></html>`;
return new Response(html, {
status: 200,
headers: {
"Content-Type": "text/html; charset=utf-8",
"Cache-Control": "public, max-age=3600"
}
});
}

View file

@ -0,0 +1,88 @@
import * as log from "../utils/logger.js";
// Request-scoped cache for getMachineData (avoids multiple D1 queries per request)
const requestCache = new Map();
const CACHE_TTL_MS = 5000;
/**
* Get machine data from D1 (with request-scope caching)
* @param {string} machineId
* @param {Object} env
* @returns {Promise<Object|null>}
*/
export async function getMachineData(machineId, env) {
const cached = requestCache.get(machineId);
if (cached && Date.now() - cached.timestamp < CACHE_TTL_MS) {
return cached.data;
}
const row = await env.DB.prepare("SELECT data FROM machines WHERE machineId = ?")
.bind(machineId)
.first();
if (!row) {
log.debug("STORAGE", `Not found: ${machineId}`);
return null;
}
const data = JSON.parse(row.data);
requestCache.set(machineId, { data, timestamp: Date.now() });
log.debug("STORAGE", `Retrieved: ${machineId}`);
return data;
}
/**
* Save machine data to D1
* @param {string} machineId
* @param {Object} data
* @param {Object} env
*/
export async function saveMachineData(machineId, data, env) {
const now = new Date().toISOString();
data.updatedAt = now;
// Upsert to D1
await env.DB.prepare(`
INSERT INTO machines (machineId, data, updatedAt)
VALUES (?, ?, ?)
ON CONFLICT(machineId) DO UPDATE SET data = ?, updatedAt = ?
`)
.bind(machineId, JSON.stringify(data), now, JSON.stringify(data), now)
.run();
// Update cache after save
requestCache.set(machineId, { data, timestamp: Date.now() });
log.debug("STORAGE", `Saved: ${machineId}`);
}
/**
* Delete machine data from D1
* @param {string} machineId
* @param {Object} env
*/
export async function deleteMachineData(machineId, env) {
await env.DB.prepare("DELETE FROM machines WHERE machineId = ?")
.bind(machineId)
.run();
// Clear cache after delete
requestCache.delete(machineId);
log.debug("STORAGE", `Deleted: ${machineId}`);
}
/**
* Update specific fields in machine data (for token refresh, rate limit, etc.)
* @param {string} machineId
* @param {string} connectionId
* @param {Object} updates
* @param {Object} env
*/
export async function updateMachineProvider(machineId, connectionId, updates, env) {
const data = await getMachineData(machineId, env);
if (!data?.providers?.[connectionId]) return;
Object.assign(data.providers[connectionId], updates);
data.providers[connectionId].updatedAt = new Date().toISOString();
await saveMachineData(machineId, data, env);
}

View file

@ -0,0 +1,11 @@
// Re-export from open-sse with worker logger
import * as log from "../utils/logger.js";
import {
TOKEN_EXPIRY_BUFFER_MS as BUFFER_MS,
refreshTokenByProvider as _refreshTokenByProvider
} from "open-sse/services/tokenRefresh.js";
export const TOKEN_EXPIRY_BUFFER_MS = BUFFER_MS;
export const refreshTokenByProvider = (provider, credentials) =>
_refreshTokenByProvider(provider, credentials, log);