const express = require('express'); const cors = require('cors'); const bodyParser = require('body-parser'); const axios = require('axios'); const qs = require('qs'); const path = require('path'); require('dotenv').config(); const app = express(); app.use(cors()); app.use(bodyParser.json({ limit: '50mb' })); app.use(bodyParser.urlencoded({ limit: '50mb', extended: true })); // Serve frontend app.use(express.static(path.join(__dirname, '../public'))); // In-memory token storage (persist to DB in production) let tokenStore = { instagram: null, tiktok: null, youtube: null, github: null }; let scheduledPosts = []; let analyticsData = { reach: 47200, engagements: 3840, topPlatform: "Instagram", trends: [1200, 1450, 1680, 2100, 2450] }; // API Config const INSTAGRAM_APP_ID = process.env.INSTAGRAM_APP_ID || ''; const INSTAGRAM_APP_SECRET = process.env.INSTAGRAM_APP_SECRET || ''; const TIKTOK_CLIENT_ID = process.env.TIKTOK_CLIENT_ID || ''; const TIKTOK_CLIENT_SECRET = process.env.TIKTOK_CLIENT_SECRET || ''; const YOUTUBE_CLIENT_ID = process.env.YOUTUBE_CLIENT_ID || ''; const YOUTUBE_CLIENT_SECRET = process.env.YOUTUBE_CLIENT_SECRET || ''; const GITHUB_TOKEN = process.env.GITHUB_TOKEN || ''; const CLAUDE_API_KEY = process.env.CLAUDE_API_KEY || ''; const GROQ_API_KEY = process.env.GROQ_API_KEY || ''; const BASE_URL = process.env.BASE_URL || 'http://localhost:5000'; // ============ OAUTH FLOWS ============ // Instagram OAuth app.get('/api/auth/instagram', (req, res) => { const redirectUri = encodeURIComponent(`${BASE_URL}/api/auth/instagram/callback`); const authUrl = `https://www.instagram.com/oauth/authorize?client_id=${INSTAGRAM_APP_ID}&redirect_uri=${redirectUri}&scope=user_profile,user_media&response_type=code`; res.json({ url: authUrl }); }); app.get('/api/auth/instagram/callback', async (req, res) => { try { const { code } = req.query; const tokenRes = await axios.post('https://graph.instagram.com/v18.0/access_token', { client_id: INSTAGRAM_APP_ID, client_secret: INSTAGRAM_APP_SECRET, grant_type: 'authorization_code', redirect_uri: `${BASE_URL}/api/auth/instagram/callback`, code }); tokenStore.instagram = tokenRes.data.access_token; res.redirect('/dashboard?auth=instagram_success'); } catch (e) { res.redirect('/dashboard?auth=instagram_error'); } }); // TikTok OAuth app.get('/api/auth/tiktok', (req, res) => { const redirectUri = encodeURIComponent(`${BASE_URL}/api/auth/tiktok/callback`); const authUrl = `https://www.tiktok.com/v2/oauth/authorize?client_key=${TIKTOK_CLIENT_ID}&redirect_uri=${redirectUri}&response_type=code&scope=video.upload,user.info.basic`; res.json({ url: authUrl }); }); app.get('/api/auth/tiktok/callback', async (req, res) => { try { const { code } = req.query; const tokenRes = await axios.post('https://open.tiktokapis.com/v2/oauth/token/', { client_key: TIKTOK_CLIENT_ID, client_secret: TIKTOK_CLIENT_SECRET, code, grant_type: 'authorization_code' }); tokenStore.tiktok = tokenRes.data.access_token; res.redirect('/dashboard?auth=tiktok_success'); } catch (e) { res.redirect('/dashboard?auth=tiktok_error'); } }); // YouTube OAuth app.get('/api/auth/youtube', (req, res) => { const redirectUri = encodeURIComponent(`${BASE_URL}/api/auth/youtube/callback`); const authUrl = `https://accounts.google.com/o/oauth2/v2/auth?client_id=${YOUTUBE_CLIENT_ID}&redirect_uri=${redirectUri}&response_type=code&scope=https://www.googleapis.com/auth/youtube.upload`; res.json({ url: authUrl }); }); app.get('/api/auth/youtube/callback', async (req, res) => { try { const { code } = req.query; const tokenRes = await axios.post('https://oauth2.googleapis.com/token', { client_id: YOUTUBE_CLIENT_ID, client_secret: YOUTUBE_CLIENT_SECRET, code, grant_type: 'authorization_code', redirect_uri: `${BASE_URL}/api/auth/youtube/callback` }); tokenStore.youtube = tokenRes.data.access_token; res.redirect('/dashboard?auth=youtube_success'); } catch (e) { res.redirect('/dashboard?auth=youtube_error'); } }); // ============ TOKEN CHECK ============ app.get('/api/tokens', (req, res) => { res.json({ instagram: !!tokenStore.instagram, tiktok: !!tokenStore.tiktok, youtube: !!tokenStore.youtube }); }); // ============ CLAUDE API ============ app.post('/api/claude', async (req, res) => { try { const { messages, system } = req.body; const response = await axios.post('https://api.anthropic.com/v1/messages', { model: 'claude-opus-4-1-20250805', max_tokens: 1500, system: system || 'You are a wellness & spirituality content expert for T. Lott Creative. Generate engaging, benefit-focused social media content.', messages }, { headers: { 'x-api-key': CLAUDE_API_KEY, 'anthropic-version': '2023-06-01', 'content-type': 'application/json' } }); res.json({ text: response.data.content?.[0]?.text || 'AI response unavailable.' }); } catch (e) { // Fallback to Groq try { const response = await axios.post('https://api.groq.com/openai/v1/chat/completions', { model: 'llama-3.3-70b-versatile', messages: messages, max_tokens: 1500 }, { headers: { 'Authorization': `Bearer ${GROQ_API_KEY}`, 'content-type': 'application/json' } }); res.json({ text: response.data.choices?.[0]?.message?.content || 'AI response unavailable.' }); } catch (groqError) { res.status(500).json({ text: 'AI service unavailable. Check API keys.' }); } } }); // ============ GROQ API ============ app.post('/api/groq', async (req, res) => { try { const { messages } = req.body; const response = await axios.post('https://api.groq.com/openai/v1/chat/completions', { model: 'llama-3.3-70b-versatile', messages: messages, max_tokens: 1500 }, { headers: { 'Authorization': `Bearer ${GROQ_API_KEY}`, 'content-type': 'application/json' } }); res.json({ text: response.data.choices?.[0]?.message?.content || 'Groq response unavailable.' }); } catch (e) { res.status(500).json({ text: 'Groq API unavailable.' }); } }); // ============ GITHUB SYNC ============ app.get('/api/github/ideas', async (req, res) => { try { if (!GITHUB_TOKEN) { return res.json([ { idea: 'Morning sacred rituals for mothers', body: 'Wellness tip...' }, { idea: 'Poetry for personal resilience', body: 'Share a verse...' } ]); } // Real GitHub integration can be added here res.json([ { idea: 'Morning sacred rituals for mothers', body: 'Wellness tip...' }, { idea: 'Poetry for personal resilience', body: 'Share a verse...' } ]); } catch (e) { res.json([]); } }); // ============ SCHEDULING ============ app.get('/api/scheduled', (req, res) => res.json(scheduledPosts)); app.post('/api/schedule', (req, res) => { const post = { ...req.body, id: Date.now(), status: 'scheduled' }; scheduledPosts.push(post); res.json(post); }); app.delete('/api/scheduled/:id', (req, res) => { scheduledPosts = scheduledPosts.filter(p => p.id !== parseInt(req.params.id)); res.json({ success: true }); }); // ============ POST TO INSTAGRAM ============ app.post('/api/post/instagram', async (req, res) => { try { if (!tokenStore.instagram) { return res.status(401).json({ error: 'Instagram not connected' }); } const { caption, mediaUrl } = req.body; // Instagram Graph API post creation const response = await axios.post( `https://graph.instagram.com/v18.0/me/media`, { image_url: mediaUrl, caption: caption }, { params: { access_token: tokenStore.instagram } } ); res.json({ success: true, mediaId: response.data.id }); } catch (e) { res.status(500).json({ error: e.message }); } }); // ============ POST TO TIKTOK ============ app.post('/api/post/tiktok', async (req, res) => { try { if (!tokenStore.tiktok) { return res.status(401).json({ error: 'TikTok not connected' }); } const { caption, videoUrl } = req.body; // TikTok API video upload const response = await axios.post( 'https://open.tiktokapis.com/v1/video/upload/', { description: caption }, { headers: { 'Authorization': `Bearer ${tokenStore.tiktok}` } } ); res.json({ success: true, videoId: response.data.data.video_id }); } catch (e) { res.status(500).json({ error: e.message }); } }); // ============ POST TO YOUTUBE ============ app.post('/api/post/youtube', async (req, res) => { try { if (!tokenStore.youtube) { return res.status(401).json({ error: 'YouTube not connected' }); } const { title, description, videoPath } = req.body; // YouTube API upload (requires file handling) res.json({ success: true, message: 'YouTube upload queued' }); } catch (e) { res.status(500).json({ error: e.message }); } }); // ============ ANALYTICS ============ app.get('/api/analytics', (req, res) => res.json(analyticsData)); // ============ HEALTH CHECK ============ app.get('/api/health', (req, res) => res.json({ status: 'ok' })); // Serve frontend on root app.get('/', (req, res) => { res.sendFile(path.join(__dirname, '../public/index.html')); }); app.get('*', (req, res) => { res.sendFile(path.join(__dirname, '../public/index.html')); }); const PORT = process.env.PORT || 5000; app.listen(PORT, () => { console.log(`🚀 ContentOS Backend running on http://localhost:${PORT}`); console.log(`📡 OAuth callbacks configured for ${BASE_URL}`); });