65 lines
2.0 KiB
TypeScript
65 lines
2.0 KiB
TypeScript
import { randomUUID } from "node:crypto";
|
|||
|
|
|
||
|
|
import { defineAction } from "@agent-native/core/action";
|
||
|
|
import { and, eq } from "@agent-native/core/db/schema";
|
||
|
|
import { z } from "zod";
|
||
|
|
|
||
|
|
import { getDb } from "../server/db/index.js";
|
||
|
|
import { stocks } from "../server/db/schema.js";
|
||
|
|
|
||
|
|
export default defineAction({
|
||
|
|
description:
|
||
|
|
"Add a new stock listing. Fails if the symbol is already listed for this user.",
|
||
|
|
schema: z.object({
|
||
|
|
symbol: z.string().min(1).max(10).describe("Ticker symbol, e.g. AAPL"),
|
||
|
|
companyName: z.string().min(1).describe("Company name"),
|
||
|
|
exchange: z
|
||
|
|
.string()
|
||
|
|
.default("NASDAQ")
|
||
|
|
.describe("Exchange, e.g. NASDAQ, NYSE"),
|
||
|
|
sector: z.string().optional().describe("Sector, e.g. Technology"),
|
||
|
|
currency: z.string().default("USD").describe("Currency code, e.g. USD"),
|
||
|
|
price: z.coerce.number().default(0).describe("Current price"),
|
||
|
|
changePercent: z.coerce
|
||
|
|
.number()
|
||
|
|
.default(0)
|
||
|
|
.describe("Price change percent, e.g. 1.5 or -2.3"),
|
||
|
|
marketCap: z.coerce
|
||
|
|
.number()
|
||
|
|
.optional()
|
||
|
|
.describe("Market capitalization"),
|
||
|
|
}),
|
||
|
|
run: async (args, ctx) => {
|
||
|
|
if (!ctx?.userEmail) throw new Error("Authentication required.");
|
||
|
|
const db = getDb();
|
||
|
|
const symbol = args.symbol.toUpperCase();
|
||
|
|
|
||
|
|
const existing = await db
|
||
|
|
.select({ id: stocks.id })
|
||
|
|
.from(stocks)
|
||
|
|
.where(and(eq(stocks.ownerEmail, ctx.userEmail), eq(stocks.symbol, symbol)));
|
||
|
|
if (existing[0]) {
|
||
|
|
throw new Error(`${symbol} is already listed.`);
|
||
|
|
}
|
||
|
|
|
||
|
|
const now = new Date().toISOString();
|
||
|
|
const row = {
|
||
|
|
id: randomUUID(),
|
||
|
|
symbol,
|
||
|
|
companyName: args.companyName,
|
||
|
|
exchange: args.exchange,
|
||
|
|
sector: args.sector ?? null,
|
||
|
|
currency: args.currency,
|
||
|
|
price: args.price,
|
||
|
|
changePercent: args.changePercent,
|
||
|
|
marketCap: args.marketCap ?? null,
|
||
|
|
watchlisted: false,
|
||
|
|
ownerEmail: ctx.userEmail,
|
||
|
|
createdAt: now,
|
||
|
|
updatedAt: now,
|
||
|
|
};
|
||
|
|
await db.insert(stocks).values(row);
|
||
|
|
return row;
|
||
|
|
},
|
||
|
|
});
|