Files
Socialflow/server/services/imageProcessor.ts
T
michaelschal f881f668a6 Add Docker support and installation guide for easy server deployment
Includes a Dockerfile for building the application image and an INSTALLATION.md file detailing the setup process, prerequisites, and helpful commands for managing the application on a private server.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: ae4037a0-2a6f-4530-9bac-79b543286bda
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/397bca8c-984f-43ff-841a-10897aeb8140/ae4037a0-2a6f-4530-9bac-79b543286bda/ds3R7vP
2025-10-01 09:52:59 +00:00

103 lines
3.0 KiB
TypeScript

import sharp from "sharp";
import { randomUUID } from "crypto";
import path from "path";
import fs from "fs/promises";
interface CropDimensions {
width: number;
height: number;
}
interface ProcessedImages {
facebookFeed: string;
instagramFeed: string;
instagramStory: string;
}
const CROP_FORMATS = {
facebookFeed: { width: 1200, height: 630 } as CropDimensions,
instagramFeed: { width: 1080, height: 1080 } as CropDimensions,
instagramStory: { width: 1080, height: 1920 } as CropDimensions,
};
export class ImageProcessor {
private uploadsDir: string;
constructor() {
this.uploadsDir = path.join(process.cwd(), "uploads");
this.ensureUploadsDirExists();
}
private async ensureUploadsDirExists() {
try {
await fs.access(this.uploadsDir);
} catch {
await fs.mkdir(this.uploadsDir, { recursive: true });
}
}
async processImage(buffer: Buffer, originalFilename: string): Promise<ProcessedImages> {
const fileExt = path.extname(originalFilename);
const baseId = randomUUID();
const processedImages: ProcessedImages = {
facebookFeed: "",
instagramFeed: "",
instagramStory: "",
};
// Process Facebook Feed (1200x630)
const fbFeedFilename = `${baseId}-fb-feed${fileExt}`;
const fbFeedPath = path.join(this.uploadsDir, fbFeedFilename);
await sharp(buffer)
.resize(CROP_FORMATS.facebookFeed.width, CROP_FORMATS.facebookFeed.height, {
fit: "cover",
position: "center",
})
.toFile(fbFeedPath);
processedImages.facebookFeed = `/uploads/${fbFeedFilename}`;
// Process Instagram Feed (1080x1080)
const igFeedFilename = `${baseId}-ig-feed${fileExt}`;
const igFeedPath = path.join(this.uploadsDir, igFeedFilename);
await sharp(buffer)
.resize(CROP_FORMATS.instagramFeed.width, CROP_FORMATS.instagramFeed.height, {
fit: "cover",
position: "center",
})
.toFile(igFeedPath);
processedImages.instagramFeed = `/uploads/${igFeedFilename}`;
// Process Instagram Story (1080x1920)
const igStoryFilename = `${baseId}-ig-story${fileExt}`;
const igStoryPath = path.join(this.uploadsDir, igStoryFilename);
await sharp(buffer)
.resize(CROP_FORMATS.instagramStory.width, CROP_FORMATS.instagramStory.height, {
fit: "cover",
position: "center",
})
.toFile(igStoryPath);
processedImages.instagramStory = `/uploads/${igStoryFilename}`;
return processedImages;
}
async saveOriginalFile(buffer: Buffer, filename: string): Promise<string> {
const uniqueFilename = `${randomUUID()}-${filename}`;
const filePath = path.join(this.uploadsDir, uniqueFilename);
await fs.writeFile(filePath, buffer);
return `/uploads/${uniqueFilename}`;
}
async deleteFile(filePath: string): Promise<void> {
try {
const fullPath = path.join(process.cwd(), filePath);
await fs.unlink(fullPath);
} catch (error) {
console.error("Error deleting file:", error);
}
}
}
export const imageProcessor = new ImageProcessor();