diff --git a/web/pages/api/v0/_types.ts b/web/pages/api/v0/_types.ts deleted file mode 100644 index 5b9a7dab..00000000 --- a/web/pages/api/v0/_types.ts +++ /dev/null @@ -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, - }) -} diff --git a/web/pages/api/v0/_validate.ts b/web/pages/api/v0/_validate.ts deleted file mode 100644 index 25f5af4e..00000000 --- a/web/pages/api/v0/_validate.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { z } from 'zod' -import { ValidationError } from './_types' - -export const validate = (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 - } -} diff --git a/web/pages/api/v0/bet/cancel/[betId].ts b/web/pages/api/v0/bet/cancel/[betId].ts deleted file mode 100644 index 878b9349..00000000 --- a/web/pages/api/v0/bet/cancel/[betId].ts +++ /dev/null @@ -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.' }) - } -} diff --git a/web/pages/api/v0/bet/index.ts b/web/pages/api/v0/bet/index.ts deleted file mode 100644 index ab7d78aa..00000000 --- a/web/pages/api/v0/bet/index.ts +++ /dev/null @@ -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.' }) - } -} diff --git a/web/pages/api/v0/bets.ts b/web/pages/api/v0/bets.ts deleted file mode 100644 index c3de3e97..00000000 --- a/web/pages/api/v0/bets.ts +++ /dev/null @@ -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 -) { - await applyCorsHeaders(req, res, CORS_UNRESTRICTED) - - let params: z.infer - 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) -} diff --git a/web/pages/api/v0/group/[slug].ts b/web/pages/api/v0/group/[slug].ts deleted file mode 100644 index f9271591..00000000 --- a/web/pages/api/v0/group/[slug].ts +++ /dev/null @@ -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) -} diff --git a/web/pages/api/v0/group/by-id/[id].ts b/web/pages/api/v0/group/by-id/[id].ts deleted file mode 100644 index 3260302b..00000000 --- a/web/pages/api/v0/group/by-id/[id].ts +++ /dev/null @@ -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) -} diff --git a/web/pages/api/v0/groups.ts b/web/pages/api/v0/groups.ts deleted file mode 100644 index 84b773b3..00000000 --- a/web/pages/api/v0/groups.ts +++ /dev/null @@ -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 -) { - await applyCorsHeaders(req, res, CORS_UNRESTRICTED) - const groups = await listAllGroups() - res.setHeader('Cache-Control', 'max-age=0') - res.status(200).json(groups) -} diff --git a/web/pages/api/v0/market/[id]/index.ts b/web/pages/api/v0/market/[id]/index.ts deleted file mode 100644 index eb238dab..00000000 --- a/web/pages/api/v0/market/[id]/index.ts +++ /dev/null @@ -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 -) { - 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)) -} diff --git a/web/pages/api/v0/market/[id]/lite.ts b/web/pages/api/v0/market/[id]/lite.ts deleted file mode 100644 index 7688caa8..00000000 --- a/web/pages/api/v0/market/[id]/lite.ts +++ /dev/null @@ -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 -) { - 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)) -} diff --git a/web/pages/api/v0/market/[id]/resolve.ts b/web/pages/api/v0/market/[id]/resolve.ts deleted file mode 100644 index 1f291288..00000000 --- a/web/pages/api/v0/market/[id]/resolve.ts +++ /dev/null @@ -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.' }) - } -} diff --git a/web/pages/api/v0/market/[id]/sell.ts b/web/pages/api/v0/market/[id]/sell.ts deleted file mode 100644 index 431121f2..00000000 --- a/web/pages/api/v0/market/[id]/sell.ts +++ /dev/null @@ -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.' }) - } -} diff --git a/web/pages/api/v0/market/index.ts b/web/pages/api/v0/market/index.ts deleted file mode 100644 index c9e82800..00000000 --- a/web/pages/api/v0/market/index.ts +++ /dev/null @@ -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.' }) - } -} diff --git a/web/pages/api/v0/markets.ts b/web/pages/api/v0/markets.ts deleted file mode 100644 index 56ecc594..00000000 --- a/web/pages/api/v0/markets.ts +++ /dev/null @@ -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 - 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 - } -} diff --git a/web/pages/api/v0/me.ts b/web/pages/api/v0/me.ts deleted file mode 100644 index 7ee3cc3f..00000000 --- a/web/pages/api/v0/me.ts +++ /dev/null @@ -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 -) { - 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.' }) - } -} diff --git a/web/pages/api/v0/slug/[slug].ts b/web/pages/api/v0/slug/[slug].ts deleted file mode 100644 index 655b63a2..00000000 --- a/web/pages/api/v0/slug/[slug].ts +++ /dev/null @@ -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 -) { - 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)) -} diff --git a/web/pages/api/v0/user/[username]/bets/index.ts b/web/pages/api/v0/user/[username]/bets/index.ts deleted file mode 100644 index 464af52c..00000000 --- a/web/pages/api/v0/user/[username]/bets/index.ts +++ /dev/null @@ -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 -) { - 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) -} diff --git a/web/pages/api/v0/user/[username]/index.ts b/web/pages/api/v0/user/[username]/index.ts deleted file mode 100644 index 58daffcd..00000000 --- a/web/pages/api/v0/user/[username]/index.ts +++ /dev/null @@ -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 -) { - 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)) -} diff --git a/web/pages/api/v0/user/by-id/[id].ts b/web/pages/api/v0/user/by-id/[id].ts deleted file mode 100644 index 6ed67d1c..00000000 --- a/web/pages/api/v0/user/by-id/[id].ts +++ /dev/null @@ -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 -) { - 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)) -} diff --git a/web/pages/api/v0/users.ts b/web/pages/api/v0/users.ts deleted file mode 100644 index 8c62b601..00000000 --- a/web/pages/api/v0/users.ts +++ /dev/null @@ -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 -) { - await applyCorsHeaders(req, res, CORS_UNRESTRICTED) - const users = await listAllUsers() - res.setHeader('Cache-Control', 'max-age=0') - res.status(200).json(users.map(toLiteUser)) -}