Spaces:
Runtime error
Runtime error
feat: Implement new admin dashboard with video analysis, object moderation, and user management capabilities.
304bdf5 | "use server"; | |
| import { auth } from "@/lib/auth"; | |
| import { headers } from "next/headers"; | |
| import { ShowcaseService } from "../services/showcase.service"; | |
| import { SocialIngestService } from "../../discovery/services/social-ingest.service"; | |
| export async function quickAddVideoAction(formData: FormData) { | |
| const session = await auth.api.getSession({ | |
| headers: await headers() | |
| }); | |
| // For public showcase, we allow anonymous ingestion | |
| const userId = session?.user?.id || null; | |
| const url = formData.get("url") as string; | |
| if (!url) return { success: false, error: "URL is required" }; | |
| const platform = SocialIngestService.detectPlatform(url); | |
| if (!platform) { | |
| return { success: false, error: "Unsupported platform. Supported: YouTube, TikTok, Instagram, Facebook" }; | |
| } | |
| let videoId: string | null = null; | |
| if (platform === 'youtube') { | |
| const videoIdMatch = url.match(/(?:youtube\.com\/(?:[^\/]+\/.+\/|(?:v|e(?:mbed)?)\/|.*[?&]v=)|youtu\.be\/)([^"&?\/\s]{11})/); | |
| videoId = videoIdMatch ? videoIdMatch[1] : null; | |
| } else if (platform === 'tiktok') { | |
| const match = url.match(/\/video\/(\d+)/); | |
| const rawId = match ? match[1] : `${Date.now()}`; | |
| videoId = rawId.startsWith('tt-') ? rawId : `tt-${rawId}`; | |
| } else if (platform === 'instagram') { | |
| const match = url.match(/\/(?:p|reels|reel)\/([A-Za-z0-9_-]+)/); | |
| const rawId = match ? match[1] : `${Date.now()}`; | |
| videoId = rawId.startsWith('ig-') ? rawId : `ig-${rawId}`; | |
| } else if (platform === 'facebook') { | |
| const match = url.match(/[?&]v=([^&]+)/) || url.match(/\/share\/v\/([^/?]+)/) || url.match(/\/reel\/([^/?]+)/); | |
| const rawId = match ? match[1] : `${Date.now()}`; | |
| videoId = rawId.startsWith('fb-') ? rawId : `fb-${rawId}`; | |
| } else { | |
| videoId = `${platform}-${Date.now()}`; | |
| } | |
| if (!videoId) { | |
| return { success: false, error: `Could not parse video ID from ${platform} URL` }; | |
| } | |
| console.log(`[quickAddVideoAction] Ingesting: ${platform} | ID: ${videoId}`); | |
| try { | |
| const result = await ShowcaseService.ingestAndAnalyze(videoId, userId, { | |
| skipAnalysis: true, | |
| platform: platform, | |
| url: url, | |
| isInWorkbench: false // Preview mode: Don't add to dashboard yet | |
| }); | |
| if (result.success) { | |
| return { success: true, videoId }; | |
| } | |
| return { | |
| success: false, | |
| error: result.error || "Failed to process video. The link might be private or restricted." | |
| }; | |
| } catch (err) { | |
| console.error(`[quickAddVideoAction] Exception:`, err); | |
| return { | |
| success: false, | |
| error: "An unexpected error occurred. Please try again with a different link." | |
| }; | |
| } | |
| } | |
| export async function checkCommunityVaultsAction(url: string) { | |
| if (!url) return { success: false, error: "URL is required" }; | |
| const platform = SocialIngestService.detectPlatform(url); | |
| if (!platform) return { success: false, error: "Unsupported platform" }; | |
| let videoId: string | null = null; | |
| if (platform === 'youtube') { | |
| const videoIdMatch = url.match(/(?:youtube\.com\/(?:[^\/]+\/.+\/|(?:v|e(?:mbed)?)\/|.*[?&]v=)|youtu\.be\/)([^"&?\/\s]{11})/); | |
| videoId = videoIdMatch ? videoIdMatch[1] : null; | |
| } else if (platform === 'tiktok') { | |
| const match = url.match(/\/video\/(\d+)/); | |
| const rawId = match ? match[1] : null; | |
| if (rawId) videoId = rawId.startsWith('tt-') ? rawId : `tt-${rawId}`; | |
| } else if (platform === 'instagram') { | |
| const match = url.match(/\/(?:p|reels|reel)\/([A-Za-z0-9_-]+)/); | |
| const rawId = match ? match[1] : null; | |
| if (rawId) videoId = rawId.startsWith('ig-') ? rawId : `ig-${rawId}`; | |
| } else if (platform === 'facebook') { | |
| const match = url.match(/[?&]v=([^&]+)/) || url.match(/\/share\/v\/([^/?]+)/) || url.match(/\/reel\/([^/?]+)/); | |
| const rawId = match ? match[1] : null; | |
| if (rawId) videoId = rawId.startsWith('fb-') ? rawId : `fb-${rawId}`; | |
| } | |
| if (!videoId) return { success: false, error: "Could not parse video ID" }; | |
| try { | |
| const vaults = await ShowcaseService.getCommunityVaults(videoId); | |
| return { success: true, vaults }; | |
| } catch (err) { | |
| console.error(`[checkCommunityVaultsAction] Error:`, err); | |
| return { success: false, error: "Failed to check community vaults" }; | |
| } | |
| } | |
| export async function requestAnalysisNotificationAction(videoId: string, email: string) { | |
| const trimmedEmail = email.toLowerCase().trim(); | |
| const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; | |
| if (!emailRegex.test(trimmedEmail)) { | |
| return { success: false, error: "Invalid email format" }; | |
| } | |
| // Reuse encryption logic pattern | |
| const hash = crypto.createHash('sha256').update(trimmedEmail).digest('hex'); | |
| const encryptionKey = process.env.ENCRYPTION_KEY || ""; | |
| if (!encryptionKey || encryptionKey.length !== 64) { | |
| return { success: false, error: "Server configuration error" }; | |
| } | |
| const iv = crypto.randomBytes(16); | |
| const cipher = crypto.createCipheriv('aes-256-cbc', Buffer.from(encryptionKey, 'hex'), iv); | |
| let encrypted = cipher.update(trimmedEmail, 'utf8', 'hex'); | |
| encrypted += cipher.final('hex'); | |
| const emailEncrypted = iv.toString('hex') + ':' + encrypted; | |
| try { | |
| // Find internal video ID | |
| const video = await db.query.youtubeVideos.findFirst({ | |
| where: eq(youtubeVideos.videoId, videoId) | |
| }); | |
| if (!video) return { success: false, error: "Video not found" }; | |
| await db.insert(analysisNotifications).values({ | |
| videoId: video.id, | |
| emailHash: hash, | |
| emailEncrypted: emailEncrypted, | |
| status: 'PENDING' | |
| }); | |
| return { success: true }; | |
| } catch (err) { | |
| console.error("[requestAnalysisNotificationAction] Error:", err); | |
| return { success: false, error: "Failed to register notification" }; | |
| } | |
| } | |
| import crypto from "crypto"; | |
| import { db } from "@/lib/db"; | |
| import { youtubeVideos, analysisNotifications } from "@/lib/db/schema"; | |
| import { eq } from "drizzle-orm"; | |