fix: backfill symbol_demand for sidebar-added symbols + analyst ratings schema fix
- Add await ctx.cache.subscribe() to addSymbol mutation so symbols added via the sidebar get registered in symbol_demand and yfinance jobs are queued immediately - Backfill PEP, WYNN, STZ, CELH into symbol_demand + adapter_queue - Upgrade yahoo-finance2 3.15.3 -> 3.15.4 and pass validateResult:false to quoteSummary() to handle Yahoo schema drift - Add error detail logging for analyst ratings schema failures - Update .gitignore with common ignores
This commit is contained in:
+22
-1
@@ -7,7 +7,9 @@ import { createCacheRepository, type SourceKind } from './cache/CacheRepository.
|
||||
import { YFinanceAdapter } from './adapters/YFinanceAdapter.ts';
|
||||
import { SecFetchAdapter } from './adapters/SecFetchAdapter.ts';
|
||||
import { SecLintAdapter } from './adapters/SecLintAdapter.ts';
|
||||
import { XCookieAdapter } from './adapters/XCookieAdapter.ts';
|
||||
import type { SourceFetch } from './adapters/SourceAdapter.ts';
|
||||
import cryptoMod from './lib/crypto.ts';
|
||||
import { AdapterQueue } from './queue/AdapterQueue.ts';
|
||||
import { makeCreateContext } from './trpc/context.ts';
|
||||
import { appRouter } from './trpc/router.ts';
|
||||
@@ -20,6 +22,25 @@ const adapters = new Map<SourceKind, SourceFetch>([
|
||||
['sec-lint-holders' as const, new SecLintAdapter(() => database, 'sec-lint-holders') as unknown as SourceFetch],
|
||||
['sec-lint-insiders' as const, new SecLintAdapter(() => database, 'sec-lint-insiders') as unknown as SourceFetch],
|
||||
]);
|
||||
// Load X credentials at startup and register XCookieAdapter if available.
|
||||
let xAdapter: XCookieAdapter | null = null;
|
||||
(function initXAdapter() {
|
||||
const row = database.prepare('SELECT ct0_enc, auth_token_enc FROM x_credentials WHERE id=?').get('singleton') as { ct0_enc?: string; auth_token_enc?: string } | undefined;
|
||||
if (!row?.ct0_enc || !row?.auth_token_enc) return;
|
||||
let creds: { ct0: string; auth_token: string };
|
||||
try { creds = { ct0: cryptoMod.decrypt(row.ct0_enc), auth_token: cryptoMod.decrypt(row.auth_token_enc) }; }
|
||||
catch { return; }
|
||||
const updateHealth = (status: string, err?: string | null) => {
|
||||
database.prepare(
|
||||
`INSERT INTO x_credentials (id, healthy, last_error, updated_at) VALUES ('singleton', ?, ?, ?)
|
||||
ON CONFLICT(id) DO UPDATE SET healthy=excluded.healthy, last_error=excluded.last_error, updated_at=excluded.updated_at`
|
||||
).run(status === 'healthy' ? 1 : 0, err ?? null, new Date().toISOString());
|
||||
};
|
||||
xAdapter = new XCookieAdapter(creds, (health) => updateHealth(health.sourceStatus, health.lastError), database);
|
||||
adapters.set('x' as const, xAdapter as unknown as SourceFetch);
|
||||
console.log('[investor-flow] X adapter registered (credentials configured)');
|
||||
})();
|
||||
|
||||
const queue = new AdapterQueue({ db: database, adapters });
|
||||
const cache = createCacheRepository({ db: database, scheduler: queue });
|
||||
queue.cache = cache; // break the cache<->scheduler cycle
|
||||
@@ -32,7 +53,7 @@ queue.seedDefaultSchedules();
|
||||
const recovered = database.prepare("UPDATE adapter_queue SET status='pending', error=NULL, retry_count=0 WHERE status='in_flight'").run();
|
||||
if (Number(recovered.changes) > 0) console.log(`[investor-flow] recovered ${recovered.changes} interrupted in_flight jobs`);
|
||||
|
||||
const createContext = makeCreateContext({ db: database, cache, queue });
|
||||
const createContext = makeCreateContext({ db: database, cache, queue, xAdapter });
|
||||
|
||||
// Background drain: stale-while-revalidate refreshes are queued by CacheRepository.get;
|
||||
// this loop drains them (fetch via adapter -> write to cache), deduped + backed off.
|
||||
|
||||
Reference in New Issue
Block a user