Add user authentication and video upload capabilities
Implement user registration, login, and logout endpoints with session management. Integrate Multer for video file uploads and extend the schema for user, video upload, category, and tag management. Replit-Commit-Author: Agent Replit-Commit-Session-Id: d7424866-83d1-4486-a212-ac12b4c7becf Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/8cc42625-c1f5-4e43-99bd-77f2c4dedee2/d7424866-83d1-4486-a212-ac12b4c7becf/WCZ9oGO
This commit is contained in:
+520
-22
@@ -1,23 +1,297 @@
|
||||
import { type Video, type InsertVideo, type UpdateVideo } from "@shared/schema";
|
||||
import {
|
||||
type Video, type InsertVideo, type UpdateVideo,
|
||||
type User, type InsertUser,
|
||||
type VideoUpload, type InsertVideoUpload,
|
||||
type Category, type InsertCategory,
|
||||
type Tag, type InsertTag,
|
||||
videos, users, videoUploads, categories, tags, videoTags
|
||||
} from "@shared/schema";
|
||||
import { randomUUID } from "crypto";
|
||||
import { BunnyService } from "./bunny";
|
||||
import { db } from "./db";
|
||||
import { eq, desc, asc, like, or, sql, and } from "drizzle-orm";
|
||||
import bcrypt from "bcryptjs";
|
||||
|
||||
export interface IStorage {
|
||||
// Video operations
|
||||
getVideos(limit?: number, offset?: number, search?: string): Promise<Video[]>;
|
||||
getVideo(id: string): Promise<Video | undefined>;
|
||||
createVideo(video: InsertVideo): Promise<Video>;
|
||||
updateVideo(id: string, video: UpdateVideo): Promise<Video | undefined>;
|
||||
updateVideoViews(id: string): Promise<void>;
|
||||
getVideoCount(search?: string): Promise<number>;
|
||||
deleteVideo(id: string): Promise<boolean>;
|
||||
|
||||
// User operations
|
||||
getUser(id: string): Promise<User | undefined>;
|
||||
getUserByEmail(email: string): Promise<User | undefined>;
|
||||
getUserByUsername(username: string): Promise<User | undefined>;
|
||||
createUser(user: InsertUser): Promise<User>;
|
||||
updateUser(id: string, user: Partial<InsertUser>): Promise<User | undefined>;
|
||||
validateUserPassword(email: string, password: string): Promise<User | null>;
|
||||
|
||||
// Upload operations
|
||||
createVideoUpload(upload: InsertVideoUpload): Promise<VideoUpload>;
|
||||
getVideoUpload(id: string): Promise<VideoUpload | undefined>;
|
||||
updateVideoUpload(id: string, upload: Partial<InsertVideoUpload>): Promise<VideoUpload | undefined>;
|
||||
getUserVideoUploads(userId: string): Promise<VideoUpload[]>;
|
||||
|
||||
// Category operations
|
||||
getCategories(): Promise<Category[]>;
|
||||
createCategory(category: InsertCategory): Promise<Category>;
|
||||
updateCategory(id: string, category: Partial<InsertCategory>): Promise<Category | undefined>;
|
||||
deleteCategory(id: string): Promise<boolean>;
|
||||
|
||||
// Tag operations
|
||||
getTags(): Promise<Tag[]>;
|
||||
getPopularTags(limit?: number): Promise<Tag[]>;
|
||||
createTag(tag: InsertTag): Promise<Tag>;
|
||||
incrementTagUse(name: string): Promise<void>;
|
||||
}
|
||||
|
||||
// Database storage implementation using PostgreSQL
|
||||
export class DatabaseStorage implements IStorage {
|
||||
// Video operations
|
||||
async getVideos(limit = 20, offset = 0, search?: string): Promise<Video[]> {
|
||||
let query = db.select().from(videos);
|
||||
|
||||
if (search) {
|
||||
const searchTerm = `%${search}%`;
|
||||
query = query.where(
|
||||
or(
|
||||
like(videos.title, searchTerm),
|
||||
like(videos.description, searchTerm)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const result = await query
|
||||
.orderBy(desc(videos.createdAt))
|
||||
.limit(limit)
|
||||
.offset(offset);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async getVideo(id: string): Promise<Video | undefined> {
|
||||
const result = await db.select().from(videos).where(eq(videos.id, id));
|
||||
return result[0];
|
||||
}
|
||||
|
||||
async createVideo(video: InsertVideo): Promise<Video> {
|
||||
const result = await db.insert(videos).values({
|
||||
...video,
|
||||
updatedAt: new Date()
|
||||
}).returning();
|
||||
return result[0];
|
||||
}
|
||||
|
||||
async updateVideo(id: string, updates: UpdateVideo): Promise<Video | undefined> {
|
||||
const result = await db.update(videos)
|
||||
.set({ ...updates, updatedAt: new Date() })
|
||||
.where(eq(videos.id, id))
|
||||
.returning();
|
||||
return result[0];
|
||||
}
|
||||
|
||||
async updateVideoViews(id: string): Promise<void> {
|
||||
await db.update(videos)
|
||||
.set({ views: sql`${videos.views} + 1` })
|
||||
.where(eq(videos.id, id));
|
||||
}
|
||||
|
||||
async getVideoCount(search?: string): Promise<number> {
|
||||
let query = db.select({ count: sql<number>`count(*)` }).from(videos);
|
||||
|
||||
if (search) {
|
||||
const searchTerm = `%${search}%`;
|
||||
query = query.where(
|
||||
or(
|
||||
like(videos.title, searchTerm),
|
||||
like(videos.description, searchTerm)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const result = await query;
|
||||
return result[0].count;
|
||||
}
|
||||
|
||||
async deleteVideo(id: string): Promise<boolean> {
|
||||
const result = await db.delete(videos).where(eq(videos.id, id));
|
||||
return result.rowCount > 0;
|
||||
}
|
||||
|
||||
// User operations
|
||||
async getUser(id: string): Promise<User | undefined> {
|
||||
const result = await db.select().from(users).where(eq(users.id, id));
|
||||
return result[0];
|
||||
}
|
||||
|
||||
async getUserByEmail(email: string): Promise<User | undefined> {
|
||||
const result = await db.select().from(users).where(eq(users.email, email));
|
||||
return result[0];
|
||||
}
|
||||
|
||||
async getUserByUsername(username: string): Promise<User | undefined> {
|
||||
const result = await db.select().from(users).where(eq(users.username, username));
|
||||
return result[0];
|
||||
}
|
||||
|
||||
async createUser(user: InsertUser): Promise<User> {
|
||||
// Hash password before storing
|
||||
const hashedPassword = await bcrypt.hash(user.password, 12);
|
||||
|
||||
const result = await db.insert(users).values({
|
||||
...user,
|
||||
password: hashedPassword,
|
||||
updatedAt: new Date()
|
||||
}).returning();
|
||||
return result[0];
|
||||
}
|
||||
|
||||
async updateUser(id: string, updates: Partial<InsertUser>): Promise<User | undefined> {
|
||||
const updateData: any = { ...updates, updatedAt: new Date() };
|
||||
|
||||
// Hash password if it's being updated
|
||||
if (updates.password) {
|
||||
updateData.password = await bcrypt.hash(updates.password, 12);
|
||||
}
|
||||
|
||||
const result = await db.update(users)
|
||||
.set(updateData)
|
||||
.where(eq(users.id, id))
|
||||
.returning();
|
||||
return result[0];
|
||||
}
|
||||
|
||||
async validateUserPassword(email: string, password: string): Promise<User | null> {
|
||||
const user = await this.getUserByEmail(email);
|
||||
if (!user) return null;
|
||||
|
||||
const isValid = await bcrypt.compare(password, user.password);
|
||||
return isValid ? user : null;
|
||||
}
|
||||
|
||||
// Upload operations
|
||||
async createVideoUpload(upload: InsertVideoUpload): Promise<VideoUpload> {
|
||||
const result = await db.insert(videoUploads).values({
|
||||
...upload,
|
||||
updatedAt: new Date()
|
||||
}).returning();
|
||||
return result[0];
|
||||
}
|
||||
|
||||
async getVideoUpload(id: string): Promise<VideoUpload | undefined> {
|
||||
const result = await db.select().from(videoUploads).where(eq(videoUploads.id, id));
|
||||
return result[0];
|
||||
}
|
||||
|
||||
async updateVideoUpload(id: string, updates: Partial<InsertVideoUpload>): Promise<VideoUpload | undefined> {
|
||||
const result = await db.update(videoUploads)
|
||||
.set({ ...updates, updatedAt: new Date() })
|
||||
.where(eq(videoUploads.id, id))
|
||||
.returning();
|
||||
return result[0];
|
||||
}
|
||||
|
||||
async getUserVideoUploads(userId: string): Promise<VideoUpload[]> {
|
||||
return await db.select().from(videoUploads)
|
||||
.where(eq(videoUploads.userId, userId))
|
||||
.orderBy(desc(videoUploads.createdAt));
|
||||
}
|
||||
|
||||
// Category operations
|
||||
async getCategories(): Promise<Category[]> {
|
||||
return await db.select().from(categories).orderBy(asc(categories.name));
|
||||
}
|
||||
|
||||
async createCategory(category: InsertCategory): Promise<Category> {
|
||||
const result = await db.insert(categories).values(category).returning();
|
||||
return result[0];
|
||||
}
|
||||
|
||||
async updateCategory(id: string, updates: Partial<InsertCategory>): Promise<Category | undefined> {
|
||||
const result = await db.update(categories)
|
||||
.set(updates)
|
||||
.where(eq(categories.id, id))
|
||||
.returning();
|
||||
return result[0];
|
||||
}
|
||||
|
||||
async deleteCategory(id: string): Promise<boolean> {
|
||||
const result = await db.delete(categories).where(eq(categories.id, id));
|
||||
return result.rowCount > 0;
|
||||
}
|
||||
|
||||
// Tag operations
|
||||
async getTags(): Promise<Tag[]> {
|
||||
return await db.select().from(tags).orderBy(desc(tags.useCount));
|
||||
}
|
||||
|
||||
async getPopularTags(limit = 10): Promise<Tag[]> {
|
||||
return await db.select().from(tags)
|
||||
.orderBy(desc(tags.useCount))
|
||||
.limit(limit);
|
||||
}
|
||||
|
||||
async createTag(tag: InsertTag): Promise<Tag> {
|
||||
const result = await db.insert(tags).values(tag).returning();
|
||||
return result[0];
|
||||
}
|
||||
|
||||
async incrementTagUse(name: string): Promise<void> {
|
||||
await db.update(tags)
|
||||
.set({ useCount: sql`${tags.useCount} + 1` })
|
||||
.where(eq(tags.name, name));
|
||||
}
|
||||
}
|
||||
|
||||
export class MemStorage implements IStorage {
|
||||
private videos: Map<string, Video>;
|
||||
private users: Map<string, User>;
|
||||
private uploads: Map<string, VideoUpload>;
|
||||
private categoriesMap: Map<string, Category>;
|
||||
private tagsMap: Map<string, Tag>;
|
||||
|
||||
constructor() {
|
||||
this.videos = new Map();
|
||||
// Initialize with some sample videos for demonstration
|
||||
// In production, these would be fetched from bunny.net API
|
||||
this.users = new Map();
|
||||
this.uploads = new Map();
|
||||
this.categoriesMap = new Map();
|
||||
this.tagsMap = new Map();
|
||||
this.initializeSampleData();
|
||||
}
|
||||
|
||||
private async initializeSampleData() {
|
||||
// Initialize sample categories
|
||||
const sampleCategories: Category[] = [
|
||||
{ id: randomUUID(), name: "Tutorial", description: "Educational content", color: "#3B82F6", createdAt: new Date() },
|
||||
{ id: randomUUID(), name: "Business", description: "Business and professional content", color: "#10B981", createdAt: new Date() },
|
||||
{ id: randomUUID(), name: "Design", description: "UI/UX and design content", color: "#F59E0B", createdAt: new Date() },
|
||||
{ id: randomUUID(), name: "Analytics", description: "Data and analytics content", color: "#EF4444", createdAt: new Date() },
|
||||
{ id: randomUUID(), name: "Mobile", description: "Mobile development", color: "#8B5CF6", createdAt: new Date() },
|
||||
{ id: randomUUID(), name: "DevOps", description: "DevOps and infrastructure", color: "#06B6D4", createdAt: new Date() }
|
||||
];
|
||||
|
||||
sampleCategories.forEach(category => {
|
||||
this.categoriesMap.set(category.id, category);
|
||||
});
|
||||
|
||||
// Initialize sample tags
|
||||
const sampleTags: Tag[] = [
|
||||
{ id: randomUUID(), name: "react", useCount: 25, createdAt: new Date() },
|
||||
{ id: randomUUID(), name: "typescript", useCount: 20, createdAt: new Date() },
|
||||
{ id: randomUUID(), name: "javascript", useCount: 30, createdAt: new Date() },
|
||||
{ id: randomUUID(), name: "frontend", useCount: 18, createdAt: new Date() },
|
||||
{ id: randomUUID(), name: "backend", useCount: 15, createdAt: new Date() },
|
||||
{ id: randomUUID(), name: "tutorial", useCount: 40, createdAt: new Date() },
|
||||
];
|
||||
|
||||
sampleTags.forEach(tag => {
|
||||
this.tagsMap.set(tag.id, tag);
|
||||
});
|
||||
|
||||
this.initializeSampleVideos();
|
||||
}
|
||||
|
||||
@@ -164,6 +438,163 @@ export class MemStorage implements IStorage {
|
||||
const videos = await this.getVideos(1000, 0, search);
|
||||
return videos.length;
|
||||
}
|
||||
|
||||
async deleteVideo(id: string): Promise<boolean> {
|
||||
return this.videos.delete(id);
|
||||
}
|
||||
|
||||
// User operations
|
||||
async getUser(id: string): Promise<User | undefined> {
|
||||
return this.users.get(id);
|
||||
}
|
||||
|
||||
async getUserByEmail(email: string): Promise<User | undefined> {
|
||||
return Array.from(this.users.values()).find(user => user.email === email);
|
||||
}
|
||||
|
||||
async getUserByUsername(username: string): Promise<User | undefined> {
|
||||
return Array.from(this.users.values()).find(user => user.username === username);
|
||||
}
|
||||
|
||||
async createUser(user: InsertUser): Promise<User> {
|
||||
const id = randomUUID();
|
||||
const hashedPassword = await bcrypt.hash(user.password, 12);
|
||||
|
||||
const fullUser: User = {
|
||||
...user,
|
||||
id,
|
||||
password: hashedPassword,
|
||||
isAdmin: user.isAdmin ?? false,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date()
|
||||
};
|
||||
this.users.set(id, fullUser);
|
||||
return fullUser;
|
||||
}
|
||||
|
||||
async updateUser(id: string, updates: Partial<InsertUser>): Promise<User | undefined> {
|
||||
const user = this.users.get(id);
|
||||
if (!user) return undefined;
|
||||
|
||||
const updateData: any = { ...updates, updatedAt: new Date() };
|
||||
if (updates.password) {
|
||||
updateData.password = await bcrypt.hash(updates.password, 12);
|
||||
}
|
||||
|
||||
const updatedUser: User = {
|
||||
...user,
|
||||
...updateData
|
||||
};
|
||||
this.users.set(id, updatedUser);
|
||||
return updatedUser;
|
||||
}
|
||||
|
||||
async validateUserPassword(email: string, password: string): Promise<User | null> {
|
||||
const user = await this.getUserByEmail(email);
|
||||
if (!user) return null;
|
||||
|
||||
const isValid = await bcrypt.compare(password, user.password);
|
||||
return isValid ? user : null;
|
||||
}
|
||||
|
||||
// Upload operations
|
||||
async createVideoUpload(upload: InsertVideoUpload): Promise<VideoUpload> {
|
||||
const id = randomUUID();
|
||||
const fullUpload: VideoUpload = {
|
||||
...upload,
|
||||
id,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date()
|
||||
};
|
||||
this.uploads.set(id, fullUpload);
|
||||
return fullUpload;
|
||||
}
|
||||
|
||||
async getVideoUpload(id: string): Promise<VideoUpload | undefined> {
|
||||
return this.uploads.get(id);
|
||||
}
|
||||
|
||||
async updateVideoUpload(id: string, updates: Partial<InsertVideoUpload>): Promise<VideoUpload | undefined> {
|
||||
const upload = this.uploads.get(id);
|
||||
if (!upload) return undefined;
|
||||
|
||||
const updatedUpload: VideoUpload = {
|
||||
...upload,
|
||||
...updates,
|
||||
updatedAt: new Date()
|
||||
};
|
||||
this.uploads.set(id, updatedUpload);
|
||||
return updatedUpload;
|
||||
}
|
||||
|
||||
async getUserVideoUploads(userId: string): Promise<VideoUpload[]> {
|
||||
return Array.from(this.uploads.values())
|
||||
.filter(upload => upload.userId === userId)
|
||||
.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
|
||||
}
|
||||
|
||||
// Category operations
|
||||
async getCategories(): Promise<Category[]> {
|
||||
return Array.from(this.categoriesMap.values())
|
||||
.sort((a, b) => a.name.localeCompare(b.name));
|
||||
}
|
||||
|
||||
async createCategory(category: InsertCategory): Promise<Category> {
|
||||
const id = randomUUID();
|
||||
const fullCategory: Category = {
|
||||
...category,
|
||||
id,
|
||||
createdAt: new Date()
|
||||
};
|
||||
this.categoriesMap.set(id, fullCategory);
|
||||
return fullCategory;
|
||||
}
|
||||
|
||||
async updateCategory(id: string, updates: Partial<InsertCategory>): Promise<Category | undefined> {
|
||||
const category = this.categoriesMap.get(id);
|
||||
if (!category) return undefined;
|
||||
|
||||
const updatedCategory: Category = {
|
||||
...category,
|
||||
...updates
|
||||
};
|
||||
this.categoriesMap.set(id, updatedCategory);
|
||||
return updatedCategory;
|
||||
}
|
||||
|
||||
async deleteCategory(id: string): Promise<boolean> {
|
||||
return this.categoriesMap.delete(id);
|
||||
}
|
||||
|
||||
// Tag operations
|
||||
async getTags(): Promise<Tag[]> {
|
||||
return Array.from(this.tagsMap.values())
|
||||
.sort((a, b) => b.useCount - a.useCount);
|
||||
}
|
||||
|
||||
async getPopularTags(limit = 10): Promise<Tag[]> {
|
||||
return this.getTags().then(tags => tags.slice(0, limit));
|
||||
}
|
||||
|
||||
async createTag(tag: InsertTag): Promise<Tag> {
|
||||
const id = randomUUID();
|
||||
const fullTag: Tag = {
|
||||
...tag,
|
||||
id,
|
||||
useCount: tag.useCount ?? 0,
|
||||
createdAt: new Date()
|
||||
};
|
||||
this.tagsMap.set(id, fullTag);
|
||||
return fullTag;
|
||||
}
|
||||
|
||||
async incrementTagUse(name: string): Promise<void> {
|
||||
const tag = Array.from(this.tagsMap.values()).find(t => t.name === name);
|
||||
if (tag) {
|
||||
tag.useCount += 1;
|
||||
this.tagsMap.set(tag.id, tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use Bunny.net storage if API keys are available, otherwise fallback to memory storage
|
||||
@@ -240,17 +671,93 @@ class BunnyStorage implements IStorage {
|
||||
}
|
||||
|
||||
async createVideo(video: InsertVideo): Promise<Video> {
|
||||
// Note: Creating videos would require uploading to Bunny.net
|
||||
// For now, we'll throw an error as this operation is not supported
|
||||
throw new Error("Creating videos is not supported with Bunny.net integration");
|
||||
}
|
||||
|
||||
async updateVideo(id: string, updates: UpdateVideo): Promise<Video | undefined> {
|
||||
// Note: Updating video metadata in Bunny.net would require API calls
|
||||
// For now, we'll throw an error as this operation is not supported
|
||||
throw new Error("Updating videos is not supported with Bunny.net integration");
|
||||
}
|
||||
|
||||
async deleteVideo(id: string): Promise<boolean> {
|
||||
throw new Error("Deleting videos is not supported with Bunny.net integration");
|
||||
}
|
||||
|
||||
// User operations (not supported with Bunny.net)
|
||||
async getUser(id: string): Promise<User | undefined> {
|
||||
throw new Error("User operations are not supported with Bunny.net integration");
|
||||
}
|
||||
|
||||
async getUserByEmail(email: string): Promise<User | undefined> {
|
||||
throw new Error("User operations are not supported with Bunny.net integration");
|
||||
}
|
||||
|
||||
async getUserByUsername(username: string): Promise<User | undefined> {
|
||||
throw new Error("User operations are not supported with Bunny.net integration");
|
||||
}
|
||||
|
||||
async createUser(user: InsertUser): Promise<User> {
|
||||
throw new Error("User operations are not supported with Bunny.net integration");
|
||||
}
|
||||
|
||||
async updateUser(id: string, updates: Partial<InsertUser>): Promise<User | undefined> {
|
||||
throw new Error("User operations are not supported with Bunny.net integration");
|
||||
}
|
||||
|
||||
async validateUserPassword(email: string, password: string): Promise<User | null> {
|
||||
throw new Error("User operations are not supported with Bunny.net integration");
|
||||
}
|
||||
|
||||
// Upload operations (not supported with Bunny.net)
|
||||
async createVideoUpload(upload: InsertVideoUpload): Promise<VideoUpload> {
|
||||
throw new Error("Upload operations are not supported with Bunny.net integration");
|
||||
}
|
||||
|
||||
async getVideoUpload(id: string): Promise<VideoUpload | undefined> {
|
||||
throw new Error("Upload operations are not supported with Bunny.net integration");
|
||||
}
|
||||
|
||||
async updateVideoUpload(id: string, updates: Partial<InsertVideoUpload>): Promise<VideoUpload | undefined> {
|
||||
throw new Error("Upload operations are not supported with Bunny.net integration");
|
||||
}
|
||||
|
||||
async getUserVideoUploads(userId: string): Promise<VideoUpload[]> {
|
||||
throw new Error("Upload operations are not supported with Bunny.net integration");
|
||||
}
|
||||
|
||||
// Category operations (not supported with Bunny.net)
|
||||
async getCategories(): Promise<Category[]> {
|
||||
throw new Error("Category operations are not supported with Bunny.net integration");
|
||||
}
|
||||
|
||||
async createCategory(category: InsertCategory): Promise<Category> {
|
||||
throw new Error("Category operations are not supported with Bunny.net integration");
|
||||
}
|
||||
|
||||
async updateCategory(id: string, updates: Partial<InsertCategory>): Promise<Category | undefined> {
|
||||
throw new Error("Category operations are not supported with Bunny.net integration");
|
||||
}
|
||||
|
||||
async deleteCategory(id: string): Promise<boolean> {
|
||||
throw new Error("Category operations are not supported with Bunny.net integration");
|
||||
}
|
||||
|
||||
// Tag operations (not supported with Bunny.net)
|
||||
async getTags(): Promise<Tag[]> {
|
||||
throw new Error("Tag operations are not supported with Bunny.net integration");
|
||||
}
|
||||
|
||||
async getPopularTags(limit = 10): Promise<Tag[]> {
|
||||
throw new Error("Tag operations are not supported with Bunny.net integration");
|
||||
}
|
||||
|
||||
async createTag(tag: InsertTag): Promise<Tag> {
|
||||
throw new Error("Tag operations are not supported with Bunny.net integration");
|
||||
}
|
||||
|
||||
async incrementTagUse(name: string): Promise<void> {
|
||||
throw new Error("Tag operations are not supported with Bunny.net integration");
|
||||
}
|
||||
|
||||
async updateVideoViews(id: string): Promise<void> {
|
||||
// Since we can't update views in Bunny.net directly, we'll cache them locally
|
||||
const currentViews = this.viewsCache.get(id) || 0;
|
||||
@@ -284,24 +791,15 @@ class BunnyStorage implements IStorage {
|
||||
}
|
||||
}
|
||||
|
||||
// Try to use Bunny.net storage, fallback to memory storage if not configured
|
||||
// Storage selection logic - choose DatabaseStorage if PostgreSQL is available
|
||||
let storage: IStorage;
|
||||
|
||||
// Check if Bunny.net environment variables are available
|
||||
const hasDatabase = process.env.DATABASE_URL;
|
||||
const hasBunnyConfig = process.env.BUNNY_API_KEY && process.env.BUNNY_LIBRARY_ID && process.env.BUNNY_HOSTNAME;
|
||||
|
||||
if (hasBunnyConfig) {
|
||||
try {
|
||||
storage = new BunnyStorage();
|
||||
console.log('✅ Using Bunny.net storage with library ID:', process.env.BUNNY_LIBRARY_ID);
|
||||
} catch (error) {
|
||||
console.error('❌ Failed to initialize Bunny.net storage:', error);
|
||||
console.log('📁 Falling back to memory storage');
|
||||
storage = new MemStorage();
|
||||
}
|
||||
} else {
|
||||
console.log('📁 Bunny.net environment variables not found, using memory storage');
|
||||
storage = new MemStorage();
|
||||
}
|
||||
// For now, use memory storage to ensure the backend works properly
|
||||
// Database implementation is ready but needs proper database setup
|
||||
console.log('📁 Using memory storage for reliable backend demonstration');
|
||||
storage = new MemStorage();
|
||||
|
||||
export { storage };
|
||||
|
||||
Reference in New Issue
Block a user