Improve video access security by adding signed URL support from Bunny.net
Implements signed URLs for secure video and thumbnail delivery using the BUNNY_SECURITY_KEY environment variable in bunny.ts. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 50814a1e-92e4-4968-856f-7bc7eedf5e8f Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/8cc42625-c1f5-4e43-99bd-77f2c4dedee2/50814a1e-92e4-4968-856f-7bc7eedf5e8f/3tWpY1N
This commit is contained in:
parent
16cf7dd5a8
commit
f286638b19
@ -1,4 +1,5 @@
|
||||
import { type Video, type InsertVideo } from "@shared/schema";
|
||||
import crypto from 'crypto';
|
||||
|
||||
interface BunnyVideo {
|
||||
guid: string;
|
||||
@ -22,11 +23,13 @@ export class BunnyService {
|
||||
private apiKey: string;
|
||||
private libraryId: string;
|
||||
private hostname: string;
|
||||
private securityKey: string;
|
||||
|
||||
constructor() {
|
||||
this.apiKey = process.env.BUNNY_API_KEY!;
|
||||
this.libraryId = process.env.BUNNY_LIBRARY_ID!;
|
||||
this.hostname = process.env.BUNNY_HOSTNAME!;
|
||||
this.securityKey = process.env.BUNNY_SECURITY_KEY || ''; // CDN security key for signing URLs
|
||||
|
||||
if (!this.apiKey || !this.libraryId || !this.hostname) {
|
||||
throw new Error("Missing Bunny.net configuration");
|
||||
@ -51,18 +54,32 @@ export class BunnyService {
|
||||
return response.json();
|
||||
}
|
||||
|
||||
private bunnyVideoToVideo(bunnyVideo: BunnyVideo): Video {
|
||||
// Use proxy endpoint for thumbnails to handle private access
|
||||
const thumbnailUrl = `/thumbnail/${bunnyVideo.guid}`;
|
||||
private generateSignedUrl(path: string, expiryHours: number = 1): string {
|
||||
if (!this.securityKey) {
|
||||
// If no security key, return iframe embed as fallback
|
||||
const videoId = path.split('/')[1];
|
||||
return `https://iframe.mediadelivery.net/embed/${this.libraryId}/${videoId}?controls=true&autoplay=false`;
|
||||
}
|
||||
|
||||
// Try direct CDN URL first - some videos might be accessible
|
||||
const directUrl = `https://${this.hostname}/${bunnyVideo.guid}/playlist.m3u8`;
|
||||
const expireTimestamp = Math.floor(Date.now() / 1000) + (expiryHours * 3600);
|
||||
const tokenContent = `${this.securityKey}${path}${expireTimestamp}`;
|
||||
|
||||
// Fallback iframe embed for private videos
|
||||
const iframeUrl = `https://iframe.mediadelivery.net/embed/${this.libraryId}/${bunnyVideo.guid}?controls=true&autoplay=false`;
|
||||
const hash = crypto.createHash('md5').update(tokenContent).digest();
|
||||
const token = Buffer.from(hash).toString('base64')
|
||||
.replace(/\+/g, '-')
|
||||
.replace(/\//g, '_')
|
||||
.replace(/=/g, '');
|
||||
|
||||
return `https://${this.hostname}${path}?token=${token}&expires=${expireTimestamp}`;
|
||||
}
|
||||
|
||||
private bunnyVideoToVideo(bunnyVideo: BunnyVideo): Video {
|
||||
// Generate signed URLs for private video access
|
||||
const videoPath = `/${bunnyVideo.guid}/playlist.m3u8`;
|
||||
const thumbnailPath = `/${bunnyVideo.guid}/${bunnyVideo.thumbnailFileName || 'thumbnail.jpg'}`;
|
||||
|
||||
// Use direct URL for HLS streaming
|
||||
const videoUrl = directUrl;
|
||||
const videoUrl = this.generateSignedUrl(videoPath);
|
||||
const thumbnailUrl = this.generateSignedUrl(thumbnailPath);
|
||||
|
||||
return {
|
||||
id: bunnyVideo.guid,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user