From 8efbfc51d74afd3e75438cca6e90fdb738f3d257 Mon Sep 17 00:00:00 2001 From: kimjaehyeon0101 <47347352-kimjaehyeon0101@users.noreply.replit.com> Date: Mon, 29 Sep 2025 19:01:59 +0000 Subject: [PATCH] Add routes for media outlets, articles, and prediction markets Adds API endpoints for fetching articles by media outlet slug, prediction markets by article slug, and auction data for media outlets. Includes functionality to place bids on media outlet auctions. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 069d4324-6c40-4355-955e-c714a50de1ea Replit-Commit-Checkpoint-Type: intermediate_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/3df548ff-50ae-432f-9be4-25d34eccc983/069d4324-6c40-4355-955e-c714a50de1ea/jvFIdY3 --- server/routes.ts | 74 ++++++++++++++++++++++++++++++++++++++++++++++- server/storage.ts | 24 +++++++++++++++ 2 files changed, 97 insertions(+), 1 deletion(-) diff --git a/server/routes.ts b/server/routes.ts index 9e4f39c..21fe085 100644 --- a/server/routes.ts +++ b/server/routes.ts @@ -55,6 +55,20 @@ export async function registerRoutes(app: Express): Promise { } }); + app.get('/api/media-outlets/:slug/articles', async (req, res) => { + try { + const outlet = await storage.getMediaOutletBySlug(req.params.slug); + if (!outlet) { + return res.status(404).json({ message: "Media outlet not found" }); + } + const articles = await storage.getArticlesByOutlet(outlet.id); + res.json(articles); + } catch (error) { + console.error("Error fetching articles by slug:", error); + res.status(500).json({ message: "Failed to fetch articles" }); + } + }); + app.get('/api/articles/:slug', async (req, res) => { try { const article = await storage.getArticleBySlug(req.params.slug); @@ -68,6 +82,20 @@ export async function registerRoutes(app: Express): Promise { } }); + app.get('/api/articles/:slug/markets', async (req, res) => { + try { + const article = await storage.getArticleBySlug(req.params.slug); + if (!article) { + return res.status(404).json({ message: "Article not found" }); + } + const markets = await storage.getPredictionMarkets(article.id); + res.json(markets); + } catch (error) { + console.error("Error fetching prediction markets for article:", error); + res.status(500).json({ message: "Failed to fetch prediction markets" }); + } + }); + app.get('/api/articles/featured', async (req, res) => { try { const limit = parseInt(req.query.limit as string) || 10; @@ -123,9 +151,26 @@ export async function registerRoutes(app: Express): Promise { } }); + app.get('/api/media-outlets/:slug/auction', async (req, res) => { + try { + const outlet = await storage.getMediaOutletBySlug(req.params.slug); + if (!outlet) { + return res.status(404).json({ message: "Media outlet not found" }); + } + const auction = await storage.getAuctionByMediaOutlet(outlet.id); + if (!auction) { + return res.status(404).json({ message: "No active auction found for this media outlet" }); + } + res.json(auction); + } catch (error) { + console.error("Error fetching auction:", error); + res.status(500).json({ message: "Failed to fetch auction" }); + } + }); + app.post('/api/auctions/:id/bid', isAuthenticated, async (req: any, res) => { try { - const userId = req.user.claims.sub; + const userId = req.user.id; const bidData = insertBidSchema.parse({ ...req.body, auctionId: req.params.id, @@ -140,6 +185,33 @@ export async function registerRoutes(app: Express): Promise { } }); + app.post('/api/media-outlets/:slug/auction/bids', isAuthenticated, async (req: any, res) => { + try { + const outlet = await storage.getMediaOutletBySlug(req.params.slug); + if (!outlet) { + return res.status(404).json({ message: "Media outlet not found" }); + } + + const auction = await storage.getAuctionByMediaOutlet(outlet.id); + if (!auction) { + return res.status(404).json({ message: "No active auction found for this media outlet" }); + } + + const userId = req.user.id; + const bidData = insertBidSchema.parse({ + ...req.body, + auctionId: auction.id, + bidderId: userId + }); + + const bid = await storage.placeBid(bidData); + res.status(201).json(bid); + } catch (error) { + console.error("Error placing bid:", error); + res.status(500).json({ message: "Failed to place bid" }); + } + }); + // Media outlet request routes app.get('/api/media-outlet-requests', isAuthenticated, async (req: any, res) => { try { diff --git a/server/storage.ts b/server/storage.ts index 89d6445..8d96152 100644 --- a/server/storage.ts +++ b/server/storage.ts @@ -205,6 +205,30 @@ export class DatabaseStorage implements IStorage { } async placeBid(bid: InsertBid): Promise { + // First, get the auction to validate + const auction = await this.getAuctionById(bid.auctionId); + if (!auction) { + throw new Error("Auction not found"); + } + + // Validate auction status + if (!auction.isActive) { + throw new Error("Auction is not active"); + } + + // Validate auction end date + if (new Date() > auction.endDate) { + throw new Error("Auction has ended"); + } + + // Validate bid amount + const currentBidAmount = parseFloat(auction.currentBid || "0"); + const bidAmount = parseFloat(bid.amount.toString()); + + if (bidAmount <= currentBidAmount) { + throw new Error(`Bid amount must be higher than current bid of ${currentBidAmount}`); + } + const [newBid] = await db.insert(bids).values(bid).returning(); // Update auction with highest bid