Add core UI components and layout for media platform
Initializes the client-side application with fundamental UI components, including navigation, cards for articles and auctions, and various elements for user interaction and display. 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/bVdKIaU
This commit is contained in:
290
server/storage.ts
Normal file
290
server/storage.ts
Normal file
@ -0,0 +1,290 @@
|
||||
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>;
|
||||
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.id,
|
||||
set: {
|
||||
...userData,
|
||||
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 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();
|
||||
Reference in New Issue
Block a user