Files
sapiens-web2/server/storage.ts
kimjaehyeon0101 020f212281 Enhance the media auction system with improved bid validation and data handling
Update the MediaOutletAuction component to correctly parse currency values for bid placement and display, and modify the DatabaseStorage to include a new method for fetching active auctions by media outlet ID.

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
2025-09-29 18:59:03 +00:00

301 lines
9.5 KiB
TypeScript

import {
users,
mediaOutlets,
articles,
predictionMarkets,
auctions,
bids,
mediaOutletRequests,
comments,
type User,
type UpsertUser,
type MediaOutlet,
type InsertMediaOutlet,
type Article,
type InsertArticle,
type PredictionMarket,
type InsertPredictionMarket,
type Auction,
type InsertAuction,
type Bid,
type InsertBid,
type MediaOutletRequest,
type InsertMediaOutletRequest,
type Comment,
type InsertComment,
} from "@shared/schema";
import { db } from "./db";
import { eq, desc, and, ilike, sql } from "drizzle-orm";
// Interface for storage operations
export interface IStorage {
// User operations (mandatory for Replit Auth)
getUser(id: string): Promise<User | undefined>;
upsertUser(user: UpsertUser): Promise<User>;
// Media outlet operations
getMediaOutlets(category?: string): Promise<MediaOutlet[]>;
getMediaOutletBySlug(slug: string): Promise<MediaOutlet | undefined>;
createMediaOutlet(outlet: InsertMediaOutlet): Promise<MediaOutlet>;
updateMediaOutlet(id: string, outlet: Partial<InsertMediaOutlet>): Promise<MediaOutlet>;
// Article operations
getArticlesByOutlet(mediaOutletId: string): Promise<Article[]>;
getArticleBySlug(slug: string): Promise<Article | undefined>;
createArticle(article: InsertArticle): Promise<Article>;
updateArticle(id: string, article: Partial<InsertArticle>): Promise<Article>;
getFeaturedArticles(limit?: number): Promise<Article[]>;
// Prediction market operations
getPredictionMarkets(articleId?: string): Promise<PredictionMarket[]>;
createPredictionMarket(market: InsertPredictionMarket): Promise<PredictionMarket>;
// Auction operations
getActiveAuctions(): Promise<Auction[]>;
getAuctionById(id: string): Promise<Auction | undefined>;
getAuctionByMediaOutlet(mediaOutletId: string): Promise<Auction | undefined>;
createAuction(auction: InsertAuction): Promise<Auction>;
placeBid(bid: InsertBid): Promise<Bid>;
// Media outlet request operations
getMediaOutletRequests(status?: string): Promise<MediaOutletRequest[]>;
createMediaOutletRequest(request: InsertMediaOutletRequest): Promise<MediaOutletRequest>;
updateMediaOutletRequestStatus(id: string, status: string, reviewerId: string): Promise<MediaOutletRequest>;
// Comment operations
getCommentsByArticle(articleId: string): Promise<Comment[]>;
createComment(comment: InsertComment): Promise<Comment>;
// Analytics operations
getAnalytics(): Promise<{
totalArticles: number;
activePredictions: number;
liveAuctions: number;
totalRevenue: number;
}>;
}
export class DatabaseStorage implements IStorage {
// User operations (mandatory for Replit Auth)
async getUser(id: string): Promise<User | undefined> {
const [user] = await db.select().from(users).where(eq(users.id, id));
return user;
}
async upsertUser(userData: UpsertUser): Promise<User> {
const [user] = await db
.insert(users)
.values(userData)
.onConflictDoUpdate({
target: users.email,
set: {
firstName: userData.firstName,
lastName: userData.lastName,
profileImageUrl: userData.profileImageUrl,
updatedAt: new Date(),
},
})
.returning();
return user;
}
// Media outlet operations
async getMediaOutlets(category?: string): Promise<MediaOutlet[]> {
if (category) {
return await db.select().from(mediaOutlets).where(and(eq(mediaOutlets.isActive, true), eq(mediaOutlets.category, category)));
}
return await db.select().from(mediaOutlets).where(eq(mediaOutlets.isActive, true));
}
async getMediaOutletBySlug(slug: string): Promise<MediaOutlet | undefined> {
const [outlet] = await db.select().from(mediaOutlets).where(eq(mediaOutlets.slug, slug));
return outlet;
}
async createMediaOutlet(outlet: InsertMediaOutlet): Promise<MediaOutlet> {
const [newOutlet] = await db.insert(mediaOutlets).values(outlet).returning();
return newOutlet;
}
async updateMediaOutlet(id: string, outlet: Partial<InsertMediaOutlet>): Promise<MediaOutlet> {
const [updated] = await db
.update(mediaOutlets)
.set({ ...outlet, updatedAt: new Date() })
.where(eq(mediaOutlets.id, id))
.returning();
return updated;
}
// Article operations
async getArticlesByOutlet(mediaOutletId: string): Promise<Article[]> {
return await db
.select()
.from(articles)
.where(eq(articles.mediaOutletId, mediaOutletId))
.orderBy(desc(articles.publishedAt));
}
async getArticleBySlug(slug: string): Promise<Article | undefined> {
const [article] = await db.select().from(articles).where(eq(articles.slug, slug));
return article;
}
async createArticle(article: InsertArticle): Promise<Article> {
const [newArticle] = await db.insert(articles).values(article).returning();
return newArticle;
}
async updateArticle(id: string, article: Partial<InsertArticle>): Promise<Article> {
const [updated] = await db
.update(articles)
.set({ ...article, updatedAt: new Date() })
.where(eq(articles.id, id))
.returning();
return updated;
}
async getFeaturedArticles(limit = 10): Promise<Article[]> {
return await db
.select()
.from(articles)
.where(eq(articles.isFeatured, true))
.orderBy(desc(articles.publishedAt))
.limit(limit);
}
// Prediction market operations
async getPredictionMarkets(articleId?: string): Promise<PredictionMarket[]> {
if (articleId) {
return await db.select().from(predictionMarkets).where(and(eq(predictionMarkets.isActive, true), eq(predictionMarkets.articleId, articleId)));
}
return await db.select().from(predictionMarkets).where(eq(predictionMarkets.isActive, true)).orderBy(desc(predictionMarkets.createdAt));
}
async createPredictionMarket(market: InsertPredictionMarket): Promise<PredictionMarket> {
const [newMarket] = await db.insert(predictionMarkets).values(market).returning();
return newMarket;
}
// Auction operations
async getActiveAuctions(): Promise<Auction[]> {
return await db
.select()
.from(auctions)
.where(and(eq(auctions.isActive, true), sql`end_date > NOW()`))
.orderBy(auctions.endDate);
}
async getAuctionById(id: string): Promise<Auction | undefined> {
const [auction] = await db.select().from(auctions).where(eq(auctions.id, id));
return auction;
}
async getAuctionByMediaOutlet(mediaOutletId: string): Promise<Auction | undefined> {
const [auction] = await db.select().from(auctions).where(
and(eq(auctions.mediaOutletId, mediaOutletId), eq(auctions.isActive, true))
);
return auction;
}
async createAuction(auction: InsertAuction): Promise<Auction> {
const [newAuction] = await db.insert(auctions).values(auction).returning();
return newAuction;
}
async placeBid(bid: InsertBid): Promise<Bid> {
const [newBid] = await db.insert(bids).values(bid).returning();
// Update auction with highest bid
await db
.update(auctions)
.set({
currentBid: bid.amount,
highestBidderId: bid.bidderId,
updatedAt: new Date()
})
.where(eq(auctions.id, bid.auctionId));
return newBid;
}
// Media outlet request operations
async getMediaOutletRequests(status?: string): Promise<MediaOutletRequest[]> {
const query = db.select().from(mediaOutletRequests);
if (status) {
return await query.where(eq(mediaOutletRequests.status, status));
}
return await query.orderBy(desc(mediaOutletRequests.createdAt));
}
async createMediaOutletRequest(request: InsertMediaOutletRequest): Promise<MediaOutletRequest> {
const [newRequest] = await db.insert(mediaOutletRequests).values(request).returning();
return newRequest;
}
async updateMediaOutletRequestStatus(id: string, status: string, reviewerId: string): Promise<MediaOutletRequest> {
const [updated] = await db
.update(mediaOutletRequests)
.set({
status,
reviewedBy: reviewerId,
reviewedAt: new Date()
})
.where(eq(mediaOutletRequests.id, id))
.returning();
return updated;
}
// Comment operations
async getCommentsByArticle(articleId: string): Promise<Comment[]> {
return await db
.select()
.from(comments)
.where(eq(comments.articleId, articleId))
.orderBy(desc(comments.isPinned), desc(comments.createdAt));
}
async createComment(comment: InsertComment): Promise<Comment> {
const [newComment] = await db.insert(comments).values(comment).returning();
return newComment;
}
// Analytics operations
async getAnalytics(): Promise<{
totalArticles: number;
activePredictions: number;
liveAuctions: number;
totalRevenue: number;
}> {
const [articleCount] = await db
.select({ count: sql<number>`count(*)` })
.from(articles);
const [predictionCount] = await db
.select({ count: sql<number>`count(*)` })
.from(predictionMarkets)
.where(eq(predictionMarkets.isActive, true));
const [auctionCount] = await db
.select({ count: sql<number>`count(*)` })
.from(auctions)
.where(and(eq(auctions.isActive, true), sql`end_date > NOW()`));
const [revenueSum] = await db
.select({ sum: sql<number>`COALESCE(SUM(current_bid), 0)` })
.from(auctions);
return {
totalArticles: articleCount.count,
activePredictions: predictionCount.count,
liveAuctions: auctionCount.count,
totalRevenue: revenueSum.sum
};
}
}
export const storage = new DatabaseStorage();