contentos-v2 / server /index.js
ziggylott's picture
🚀 ContentOS v2.0 — Full omnichannel social media platform with OAuth, LLM integration, and real backend scheduling
de8b213
Raw
History Blame Contribute Delete
9.8 kB
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}`);
});