Skip to content

Commit 872232b

Browse files
committed
fix(ci,oauth,db): crawl fallback, proxy secret, querylayer auto
1 parent 173d4ff commit 872232b

4 files changed

Lines changed: 46 additions & 10 deletions

File tree

.github/workflows/deploy-pages.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,21 @@ jobs:
4242
- name: Crawl JWXT snapshots (SSG bundled)
4343
# Cloud-side fallback only: prefer frontend-first runtime.
4444
# This step updates SSG-bundled snapshots under `app/static/crawler/data/**`.
45+
continue-on-error: true
46+
timeout-minutes: 15
4547
run: |
4648
if [ -z "${JWXT_USERID:-}" ] || [ -z "${JWXT_PASSWORD:-}" ]; then
4749
echo "JWXT secrets not set; skip crawl"
4850
exit 0
4951
fi
52+
set +e
5053
npm run crawl:jwxt
54+
code="$?"
55+
set -e
56+
if [ "$code" -ne 0 ]; then
57+
echo "JWXT crawl failed (code=$code); continue using existing snapshots"
58+
exit 0
59+
fi
5160
env:
5261
JWXT_USERID: ${{ secrets.JWXT_USERID }}
5362
JWXT_PASSWORD: ${{ secrets.JWXT_PASSWORD }}

app/src/config/queryLayer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export interface QueryLayerConfig {
1313
const DEFAULT_CONFIG: QueryLayerConfig = {
1414
// NOTE: QueryLayer backs termState/actionLog/solverResult persistence.
1515
// Default to DuckDB-Wasm; browser persistence is provided via OPFS-backed db path.
16-
engine: resolveEngine(import.meta.env?.VITE_QUERY_LAYER_ENGINE ?? 'duckdb'),
16+
engine: resolveEngine(import.meta.env?.VITE_QUERY_LAYER_ENGINE ?? 'auto'),
1717
lazyInit: true,
1818
strictEngine: resolveBool(import.meta.env?.VITE_QUERY_LAYER_STRICT_ENGINE)
1919
};

app/src/lib/data/db/createQueryLayer.ts

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export interface QueryLayer {
1212

1313
let dbPromise: Promise<QueryLayer> | null = null;
1414
let currentEngine: QueryLayerConfig['engine'] | null = null;
15+
let duckdbFallbackWarned = false;
1516

1617
const duckdbWasmMvp = new URL('@duckdb/duckdb-wasm/dist/duckdb-mvp.wasm', import.meta.url).href;
1718
const duckdbWasmEh = new URL('@duckdb/duckdb-wasm/dist/duckdb-eh.wasm', import.meta.url).href;
@@ -96,6 +97,22 @@ async function instantiateLayer(config: QueryLayerConfig): Promise<QueryLayer> {
9697
// breaking term_state persistence and to keep the app usable.
9798
return /^DUCKDB_OPFS_(UNSUPPORTED|LOCKED|OPEN_FAILED)/.test(msg);
9899
};
100+
101+
const warnDuckdbFallback = (error: unknown) => {
102+
if (duckdbFallbackWarned) return;
103+
duckdbFallbackWarned = true;
104+
console.warn('[DB] DuckDB-Wasm 初始化失败,回退至 sql.js', toErrorInfo(error));
105+
};
106+
107+
const canUseDuckdbOpfs = () => {
108+
if (!isBrowserEnv()) return false;
109+
try {
110+
// DuckDB OPFS persistence relies on OPFS Sync Access Handles.
111+
return typeof (globalThis as any).FileSystemFileHandle?.prototype?.createSyncAccessHandle === 'function';
112+
} catch {
113+
return false;
114+
}
115+
};
99116
if (currentEngine && config.engine === currentEngine && dbPromise) {
100117
return dbPromise;
101118
}
@@ -106,25 +123,24 @@ async function instantiateLayer(config: QueryLayerConfig): Promise<QueryLayer> {
106123
return await initDuckDB();
107124
} catch (error) {
108125
if (config.strictEngine && !shouldForceFallback(error)) throw error;
109-
// OPFS access handles are not available in all browsers/contexts; fall back silently.
110-
if (!shouldForceFallback(error)) {
111-
console.warn('[DB] DuckDB-Wasm 初始化失败,回退至 sql.js', toErrorInfo(error));
112-
}
126+
warnDuckdbFallback(error);
113127
currentEngine = 'sqljs';
114128
return createFallbackLayer();
115129
}
116130
case 'sqljs':
117131
currentEngine = 'sqljs';
118132
return createFallbackLayer();
119133
default:
134+
if (!canUseDuckdbOpfs()) {
135+
currentEngine = 'sqljs';
136+
return createFallbackLayer();
137+
}
120138
try {
121139
currentEngine = 'duckdb';
122140
return await initDuckDB();
123141
} catch (error) {
124142
if (config.strictEngine && !shouldForceFallback(error)) throw error;
125-
if (!shouldForceFallback(error)) {
126-
console.warn('[DB] DuckDB-Wasm 初始化失败,回退至 sql.js', toErrorInfo(error));
127-
}
143+
warnDuckdbFallback(error);
128144
currentEngine = 'sqljs';
129145
return createFallbackLayer();
130146
}

oauth-proxy/src/index.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
type Env = {
22
ALLOWED_ORIGINS?: string;
3+
GITHUB_CLIENT_ID?: string;
4+
GITHUB_CLIENT_SECRET?: string;
35
};
46

57
function parseAllowedOrigins(raw: string | undefined) {
@@ -59,13 +61,23 @@ async function handleToken(request: Request, env: Env): Promise<Response> {
5961
return withCors(request, env, new Response('Missing required fields', { status: 400 }));
6062
}
6163

64+
// Optional hardening: if the worker is configured with a fixed client_id, reject mismatches.
65+
const configuredClientId = String(env.GITHUB_CLIENT_ID || '').trim();
66+
if (configuredClientId && configuredClientId !== client_id) {
67+
return withCors(request, env, new Response('Invalid client_id', { status: 400 }));
68+
}
69+
70+
const params = new URLSearchParams({ client_id, code, redirect_uri, code_verifier });
71+
const clientSecret = String(env.GITHUB_CLIENT_SECRET || '').trim();
72+
if (clientSecret) params.set('client_secret', clientSecret);
73+
6274
const tokenResponse = await fetch('https://github.com/login/oauth/access_token', {
6375
method: 'POST',
6476
headers: {
6577
Accept: 'application/json',
6678
'Content-Type': 'application/x-www-form-urlencoded'
6779
},
68-
body: new URLSearchParams({ client_id, code, redirect_uri, code_verifier })
80+
body: params
6981
});
7082

7183
const bodyText = await tokenResponse.text().catch(() => '');
@@ -83,4 +95,3 @@ export default {
8395
return new Response('Not Found', { status: 404 });
8496
}
8597
};
86-

0 commit comments

Comments
 (0)