feat: bootstrap personal stock agent
This commit is contained in:
98
web/src/server/index.ts
Normal file
98
web/src/server/index.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import "dotenv/config";
|
||||
import cors from "cors";
|
||||
import express from "express";
|
||||
import { join } from "node:path";
|
||||
import { generateDailyReport } from "../agent/report";
|
||||
import { createProvider } from "../agent/provider";
|
||||
import { WatchlistStore } from "../agent/watchlistStore";
|
||||
import { loadIpadsConfig } from "./ipadsConfig";
|
||||
|
||||
const port = Number(process.env.PORT ?? 8787);
|
||||
const dataDir = process.env.STOCK_AGENT_DATA_DIR ?? join(process.cwd(), "data");
|
||||
|
||||
const watchlistStore = new WatchlistStore(join(dataDir, "watchlist.json"));
|
||||
const app = express();
|
||||
|
||||
let cachedReportUpdatedAt = 0;
|
||||
let cachedReport: Awaited<ReturnType<typeof generateDailyReport>> | undefined;
|
||||
|
||||
app.use(cors());
|
||||
app.use(express.json());
|
||||
|
||||
app.get("/api/health", async (_request, response) => {
|
||||
const config = loadIpadsConfig();
|
||||
response.json({
|
||||
ok: true,
|
||||
providerConfigured: config.apiKeyConfigured,
|
||||
baseUrl: config.baseUrl,
|
||||
wireApi: config.wireApi,
|
||||
scheduler: "24h"
|
||||
});
|
||||
});
|
||||
|
||||
app.get("/api/watchlist", async (_request, response) => {
|
||||
response.json(await watchlistStore.list());
|
||||
});
|
||||
|
||||
app.post("/api/watchlist", async (request, response, next) => {
|
||||
try {
|
||||
response.status(201).json(await watchlistStore.add(String(request.body?.symbol ?? ""), request.body?.name));
|
||||
cachedReport = undefined;
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
app.delete("/api/watchlist/:symbol", async (request, response, next) => {
|
||||
try {
|
||||
await watchlistStore.remove(request.params.symbol);
|
||||
cachedReport = undefined;
|
||||
response.status(204).end();
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
app.get("/api/report/daily", async (_request, response, next) => {
|
||||
try {
|
||||
response.json(await getDailyReport());
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
app.post("/api/report/daily/refresh", async (_request, response, next) => {
|
||||
try {
|
||||
cachedReport = undefined;
|
||||
response.json(await getDailyReport());
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
app.use((error: unknown, _request: express.Request, response: express.Response, _next: express.NextFunction) => {
|
||||
const message = error instanceof Error ? error.message : "Unknown server error";
|
||||
response.status(400).json({ error: message });
|
||||
});
|
||||
|
||||
setInterval(() => {
|
||||
void getDailyReport().catch((error) => {
|
||||
console.error("daily report refresh failed", error);
|
||||
});
|
||||
}, 24 * 60 * 60 * 1000).unref();
|
||||
|
||||
app.listen(port, "127.0.0.1", () => {
|
||||
console.log(`stock agent api listening at http://127.0.0.1:${port}`);
|
||||
});
|
||||
|
||||
async function getDailyReport() {
|
||||
const now = Date.now();
|
||||
if (cachedReport && now - cachedReportUpdatedAt < 24 * 60 * 60 * 1000) {
|
||||
return cachedReport;
|
||||
}
|
||||
|
||||
const watchlist = await watchlistStore.list();
|
||||
cachedReport = await generateDailyReport(watchlist, createProvider(loadIpadsConfig().apiKey), new Date());
|
||||
cachedReportUpdatedAt = now;
|
||||
return cachedReport;
|
||||
}
|
||||
Reference in New Issue
Block a user