Delete api

This commit is contained in:
James Grugett 2022-08-12 11:59:31 -05:00
parent 919af972c8
commit f32d207178
20 changed files with 0 additions and 721 deletions

View File

@ -1,230 +0,0 @@
import { Bet } from 'common/bet'
import { Answer } from 'common/answer'
import { getOutcomeProbability, getProbability } from 'common/calculate'
import { Comment } from 'common/comment'
import { Contract } from 'common/contract'
import { User } from 'common/user'
import { removeUndefinedProps } from 'common/util/object'
import { ENV_CONFIG } from 'common/envs/constants'
import { JSONContent } from '@tiptap/core'
export type LiteMarket = {
// Unique identifer for this market
id: string
// Attributes about the creator
creatorUsername: string
creatorName: string
createdTime: number
creatorAvatarUrl?: string
// Market attributes. All times are in milliseconds since epoch
closeTime?: number
question: string
description: string | JSONContent
tags: string[]
url: string
outcomeType: string
mechanism: string
pool: { [outcome: string]: number }
probability?: number
p?: number
totalLiquidity?: number
volume: number
volume7Days: number
volume24Hours: number
isResolved: boolean
resolution?: string
resolutionTime?: number
resolutionProbability?: number
}
export type ApiAnswer = Answer & {
probability?: number
}
export type FullMarket = LiteMarket & {
bets: Bet[]
comments: Comment[]
answers?: ApiAnswer[]
}
export type ApiError = {
error: string
}
type ValidationErrorDetail = {
field: string | null
error: string
}
export class ValidationError {
details: ValidationErrorDetail[]
constructor(details: ValidationErrorDetail[]) {
this.details = details
}
}
export function toLiteMarket(contract: Contract): LiteMarket {
const {
id,
creatorUsername,
creatorName,
createdTime,
creatorAvatarUrl,
closeTime,
question,
description,
tags,
slug,
pool,
outcomeType,
mechanism,
volume,
volume7Days,
volume24Hours,
isResolved,
resolution,
resolutionTime,
resolutionProbability,
} = contract
const { p, totalLiquidity } = contract as any
const probability =
contract.outcomeType === 'BINARY' ? getProbability(contract) : undefined
return removeUndefinedProps({
id,
creatorUsername,
creatorName,
createdTime,
creatorAvatarUrl,
closeTime:
resolutionTime && closeTime
? Math.min(resolutionTime, closeTime)
: closeTime,
question,
description,
tags,
url: `https://manifold.markets/${creatorUsername}/${slug}`,
pool,
probability,
p,
totalLiquidity,
outcomeType,
mechanism,
volume,
volume7Days,
volume24Hours,
isResolved,
resolution,
resolutionTime,
resolutionProbability,
})
}
export function toFullMarket(
contract: Contract,
comments: Comment[],
bets: Bet[]
): FullMarket {
const liteMarket = toLiteMarket(contract)
const answers =
contract.outcomeType === 'FREE_RESPONSE'
? contract.answers.map((answer) =>
augmentAnswerWithProbability(contract, answer)
)
: undefined
return {
...liteMarket,
answers,
comments,
bets,
}
}
function augmentAnswerWithProbability(
contract: Contract,
answer: Answer
): ApiAnswer {
const probability = getOutcomeProbability(contract, answer.id)
return {
...answer,
probability,
}
}
export type LiteUser = {
id: string
createdTime: number
name: string
username: string
url: string
avatarUrl?: string
bio?: string
bannerUrl?: string
website?: string
twitterHandle?: string
discordHandle?: string
balance: number
totalDeposits: number
profitCached: {
daily: number
weekly: number
monthly: number
allTime: number
}
creatorVolumeCached: {
daily: number
weekly: number
monthly: number
allTime: number
}
}
export function toLiteUser(user: User): LiteUser {
const {
id,
createdTime,
name,
username,
avatarUrl,
bio,
bannerUrl,
website,
twitterHandle,
discordHandle,
balance,
totalDeposits,
profitCached,
creatorVolumeCached,
} = user
return removeUndefinedProps({
id,
createdTime,
name,
username,
url: `https://${ENV_CONFIG.domain}/${username}`,
avatarUrl,
bio,
bannerUrl,
website,
twitterHandle,
discordHandle,
balance,
totalDeposits,
profitCached,
creatorVolumeCached,
})
}

