A Prediction Market on the AT Protocol

feat(src/core/api.ts): move db calls here from server/index

Ciaran 17a72e11 f5f2bcb1

+88 -35
+53
src/core/api.ts
··· 4 4 import { is, type ActorIdentifier } from '@atcute/lexicons'; 5 5 import type { CreateCommit, DeleteCommit } from '@atcute/jetstream'; 6 6 import * as schema from "../db/schema" 7 + import { DEFAULT_MARKET_COLS, DEFAULT_BET_COLS, DEFAULT_RESOLUTION_COLS } from '../db/schema'; 7 8 import { eq } from 'drizzle-orm'; 9 + 8 10 9 11 export const db = drizzle(process.env.DATABASE_URL!, { schema }); 10 12 13 + export async function tryListMarkets() { 14 + return await db.query.marketsTable.findMany({ 15 + columns: DEFAULT_MARKET_COLS, 16 + orderBy: (markets, { desc }) => [desc(markets.createdAt)], 17 + with: { 18 + bets: { columns: DEFAULT_BET_COLS }, 19 + resolution: { columns: DEFAULT_RESOLUTION_COLS } 20 + }, 21 + }) 22 + } 23 + 24 + export async function tryFindMarket(uri: string) { 25 + return await db.query.marketsTable.findFirst({ 26 + columns: DEFAULT_MARKET_COLS, 27 + where: eq(schema.marketsTable.uri, uri), 28 + with: { 29 + bets: { columns: DEFAULT_BET_COLS }, 30 + resolution: { columns: DEFAULT_RESOLUTION_COLS } 31 + } 32 + }) 33 + } 34 + 35 + export async function tryFindMarketBets(uri: string) { 36 + return await db.query.betsTable.findMany({ 37 + columns: DEFAULT_BET_COLS, 38 + where: eq(schema.betsTable.marketUri, uri), 39 + orderBy: (bets, { desc }) => [desc(bets.createdAt)], 40 + with: { 41 + market: { 42 + columns: DEFAULT_MARKET_COLS, 43 + with: { resolution: { columns: DEFAULT_RESOLUTION_COLS } } 44 + } 45 + } 46 + }) 47 + } 48 + 49 + export async function tryFindMarketResolutions(uri: string) { 50 + return await db.query.resolutionsTable.findFirst({ 51 + columns: DEFAULT_RESOLUTION_COLS, 52 + where: eq(schema.resolutionsTable.marketUri, uri), 53 + orderBy: (resolutions, { desc }) => [desc(resolutions.createdAt)], 54 + with: { 55 + market: { 56 + columns: DEFAULT_MARKET_COLS, 57 + with: { bets: { columns: DEFAULT_BET_COLS } } 58 + } 59 + } 60 + }) 61 + } 62 + 11 63 export async function tryCreateMarket(did: ActorIdentifier, { record, rev, rkey, cid }: CreateCommit) { 12 64 if (is(ZaCoCiaranCumulusMarket.mainSchema, record)) { 13 65 const uri = `at://${did}/${record.$type}/${rkey}`; 14 66 console.log("> Creating Market:", uri); 67 + 15 68 const { question, liquidity } = record; 16 69 const [closesAt, createdAt] = [new Date(record.closesAt), new Date(record.createdAt)]; 17 70
+26
src/db/schema.ts
··· 21 21 closesAt: timestamp({ withTimezone: true }).notNull(), 22 22 }); 23 23 24 + export const DEFAULT_MARKET_COLS = { 25 + uri: true, 26 + did: true, 27 + cid: true, 28 + question: true, 29 + liquidity: true, 30 + closesAt: true, 31 + createdAt: true, 32 + } 33 + 24 34 export const betsTable = pgTable("bets", { 25 35 ...SHARED_SCHEMA, 26 36 position: betPositionEnum().notNull(), 27 37 marketUri: text().notNull(), 28 38 }); 29 39 40 + export const DEFAULT_BET_COLS = { 41 + uri: true, 42 + did: true, 43 + cid: true, 44 + position: true, 45 + createdAt: true, 46 + } 47 + 30 48 export const resolutionsTable = pgTable("resolutions", { 31 49 ...SHARED_SCHEMA, 32 50 answer: resolutionAnswerEnum().notNull(), 33 51 marketUri: text().notNull(), 34 52 }); 53 + 54 + export const DEFAULT_RESOLUTION_COLS = { 55 + uri: true, 56 + did: true, 57 + cid: true, 58 + answer: true, 59 + createdAt: true, 60 + } 35 61 36 62 export const marketsRelations = relations(marketsTable, ({ many, one }) => ({ 37 63 bets: many(betsTable),
+9 -35
src/server/index.ts
··· 2 2 import { staticPlugin } from '@elysiajs/static' 3 3 import { swagger } from '@elysiajs/swagger' 4 4 import { cors } from '@elysiajs/cors' 5 - import { drizzle } from "drizzle-orm/node-postgres"; 6 - import * as schema from "../db/schema" 7 - import { eq } from "drizzle-orm"; 8 - 9 - const db = drizzle(process.env.DATABASE_URL!, { schema }); 5 + import { tryFindMarket, tryFindMarketBets, tryFindMarketResolutions, tryListMarkets } from "@/core/api"; 10 6 11 7 export const app = new Elysia() 12 8 .use(cors()) 13 9 .use(swagger()) 14 10 .use(staticPlugin({ prefix: "/", assets: "dist" })) 15 11 .get("/", () => new Response(Bun.file("dist/index.html"))) 16 - .group("/api", (app) => ( 17 - app.get("/markets", async () => 18 - Response.json(await db.query.marketsTable.findMany({ 19 - with: { bets: true, resolution: true }, 20 - orderBy: (markets, { desc }) => [desc(markets.createdAt)], 21 - })) 22 - ).group("/market", (app) => ( 23 - app.get("/:uri", async ({ params: { uri } }) => 24 - Response.json(await db.query.marketsTable.findFirst({ 25 - where: eq(schema.marketsTable.uri, uri), 26 - with: { bets: true, resolution: true } 27 - })) 28 - ).get("/:uri/bets", async ({ params: { uri } }) => 29 - Response.json(await db.query.betsTable.findMany({ 30 - where: eq(schema.betsTable.marketUri, uri), 31 - with: { 32 - market: { with: { resolution: true } } 33 - } 34 - })) 35 - ).get("/:uri/resolutions", async ({ params: { uri } }) => 36 - Response.json(await db.query.resolutionsTable.findMany({ 37 - where: eq(schema.resolutionsTable.marketUri, uri), 38 - with: { 39 - market: { with: { bets: true } } 40 - } 41 - })) 42 - ) 43 - )) 44 - )) 45 - .listen({ port: process.env.PORT!, hostname: "0.0.0.0" }) 12 + .group("/api", api => api 13 + .get("/markets", async () => await tryListMarkets()) 14 + .group("/market", (market) => market 15 + .get("/:uri", async ({ params }) => await tryFindMarket(params.uri)) 16 + .get("/:uri/bets", async ({ params }) => await tryFindMarketBets(params.uri)) 17 + .get("/:uri/resolutions", async ({ params }) => await tryFindMarketResolutions(params.uri)) 18 + ) 19 + ).listen({ port: process.env.PORT!, hostname: "0.0.0.0" }) 46 20 47 21 console.log(`> Server running on ${app.server?.protocol}://${app.server?.hostname}:${app.server?.port}`);