Spaces:
Sleeping
Sleeping
| 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}`); | |
| }); | |