View File

@ -1,17 +0,0 @@
import { z } from 'zod'
import { ValidationError } from './_types'
export const validate = <T extends z.ZodTypeAny>(schema: T, val: unknown) => {
const result = schema.safeParse(val)
if (!result.success) {
const issues = result.error.issues.map((i) => {
return {
field: i.path.join('.') || null,
error: i.message,
}
})
throw new ValidationError(issues)
} else {
return result.data as z.infer<T>
}
}

View File

@ -1,27 +0,0 @@
import { NextApiRequest, NextApiResponse } from 'next'
import {
CORS_ORIGIN_MANIFOLD,
CORS_ORIGIN_LOCALHOST,
} from 'common/envs/constants'
import { applyCorsHeaders } from 'web/lib/api/cors'
import { fetchBackend, forwardResponse } from 'web/lib/api/proxy'
export const config = { api: { bodyParser: true } }
export default async function route(req: NextApiRequest, res: NextApiResponse) {
await applyCorsHeaders(req, res, {
origin: [CORS_ORIGIN_MANIFOLD, CORS_ORIGIN_LOCALHOST],
methods: 'POST',
})
const { betId } = req.query as { betId: string }
if (req.body) req.body.betId = betId
try {
const backendRes = await fetchBackend(req, 'cancelbet')
await forwardResponse(res, backendRes)
} catch (err) {
console.error('Error talking to cloud function: ', err)
res.status(500).json({ message: 'Error communicating with backend.' })
}
}

View File

@ -1,23 +0,0 @@
import { NextApiRequest, NextApiResponse } from 'next'
import {
CORS_ORIGIN_MANIFOLD,
CORS_ORIGIN_LOCALHOST,
} from 'common/envs/constants'
import { applyCorsHeaders } from 'web/lib/api/cors'
import { fetchBackend, forwardResponse } from 'web/lib/api/proxy'
export const config = { api: { bodyParser: false } }
export default async function route(req: NextApiRequest, res: NextApiResponse) {
await applyCorsHeaders(req, res, {
origin: [CORS_ORIGIN_MANIFOLD, CORS_ORIGIN_LOCALHOST],
methods: 'POST',
})
try {
const backendRes = await fetchBackend(req, 'placebet')
await forwardResponse(res, backendRes)
} catch (err) {
console.error('Error talking to cloud function: ', err)
res.status(500).json({ message: 'Error communicating with backend.' })
}
}

View File

@ -1,66 +0,0 @@
import { NextApiRequest, NextApiResponse } from 'next'
import { applyCorsHeaders, CORS_UNRESTRICTED } from 'web/lib/api/cors'
import { Bet, getBets } from 'web/lib/firebase/bets'
import { getContractFromSlug } from 'web/lib/firebase/contracts'
import { getUserByUsername } from 'web/lib/firebase/users'
import { ApiError, ValidationError } from './_types'
import { z } from 'zod'
import { validate } from './_validate'
const queryParams = z
.object({
username: z.string().optional(),
market: z.string().optional(),
limit: z
.number()
.default(1000)
.or(z.string().regex(/\d+/).transform(Number))
.refine((n) => n >= 0 && n <= 1000, 'Limit must be between 0 and 1000'),
before: z.string().optional(),
})
.strict()
export default async function handler(
req: NextApiRequest,
res: NextApiResponse<Bet[] | ValidationError | ApiError>
) {
await applyCorsHeaders(req, res, CORS_UNRESTRICTED)
let params: z.infer<typeof queryParams>
try {
params = validate(queryParams, req.query)
} catch (e) {
if (e instanceof ValidationError) {
return res.status(400).json(e)
}
console.error(`Unknown error during validation: ${e}`)
return res.status(500).json({ error: 'Unknown error during validation' })
}
const { username, market, limit, before } = params
let userId: string | undefined
if (username) {
const user = await getUserByUsername(username)
if (!user) {
res.status(404).json({ error: 'User not found' })
return
}
userId = user.id
}
let contractId: string | undefined
if (market) {
const contract = await getContractFromSlug(market)
if (!contract) {
res.status(404).json({ error: 'Contract not found' })
return
}
contractId = contract.id
}
const bets = await getBets({ userId, contractId, limit, before })
res.setHeader('Cache-Control', 'max-age=0')
return res.status(200).json(bets)
}

