UserSyncInterface / server.cjs
AUXteam's picture
Upload folder using huggingface_hub
ef213b3 verified
const express = require('express');
const path = require('path');
const cookieParser = require('cookie-parser');
const crypto = require('crypto');
const fs = require('fs');
const app = express();
const port = 7860;
app.use(express.json({ limit: '50mb' }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'dist')));
const OAUTH_CLIENT_ID = process.env.OAUTH_CLIENT_ID;
const OAUTH_CLIENT_SECRET = process.env.OAUTH_CLIENT_SECRET;
const OAUTH_SCOPES = process.env.OAUTH_SCOPES || "openid profile";
const OPENID_PROVIDER_URL = process.env.OPENID_PROVIDER_URL || "https://huggingface.co";
const SPACE_HOST = process.env.SPACE_HOST;
const REDIRECT_URI = SPACE_HOST
? `https://${SPACE_HOST}/oauth/callback`
: `http://localhost:${port}/oauth/callback`;
app.post('/api/craft', async (req, res) => {
const { content, variation } = req.body;
const apiKey = process.env.BLABLADOR_API_KEY;
if (!apiKey) {
return res.status(500).json({ error: 'BLABLADOR_API_KEY is not configured on the server.' });
}
const model = content.length > 500 ? 'alias-large' : 'alias-fast';
const prompt = `You are a professional content creator. Help me craft a ${variation || 'social media post'} based on the following content:\n\n${content}\n\nProvide 3 distinct and engaging variations.`;
try {
const response = await fetch('https://api.helmholtz-blablador.fz-juelich.de/v1/chat/completions', {
method: 'POST',
headers: {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: model,
messages: [
{ role: 'system', content: 'You are a helpful assistant that helps craft engaging marketing and social media content.' },
{ role: 'user', content: prompt }
],
temperature: 0.7
})
});
if (!response.ok) {
const errorData = await response.json();
return res.status(response.status).json(errorData);
}
const data = await response.json();
res.json({ result: data.choices[0].message.content });
} catch (error) {
console.error('Blablador API error:', error);
res.status(500).json({ error: 'Failed to connect to Blablador API.' });
}
});
app.get('/api/config', (req, res) => {
res.json({
clientId: OAUTH_CLIENT_ID,
scopes: OAUTH_SCOPES,
});
});
app.get('/login', (req, res) => {
if (!OAUTH_CLIENT_ID) {
return res.status(500).json({ error: "OAuth is not configured (missing OAUTH_CLIENT_ID)" });
}
const state = crypto.randomBytes(16).toString('hex');
res.cookie('oauth_state', state, { httpOnly: true, maxAge: 600000 }); // 10 mins
const authUrl = `${OPENID_PROVIDER_URL}/oauth/authorize` +
`?client_id=${OAUTH_CLIENT_ID}` +
`&redirect_uri=${encodeURIComponent(REDIRECT_URI)}` +
`&scope=${encodeURIComponent(OAUTH_SCOPES)}` +
`&response_type=code` +
`&state=${state}`;
res.redirect(authUrl);
});
app.get('/oauth/callback', async (req, res) => {
const { code, state } = req.query;
const savedState = req.cookies.oauth_state;
if (!state || state !== savedState) {
console.error("State mismatch:", { state, savedState });
return res.status(403).json({ error: "Invalid OAuth state" });
}
res.clearCookie('oauth_state');
const tokenUrl = `${OPENID_PROVIDER_URL}/oauth/token`;
const authStr = Buffer.from(`${OAUTH_CLIENT_ID}:${OAUTH_CLIENT_SECRET}`).toString('base64');
try {
const tokenResp = await fetch(tokenUrl, {
method: 'POST',
headers: {
'Authorization': `Basic ${authStr}`,
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
grant_type: 'authorization_code',
code: code,
redirect_uri: REDIRECT_URI,
client_id: OAUTH_CLIENT_ID,
})
});
if (!tokenResp.ok) {
const errorText = await tokenResp.text();
console.error("Token exchange failed:", errorText);
return res.status(400).json({ error: "Failed to retrieve access token" });
}
const tokenData = await tokenResp.json();
const accessToken = tokenData.access_token;
const userResp = await fetch("https://huggingface.co/api/whoami-v2", {
headers: { "Authorization": `Bearer ${accessToken}` }
});
const userInfo = await userResp.json();
// Set cookie and redirect back to app
res.cookie('hf_user', JSON.stringify(userInfo), { path: '/', httpOnly: false });
res.redirect('/');
} catch (error) {
console.error("OAuth callback error:", error);
res.status(500).json({ error: "Authentication failed" });
}
});
app.get('/api/user', (req, res) => {
if (req.cookies.hf_user) {
try {
res.json(JSON.parse(req.cookies.hf_user));
} catch (e) {
res.status(400).json({ error: "Invalid user cookie" });
}
} else {
res.status(401).json({ error: "Not authenticated" });
}
});
app.get('/api/logout', (req, res) => {
res.clearCookie('hf_user');
res.redirect('/');
});
app.post('/api/save-data', (req, res) => {
let { type, data, user } = req.body;
// Sanitize inputs to prevent path traversal
user = String(user).replace(/[^a-z0-9]/gi, '_');
type = String(type).replace(/[^a-z0-9]/gi, '_');
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const filename = `${user}_${type}_${timestamp}.json`;
const dirPath = path.join(__dirname, 'data');
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true });
}
const filePath = path.join(dirPath, filename);
try {
fs.writeFileSync(filePath, JSON.stringify({ user, type, timestamp, data }, null, 2));
console.log(`Saved data to ${filePath}`);
res.json({ success: true, message: `Data saved as ${filename}` });
} catch (error) {
console.error('Failed to save data:', error);
res.status(500).json({ error: 'Failed to save data' });
}
});
app.get('/api/list-data', (req, res) => {
let { type, user } = req.query;
const dirPath = path.join(__dirname, 'data');
// Sanitize inputs
if (user) user = String(user).replace(/[^a-z0-9]/gi, '_');
if (type) type = String(type).replace(/[^a-z0-9]/gi, '_');
if (!fs.existsSync(dirPath)) {
return res.json([]);
}
try {
const files = fs.readdirSync(dirPath);
const results = files
.filter(f => f.endsWith('.json'))
.map(f => {
try {
const content = fs.readFileSync(path.join(dirPath, f), 'utf8');
return JSON.parse(content);
} catch (e) {
return null;
}
})
.filter(d => d && (!type || d.type === type) && (!user || d.user === user));
res.json(results);
} catch (error) {
console.error('Failed to list data:', error);
res.status(500).json({ error: 'Failed to list data' });
}
});
app.get('/health', (req, res) => {
res.status(200).send('OK');
});
app.use((req, res) => {
res.sendFile(path.join(__dirname, 'dist', 'index.html'));
});
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});