Add new video attributes and improve storage management
Implement a hybrid storage solution combining Bunny.net for video operations and PostgreSQL for user data. Update video schema with face detection and content type fields, and add a `isSuperAdmin` flag for user roles. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 22c71427-e484-49cc-bab4-5c9a7f7d98bc Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/8cc42625-c1f5-4e43-99bd-77f2c4dedee2/22c71427-e484-49cc-bab4-5c9a7f7d98bc/JP4XKDb
This commit is contained in:
parent
c71720454f
commit
e394e3f671
1
.replit
1
.replit
@ -40,4 +40,3 @@ args = "npm run dev"
|
|||||||
waitForPort = 5000
|
waitForPort = 5000
|
||||||
|
|
||||||
[agent]
|
[agent]
|
||||||
integrations = ["javascript_database==1.0.0", "javascript_log_in_with_replit==1.0.0", "javascript_object_storage==1.0.0"]
|
|
||||||
|
|||||||
@ -391,11 +391,16 @@ export class MemStorage implements IStorage {
|
|||||||
description: video.description || "",
|
description: video.description || "",
|
||||||
category: video.category || "",
|
category: video.category || "",
|
||||||
customThumbnailUrl: null,
|
customThumbnailUrl: null,
|
||||||
|
faceCenterPosition: null,
|
||||||
|
facesDetected: 0,
|
||||||
|
faceConfidence: 0,
|
||||||
videoUrlMp4: null,
|
videoUrlMp4: null,
|
||||||
videoUrlIframe: null,
|
videoUrlIframe: null,
|
||||||
tags: [],
|
tags: [],
|
||||||
isPublic: true,
|
isPublic: true,
|
||||||
views: video.views || 0,
|
views: video.views || 0,
|
||||||
|
contentType: video.contentType || 'video',
|
||||||
|
genre: video.genre || 'other',
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
uploadStatus: video.uploadStatus || "completed",
|
uploadStatus: video.uploadStatus || "completed",
|
||||||
@ -442,11 +447,16 @@ export class MemStorage implements IStorage {
|
|||||||
description: video.description || "",
|
description: video.description || "",
|
||||||
category: video.category || "",
|
category: video.category || "",
|
||||||
customThumbnailUrl: null,
|
customThumbnailUrl: null,
|
||||||
|
faceCenterPosition: null,
|
||||||
|
facesDetected: 0,
|
||||||
|
faceConfidence: 0,
|
||||||
videoUrlMp4: null,
|
videoUrlMp4: null,
|
||||||
videoUrlIframe: null,
|
videoUrlIframe: null,
|
||||||
tags: video.tags || [],
|
tags: video.tags || [],
|
||||||
isPublic: video.isPublic ?? true,
|
isPublic: video.isPublic ?? true,
|
||||||
views: video.views || 0,
|
views: video.views || 0,
|
||||||
|
contentType: video.contentType || 'video',
|
||||||
|
genre: video.genre || 'other',
|
||||||
uploadStatus: video.uploadStatus || "completed",
|
uploadStatus: video.uploadStatus || "completed",
|
||||||
originalFileName: video.originalFileName || null,
|
originalFileName: video.originalFileName || null,
|
||||||
fileSize: video.fileSize || null,
|
fileSize: video.fileSize || null,
|
||||||
@ -526,6 +536,7 @@ export class MemStorage implements IStorage {
|
|||||||
lastName: user.lastName || null,
|
lastName: user.lastName || null,
|
||||||
profileImageUrl: user.profileImageUrl || null,
|
profileImageUrl: user.profileImageUrl || null,
|
||||||
isAdmin: user.isAdmin ?? false,
|
isAdmin: user.isAdmin ?? false,
|
||||||
|
isSuperAdmin: user.isSuperAdmin ?? false,
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
updatedAt: new Date()
|
updatedAt: new Date()
|
||||||
};
|
};
|
||||||
@ -770,8 +781,8 @@ class BunnyStorage implements IStorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update views cache
|
// Update views cache
|
||||||
if (views !== undefined) {
|
if (views !== undefined && typeof views === 'number') {
|
||||||
this.viewsCache.set(id, (this.viewsCache.get(id) || 0) + (views - (this.viewsCache.get(id) || 0)));
|
this.viewsCache.set(id, views);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return updated video
|
// Return updated video
|
||||||
@ -899,14 +910,145 @@ class BunnyStorage implements IStorage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Storage selection logic - choose DatabaseStorage if PostgreSQL is available
|
// Hybrid storage implementation that uses Bunny.net for videos and Database for users
|
||||||
|
export class HybridStorage implements IStorage {
|
||||||
|
private bunnyStorage: BunnyStorage;
|
||||||
|
private databaseStorage: DatabaseStorage;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.bunnyStorage = new BunnyStorage();
|
||||||
|
this.databaseStorage = new DatabaseStorage();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Video operations - use Bunny.net
|
||||||
|
async getVideos(limit?: number, offset?: number, search?: string): Promise<Video[]> {
|
||||||
|
return this.bunnyStorage.getVideos(limit, offset, search);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getVideo(id: string): Promise<Video | undefined> {
|
||||||
|
return this.bunnyStorage.getVideo(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
async createVideo(video: InsertVideo): Promise<Video> {
|
||||||
|
return this.bunnyStorage.createVideo(video);
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateVideo(id: string, video: UpdateVideo): Promise<Video | undefined> {
|
||||||
|
return this.bunnyStorage.updateVideo(id, video);
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateVideoViews(id: string): Promise<void> {
|
||||||
|
return this.bunnyStorage.updateVideoViews(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getVideoCount(search?: string): Promise<number> {
|
||||||
|
return this.bunnyStorage.getVideoCount(search);
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteVideo(id: string): Promise<boolean> {
|
||||||
|
return this.bunnyStorage.deleteVideo(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// User operations - use Database
|
||||||
|
async getUser(id: string): Promise<User | undefined> {
|
||||||
|
return this.databaseStorage.getUser(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getUserByEmail(email: string): Promise<User | undefined> {
|
||||||
|
return this.databaseStorage.getUserByEmail(email);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getUserByUsername(username: string): Promise<User | undefined> {
|
||||||
|
return this.databaseStorage.getUserByUsername(username);
|
||||||
|
}
|
||||||
|
|
||||||
|
async createUser(user: InsertUser): Promise<User> {
|
||||||
|
return this.databaseStorage.createUser(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateUser(id: string, user: Partial<InsertUser>): Promise<User | undefined> {
|
||||||
|
return this.databaseStorage.updateUser(id, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
async upsertUser(user: any): Promise<User> {
|
||||||
|
return this.databaseStorage.upsertUser(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
async validateUserPassword(email: string, password: string): Promise<User | null> {
|
||||||
|
return this.databaseStorage.validateUserPassword(email, password);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upload operations - use Database
|
||||||
|
async createVideoUpload(upload: InsertVideoUpload): Promise<VideoUpload> {
|
||||||
|
return this.databaseStorage.createVideoUpload(upload);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getVideoUpload(id: string): Promise<VideoUpload | undefined> {
|
||||||
|
return this.databaseStorage.getVideoUpload(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateVideoUpload(id: string, upload: Partial<InsertVideoUpload>): Promise<VideoUpload | undefined> {
|
||||||
|
return this.databaseStorage.updateVideoUpload(id, upload);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getUserVideoUploads(userId: string): Promise<VideoUpload[]> {
|
||||||
|
return this.databaseStorage.getUserVideoUploads(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Category operations - use Database
|
||||||
|
async getCategories(): Promise<Category[]> {
|
||||||
|
return this.databaseStorage.getCategories();
|
||||||
|
}
|
||||||
|
|
||||||
|
async createCategory(category: InsertCategory): Promise<Category> {
|
||||||
|
return this.databaseStorage.createCategory(category);
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateCategory(id: string, category: Partial<InsertCategory>): Promise<Category | undefined> {
|
||||||
|
return this.databaseStorage.updateCategory(id, category);
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteCategory(id: string): Promise<boolean> {
|
||||||
|
return this.databaseStorage.deleteCategory(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tag operations - use Database
|
||||||
|
async getTags(): Promise<Tag[]> {
|
||||||
|
return this.databaseStorage.getTags();
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPopularTags(limit?: number): Promise<Tag[]> {
|
||||||
|
return this.databaseStorage.getPopularTags(limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
async createTag(tag: InsertTag): Promise<Tag> {
|
||||||
|
return this.databaseStorage.createTag(tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
async incrementTagUse(name: string): Promise<void> {
|
||||||
|
return this.databaseStorage.incrementTagUse(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Storage selection logic - use hybrid approach when both Bunny.net and Database are available
|
||||||
let storage: IStorage;
|
let storage: IStorage;
|
||||||
|
|
||||||
const hasDatabase = process.env.DATABASE_URL;
|
const hasDatabase = process.env.DATABASE_URL;
|
||||||
const hasBunnyConfig = process.env.BUNNY_API_KEY && process.env.BUNNY_LIBRARY_ID && process.env.BUNNY_HOSTNAME;
|
const hasBunnyConfig = process.env.BUNNY_API_KEY && process.env.BUNNY_LIBRARY_ID && process.env.BUNNY_HOSTNAME;
|
||||||
|
|
||||||
// Prioritize Bunny.net storage for user's video content
|
// Use hybrid storage when both Bunny.net and Database are available
|
||||||
if (hasBunnyConfig) {
|
if (hasBunnyConfig && hasDatabase) {
|
||||||
|
try {
|
||||||
|
storage = new HybridStorage();
|
||||||
|
console.log('✅ Using Hybrid storage (Bunny.net for videos + Database for users)');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Failed to initialize Hybrid storage:', error);
|
||||||
|
console.log('📁 Falling back to memory storage');
|
||||||
|
storage = new MemStorage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Prioritize Bunny.net storage for video content only
|
||||||
|
else if (hasBunnyConfig) {
|
||||||
try {
|
try {
|
||||||
storage = new BunnyStorage();
|
storage = new BunnyStorage();
|
||||||
console.log('✅ Using Bunny.net storage with library ID:', process.env.BUNNY_LIBRARY_ID);
|
console.log('✅ Using Bunny.net storage with library ID:', process.env.BUNNY_LIBRARY_ID);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user