View File

@ -1,18 +0,0 @@
import { NextApiRequest, NextApiResponse } from 'next'
import { getGroupBySlug } from 'web/lib/firebase/groups'
import { applyCorsHeaders, CORS_UNRESTRICTED } from 'web/lib/api/cors'
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
await applyCorsHeaders(req, res, CORS_UNRESTRICTED)
const { slug } = req.query
const group = await getGroupBySlug(slug as string)
if (!group) {
res.status(404).json({ error: 'Group not found' })
return
}
res.setHeader('Cache-Control', 'no-cache')
return res.status(200).json(group)
}

View File

@ -1,18 +0,0 @@
import { NextApiRequest, NextApiResponse } from 'next'
import { getGroup } from 'web/lib/firebase/groups'
import { applyCorsHeaders, CORS_UNRESTRICTED } from 'web/lib/api/cors'
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
await applyCorsHeaders(req, res, CORS_UNRESTRICTED)
const { id } = req.query
const group = await getGroup(id as string)
if (!group) {
res.status(404).json({ error: 'Group not found' })
return
}
res.setHeader('Cache-Control', 'no-cache')
return res.status(200).json(group)
}

View File

@ -1,15 +0,0 @@
import type { NextApiRequest, NextApiResponse } from 'next'
import { listAllGroups } from 'web/lib/firebase/groups'
import { applyCorsHeaders, CORS_UNRESTRICTED } from 'web/lib/api/cors'
type Data = any[]
export default async function handler(
req: NextApiRequest,
res: NextApiResponse<Data>
) {
await applyCorsHeaders(req, res, CORS_UNRESTRICTED)
const groups = await listAllGroups()
res.setHeader('Cache-Control', 'max-age=0')
res.status(200).json(groups)
}

View File

@ -1,29 +0,0 @@
import { NextApiRequest, NextApiResponse } from 'next'
import { listAllBets } from 'web/lib/firebase/bets'
import { listAllComments } from 'web/lib/firebase/comments'
import { getContractFromId } from 'web/lib/firebase/contracts'
import { applyCorsHeaders, CORS_UNRESTRICTED } from 'web/lib/api/cors'
import { FullMarket, ApiError, toFullMarket } from '../../_types'
export default async function handler(
req: NextApiRequest,
res: NextApiResponse<FullMarket | ApiError>
) {
await applyCorsHeaders(req, res, CORS_UNRESTRICTED)
const { id } = req.query
const contractId = id as string
const [contract, bets, comments] = await Promise.all([
getContractFromId(contractId),
listAllBets(contractId),
listAllComments(contractId),
])
if (!contract) {
res.status(404).json({ error: 'Contract not found' })
return
}
res.setHeader('Cache-Control', 'max-age=0')
return res.status(200).json(toFullMarket(contract, comments, bets))
}

View File

@ -1,23 +0,0 @@
import { NextApiRequest, NextApiResponse } from 'next'
import { getContractFromId } from 'web/lib/firebase/contracts'
import { applyCorsHeaders, CORS_UNRESTRICTED } from 'web/lib/api/cors'
import { ApiError, toLiteMarket, LiteMarket } from '../../_types'
export default async function handler(
req: NextApiRequest,
res: NextApiResponse<LiteMarket | ApiError>
) {
await applyCorsHeaders(req, res, CORS_UNRESTRICTED)
const { id } = req.query
const contractId = id as string
const contract = await getContractFromId(contractId)
if (!contract) {
res.status(404).json({ error: 'Contract not found' })
return
}
res.setHeader('Cache-Control', 'max-age=0')
return res.status(200).json(toLiteMarket(contract))
}

View File

@ -1,28 +0,0 @@
import { NextApiRequest, NextApiResponse } from 'next'
import {
CORS_ORIGIN_MANIFOLD,
CORS_ORIGIN_LOCALHOST,
} from 'common/envs/constants'
import { applyCorsHeaders } from 'web/lib/api/cors'
import { fetchBackend, forwardResponse } from 'web/lib/api/proxy'
export const config = { api: { bodyParser: true } }
export default async function route(req: NextApiRequest, res: NextApiResponse) {
await applyCorsHeaders(req, res, {
origin: [CORS_ORIGIN_MANIFOLD, CORS_ORIGIN_LOCALHOST],
methods: 'POST',
})
const { id } = req.query
const contractId = id as string
if (req.body) req.body.contractId = contractId
try {
const backendRes = await fetchBackend(req, 'resolvemarket')
await forwardResponse(res, backendRes)
} catch (err) {
console.error('Error talking to cloud function: ', err)
res.status(500).json({ message: 'Error communicating with backend.' })
}
}

