mirror of
https://github.com/R0m1k3/Socialflow.git
synced 2026-10-12 01:36:54 +02:00
Implement Cloudinary integration for uploading, storing, and transforming media files, including a new settings section for user configuration and API endpoints for media management. 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/p0c5kxJ
290 lines
9.4 KiB
TypeScript
290 lines
9.4 KiB
TypeScript
import type { Express } from "express";
|
|
import { createServer, type Server } from "http";
|
|
import { storage } from "./storage";
|
|
import multer from "multer";
|
|
import { openRouterService } from "./services/openrouter";
|
|
import { cloudinaryService } from "./services/cloudinary";
|
|
import { insertPostSchema, insertScheduledPostSchema, insertSocialPageSchema, insertAiGenerationSchema, insertCloudinaryConfigSchema } from "@shared/schema";
|
|
|
|
const upload = multer({ storage: multer.memoryStorage() });
|
|
|
|
export async function registerRoutes(app: Express): Promise<Server> {
|
|
|
|
// Stats endpoint
|
|
app.get("/api/stats", async (req, res) => {
|
|
try {
|
|
// For now, return mock stats. In a real app, calculate from database
|
|
const userId = "demo-user"; // In real app, get from session/auth
|
|
|
|
const posts = await storage.getPosts(userId);
|
|
const pages = await storage.getSocialPages(userId);
|
|
const media = await storage.getMedia(userId);
|
|
const aiGenerations = await storage.getAiGenerations(userId);
|
|
|
|
const scheduledPosts = posts.filter(p => p.status === "scheduled").length;
|
|
|
|
res.json({
|
|
scheduledPosts,
|
|
connectedPages: pages.length,
|
|
aiTextsGenerated: aiGenerations.length,
|
|
mediaStored: media.length,
|
|
});
|
|
} catch (error) {
|
|
console.error("Error fetching stats:", error);
|
|
res.status(500).json({ error: "Failed to fetch stats" });
|
|
}
|
|
});
|
|
|
|
// AI text generation
|
|
app.post("/api/ai/generate", async (req, res) => {
|
|
try {
|
|
const productInfo = req.body;
|
|
const userId = "demo-user"; // In real app, get from session/auth
|
|
|
|
const generatedTexts = await openRouterService.generatePostText(productInfo);
|
|
|
|
// Save to database
|
|
await storage.createAiGeneration({
|
|
userId,
|
|
productInfo,
|
|
generatedTexts,
|
|
});
|
|
|
|
res.json({ variants: generatedTexts });
|
|
} catch (error) {
|
|
console.error("Error generating text:", error);
|
|
res.status(500).json({ error: "Failed to generate text" });
|
|
}
|
|
});
|
|
|
|
// Media upload
|
|
app.post("/api/media/upload", upload.single("file"), async (req, res) => {
|
|
try {
|
|
if (!req.file) {
|
|
return res.status(400).json({ error: "No file uploaded" });
|
|
}
|
|
|
|
const userId = "demo-user"; // In real app, get from session/auth
|
|
|
|
// Check if Cloudinary is configured
|
|
const cloudinaryConfig = await storage.getCloudinaryConfig(userId);
|
|
|
|
if (!cloudinaryConfig) {
|
|
return res.status(400).json({
|
|
error: "Cloudinary not configured. Please configure Cloudinary in Settings first."
|
|
});
|
|
}
|
|
|
|
// Upload to Cloudinary
|
|
const uploadResult = await cloudinaryService.uploadMedia(
|
|
req.file.buffer,
|
|
req.file.originalname,
|
|
userId,
|
|
req.file.mimetype
|
|
);
|
|
|
|
const mediaItem = await storage.createMedia({
|
|
userId,
|
|
type: req.file.mimetype.startsWith("video/") ? "video" : "image",
|
|
cloudinaryPublicId: uploadResult.publicId,
|
|
originalUrl: uploadResult.originalUrl,
|
|
facebookFeedUrl: uploadResult.facebookFeedUrl,
|
|
instagramFeedUrl: uploadResult.instagramFeedUrl,
|
|
instagramStoryUrl: uploadResult.instagramStoryUrl,
|
|
fileName: req.file.originalname,
|
|
fileSize: req.file.size,
|
|
});
|
|
|
|
res.json(mediaItem);
|
|
} catch (error) {
|
|
console.error("Error uploading media:", error);
|
|
const errorMessage = error instanceof Error ? error.message : "Failed to upload media";
|
|
res.status(500).json({ error: errorMessage });
|
|
}
|
|
});
|
|
|
|
// Get media
|
|
app.get("/api/media", async (req, res) => {
|
|
try {
|
|
const userId = "demo-user"; // In real app, get from session/auth
|
|
const media = await storage.getMedia(userId);
|
|
res.json(media);
|
|
} catch (error) {
|
|
console.error("Error fetching media:", error);
|
|
res.status(500).json({ error: "Failed to fetch media" });
|
|
}
|
|
});
|
|
|
|
// Delete media
|
|
app.delete("/api/media/:id", async (req, res) => {
|
|
try {
|
|
const userId = "demo-user"; // In real app, get from session/auth
|
|
const mediaId = req.params.id;
|
|
|
|
// Get media to find cloudinary public ID
|
|
const allMedia = await storage.getMedia(userId);
|
|
const mediaToDelete = allMedia.find(m => m.id === mediaId);
|
|
|
|
if (!mediaToDelete) {
|
|
return res.status(404).json({ error: "Media not found" });
|
|
}
|
|
|
|
// Delete from Cloudinary
|
|
if (mediaToDelete.cloudinaryPublicId) {
|
|
await cloudinaryService.deleteMedia(
|
|
mediaToDelete.cloudinaryPublicId,
|
|
userId,
|
|
mediaToDelete.type
|
|
);
|
|
}
|
|
|
|
// Delete from database
|
|
await storage.deleteMedia(mediaId);
|
|
|
|
res.json({ success: true });
|
|
} catch (error) {
|
|
console.error("Error deleting media:", error);
|
|
res.status(500).json({ error: "Failed to delete media" });
|
|
}
|
|
});
|
|
|
|
// Posts
|
|
app.get("/api/posts", async (req, res) => {
|
|
try {
|
|
const userId = "demo-user"; // In real app, get from session/auth
|
|
const posts = await storage.getPosts(userId);
|
|
res.json(posts);
|
|
} catch (error) {
|
|
console.error("Error fetching posts:", error);
|
|
res.status(500).json({ error: "Failed to fetch posts" });
|
|
}
|
|
});
|
|
|
|
app.post("/api/posts", async (req, res) => {
|
|
try {
|
|
const userId = "demo-user"; // In real app, get from session/auth
|
|
const postData = insertPostSchema.parse({ ...req.body, userId });
|
|
const post = await storage.createPost(postData);
|
|
res.json(post);
|
|
} catch (error) {
|
|
console.error("Error creating post:", error);
|
|
res.status(500).json({ error: "Failed to create post" });
|
|
}
|
|
});
|
|
|
|
// Scheduled posts
|
|
app.get("/api/scheduled-posts", async (req, res) => {
|
|
try {
|
|
const userId = "demo-user"; // In real app, get from session/auth
|
|
const { startDate, endDate } = req.query;
|
|
|
|
const start = startDate ? new Date(startDate as string) : undefined;
|
|
const end = endDate ? new Date(endDate as string) : undefined;
|
|
|
|
const scheduledPosts = await storage.getScheduledPosts(userId, start, end);
|
|
res.json(scheduledPosts);
|
|
} catch (error) {
|
|
console.error("Error fetching scheduled posts:", error);
|
|
res.status(500).json({ error: "Failed to fetch scheduled posts" });
|
|
}
|
|
});
|
|
|
|
app.post("/api/scheduled-posts", async (req, res) => {
|
|
try {
|
|
const scheduledPostData = insertScheduledPostSchema.parse(req.body);
|
|
const scheduledPost = await storage.createScheduledPost(scheduledPostData);
|
|
res.json(scheduledPost);
|
|
} catch (error) {
|
|
console.error("Error creating scheduled post:", error);
|
|
res.status(500).json({ error: "Failed to create scheduled post" });
|
|
}
|
|
});
|
|
|
|
// Social pages
|
|
app.get("/api/pages", async (req, res) => {
|
|
try {
|
|
const userId = "demo-user"; // In real app, get from session/auth
|
|
const pages = await storage.getSocialPages(userId);
|
|
res.json(pages);
|
|
} catch (error) {
|
|
console.error("Error fetching pages:", error);
|
|
res.status(500).json({ error: "Failed to fetch pages" });
|
|
}
|
|
});
|
|
|
|
app.post("/api/pages", async (req, res) => {
|
|
try {
|
|
const userId = "demo-user"; // In real app, get from session/auth
|
|
const pageData = insertSocialPageSchema.parse({ ...req.body, userId });
|
|
const page = await storage.createSocialPage(pageData);
|
|
res.json(page);
|
|
} catch (error) {
|
|
console.error("Error creating page:", error);
|
|
res.status(500).json({ error: "Failed to create page" });
|
|
}
|
|
});
|
|
|
|
// AI Generations
|
|
app.get("/api/ai/generations", async (req, res) => {
|
|
try {
|
|
const userId = "demo-user"; // In real app, get from session/auth
|
|
const generations = await storage.getAiGenerations(userId);
|
|
res.json(generations);
|
|
} catch (error) {
|
|
console.error("Error fetching AI generations:", error);
|
|
res.status(500).json({ error: "Failed to fetch AI generations" });
|
|
}
|
|
});
|
|
|
|
// Cloudinary Config
|
|
app.get("/api/cloudinary/config", async (req, res) => {
|
|
try {
|
|
const userId = "demo-user"; // In real app, get from session/auth
|
|
const config = await storage.getCloudinaryConfig(userId);
|
|
|
|
if (!config) {
|
|
return res.json(null);
|
|
}
|
|
|
|
// Don't send the API secret to the client
|
|
const { apiSecret, ...safeConfig } = config;
|
|
res.json(safeConfig);
|
|
} catch (error) {
|
|
console.error("Error fetching Cloudinary config:", error);
|
|
res.status(500).json({ error: "Failed to fetch Cloudinary config" });
|
|
}
|
|
});
|
|
|
|
app.post("/api/cloudinary/config", async (req, res) => {
|
|
try {
|
|
const userId = "demo-user"; // In real app, get from session/auth
|
|
|
|
const configData = insertCloudinaryConfigSchema.parse({
|
|
...req.body,
|
|
userId,
|
|
});
|
|
|
|
// Check if config already exists
|
|
const existingConfig = await storage.getCloudinaryConfig(userId);
|
|
|
|
let config;
|
|
if (existingConfig) {
|
|
config = await storage.updateCloudinaryConfig(userId, configData);
|
|
} else {
|
|
config = await storage.createCloudinaryConfig(configData);
|
|
}
|
|
|
|
// Don't send the API secret back
|
|
const { apiSecret, ...safeConfig } = config;
|
|
res.json(safeConfig);
|
|
} catch (error) {
|
|
console.error("Error saving Cloudinary config:", error);
|
|
res.status(500).json({ error: "Failed to save Cloudinary config" });
|
|
}
|
|
});
|
|
|
|
const httpServer = createServer(app);
|
|
|
|
return httpServer;
|
|
}
|