View File

@ -1,28 +0,0 @@
import { NextApiRequest, NextApiResponse } from 'next'
import {
CORS_ORIGIN_MANIFOLD,
CORS_ORIGIN_LOCALHOST,
} from 'common/envs/constants'
import { applyCorsHeaders } from 'web/lib/api/cors'
import { fetchBackend, forwardResponse } from 'web/lib/api/proxy'
export const config = { api: { bodyParser: true } }
export default async function route(req: NextApiRequest, res: NextApiResponse) {
await applyCorsHeaders(req, res, {
origin: [CORS_ORIGIN_MANIFOLD, CORS_ORIGIN_LOCALHOST],
methods: 'POST',
})
const { id } = req.query
const contractId = id as string
if (req.body) req.body.contractId = contractId
try {
const backendRes = await fetchBackend(req, 'sellshares')
await forwardResponse(res, backendRes)
} catch (err) {
console.error('Error talking to cloud function: ', err)
res.status(500).json({ message: 'Error communicating with backend.' })
}
}

View File

@ -1,23 +0,0 @@
import { NextApiRequest, NextApiResponse } from 'next'
import {
CORS_ORIGIN_MANIFOLD,
CORS_ORIGIN_LOCALHOST,
} from 'common/envs/constants'
import { applyCorsHeaders } from 'web/lib/api/cors'
import { fetchBackend, forwardResponse } from 'web/lib/api/proxy'
export const config = { api: { bodyParser: false } }
export default async function route(req: NextApiRequest, res: NextApiResponse) {
await applyCorsHeaders(req, res, {
origin: [CORS_ORIGIN_MANIFOLD, CORS_ORIGIN_LOCALHOST],
methods: 'POST',
})
try {
const backendRes = await fetchBackend(req, 'createmarket')
await forwardResponse(res, backendRes)
} catch (err) {
console.error('Error talking to cloud function: ', err)
res.status(500).json({ message: 'Error communicating with backend.' })
}
}

View File

@ -1,51 +0,0 @@
// Next.js API route support: https://vercel.com/docs/concepts/functions/serverless-functions
import type { NextApiRequest, NextApiResponse } from 'next'
import { listAllContracts } from 'web/lib/firebase/contracts'
import { applyCorsHeaders, CORS_UNRESTRICTED } from 'web/lib/api/cors'
import { toLiteMarket, ValidationError } from './_types'
import { z } from 'zod'
import { validate } from './_validate'
const queryParams = z
.object({
limit: z
.number()
.default(1000)
.or(z.string().regex(/\d+/).transform(Number))
.refine((n) => n >= 0 && n <= 1000, 'Limit must be between 0 and 1000'),
before: z.string().optional(),
})
.strict()
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
await applyCorsHeaders(req, res, CORS_UNRESTRICTED)
let params: z.infer<typeof queryParams>
try {
params = validate(queryParams, req.query)
} catch (e) {
if (e instanceof ValidationError) {
return res.status(400).json(e)
}
console.error(`Unknown error during validation: ${e}`)
return res.status(500).json({ error: 'Unknown error during validation' })
}
const { limit, before } = params
try {
const contracts = await listAllContracts(limit, before)
// Serve from Vercel cache, then update. see https://vercel.com/docs/concepts/functions/edge-caching
res.setHeader('Cache-Control', 's-maxage=1, stale-while-revalidate')
res.status(200).json(contracts.map(toLiteMarket))
} catch (e) {
res.status(400).json({
error:
'Failed to fetch markets (did you pass an invalid ID as the before parameter?)',
})
return
}
}

View File

@ -1,16 +0,0 @@
import { NextApiRequest, NextApiResponse } from 'next'
import { fetchBackend, forwardResponse } from 'web/lib/api/proxy'
import { LiteUser, ApiError } from './_types'
export default async function handler(
req: NextApiRequest,
res: NextApiResponse<LiteUser | ApiError>
) {
try {
const backendRes = await fetchBackend(req, 'getcurrentuser')
await forwardResponse(res, backendRes)
} catch (err) {
console.error('Error talking to cloud function: ', err)
res.status(500).json({ error: 'Error communicating with backend.' })
}
}

View File

@ -1,29 +0,0 @@
import { NextApiRequest, NextApiResponse } from 'next'
import { applyCorsHeaders, CORS_UNRESTRICTED } from 'web/lib/api/cors'
import { listAllBets } from 'web/lib/firebase/bets'
import { listAllComments } from 'web/lib/firebase/comments'
import { getContractFromSlug } from 'web/lib/firebase/contracts'
import { FullMarket, ApiError, toFullMarket } from '../_types'
export default async function handler(
req: NextApiRequest,
res: NextApiResponse<FullMarket | ApiError>
) {
await applyCorsHeaders(req, res, CORS_UNRESTRICTED)
const { slug } = req.query
const contract = await getContractFromSlug(slug as string)
if (!contract) {
res.status(404).json({ error: 'Contract not found' })
return
}
const [bets, comments] = await Promise.all([
listAllBets(contract.id),
listAllComments(contract.id),
])
res.setHeader('Cache-Control', 'max-age=0')
return res.status(200).json(toFullMarket(contract, comments, bets))
}

View File

@ -1,25 +0,0 @@
import { NextApiRequest, NextApiResponse } from 'next'
import { applyCorsHeaders, CORS_UNRESTRICTED } from 'web/lib/api/cors'
import { Bet, getUserBets } from 'web/lib/firebase/bets'
import { getUserByUsername } from 'web/lib/firebase/users'
import { ApiError } from '../../../_types'
export default async function handler(
req: NextApiRequest,
res: NextApiResponse<Bet[] | ApiError>
) {
await applyCorsHeaders(req, res, CORS_UNRESTRICTED)
const { username } = req.query
const user = await getUserByUsername(username as string)
if (!user) {
res.status(404).json({ error: 'User not found' })
return
}
const bets = await getUserBets(user.id, { includeRedemptions: false })
res.setHeader('Cache-Control', 'max-age=0')
return res.status(200).json(bets)
}

View File

@ -1,19 +0,0 @@
import { NextApiRequest, NextApiResponse } from 'next'
import { getUserByUsername } from 'web/lib/firebase/users'
import { applyCorsHeaders, CORS_UNRESTRICTED } from 'web/lib/api/cors'
import { LiteUser, ApiError, toLiteUser } from '../../_types'
export default async function handler(
req: NextApiRequest,
res: NextApiResponse<LiteUser | ApiError>
) {
await applyCorsHeaders(req, res, CORS_UNRESTRICTED)
const { username } = req.query
const user = await getUserByUsername(username as string)
if (!user) {
res.status(404).json({ error: 'User not found' })
return
}
res.setHeader('Cache-Control', 'no-cache')
return res.status(200).json(toLiteUser(user))
}

View File

@ -1,19 +0,0 @@
import { NextApiRequest, NextApiResponse } from 'next'
import { getUser } from 'web/lib/firebase/users'
import { applyCorsHeaders, CORS_UNRESTRICTED } from 'web/lib/api/cors'
import { LiteUser, ApiError, toLiteUser } from '../../_types'
export default async function handler(
req: NextApiRequest,
res: NextApiResponse<LiteUser | ApiError>
) {
await applyCorsHeaders(req, res, CORS_UNRESTRICTED)
const { id } = req.query
const user = await getUser(id as string)
if (!user) {
res.status(404).json({ error: 'User not found' })
return
}
res.setHeader('Cache-Control', 'no-cache')
return res.status(200).json(toLiteUser(user))
}

View File

@ -1,17 +0,0 @@
// Next.js API route support: https://vercel.com/docs/concepts/functions/serverless-functions
import type { NextApiRequest, NextApiResponse } from 'next'
import { listAllUsers } from 'web/lib/firebase/users'
import { applyCorsHeaders, CORS_UNRESTRICTED } from 'web/lib/api/cors'
import { toLiteUser } from './_types'
type Data = any[]
export default async function handler(
req: NextApiRequest,
res: NextApiResponse<Data>
) {
await applyCorsHeaders(req, res, CORS_UNRESTRICTED)
const users = await listAllUsers()
res.setHeader('Cache-Control', 'max-age=0')
res.status(200).json(users.map(toLiteUser))
}