-
Notifications
You must be signed in to change notification settings - Fork 44
Expand file tree
/
Copy pathserver.js
More file actions
129 lines (106 loc) · 3.32 KB
/
server.js
File metadata and controls
129 lines (106 loc) · 3.32 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import express from 'express';
import cors from 'cors';
import path from 'path';
import { fileURLToPath } from 'url';
import Sora2 from './sora2.js';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const app = express();
const PORT = process.env.PORT || 3000;
// Middleware
app.use(cors());
app.use(express.json());
app.use(express.static('public'));
// Initialize Sora2 client
const sora = new Sora2();
// API Routes
app.post('/api/chat', async (req, res) => {
try {
const { messages, options } = req.body;
if (!messages || !Array.isArray(messages)) {
return res.status(400).json({ error: 'Invalid messages format' });
}
const response = await sora.chat(messages, options);
res.json(response);
} catch (error) {
console.error('Chat error:', error);
res.status(500).json({ error: error.message });
}
});
// Streaming chat endpoint
app.post('/api/chat/stream', async (req, res) => {
try {
const { messages, options } = req.body;
if (!messages || !Array.isArray(messages)) {
return res.status(400).json({ error: 'Invalid messages format' });
}
// Set headers for SSE
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
// Forward the stream from Sora API
await sora.chatStream(messages, options, (chunk) => {
res.write(chunk);
});
res.end();
} catch (error) {
console.error('Chat stream error:', error);
res.write(`data: ${JSON.stringify({ error: error.message })}\n\n`);
res.end();
}
});
app.post('/api/completion', async (req, res) => {
try {
const { prompt, options } = req.body;
if (!prompt) {
return res.status(400).json({ error: 'Prompt is required' });
}
const response = await sora.createCompletion(prompt, options);
res.json(response);
} catch (error) {
console.error('Completion error:', error);
res.status(500).json({ error: error.message });
}
});
// Video generation
app.post('/api/video/generate', async (req, res) => {
try {
const { prompt, options } = req.body;
if (!prompt) {
return res.status(400).json({ error: 'Prompt is required' });
}
const response = await sora.generateVideo(prompt, options);
res.json(response);
} catch (error) {
console.error('Video generation error:', error);
res.status(500).json({ error: error.message });
}
});
// Get video task status
app.get('/api/video/tasks/:taskId', async (req, res) => {
try {
const { taskId } = req.params;
const response = await sora.getVideoTask(taskId);
res.json(response);
} catch (error) {
console.error('Task query error:', error);
res.status(500).json({ error: error.message });
}
});
// Health check
app.get('/api/health', (req, res) => {
res.json({ status: 'ok', timestamp: new Date().toISOString() });
});
// Root route - serve index.html
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'index.html'));
});
// Start server (only for local development)
if (process.env.NODE_ENV !== 'production') {
app.listen(PORT, () => {
console.log(`🚀 Sora-2 MVP Server running at http://localhost:${PORT}`);
console.log(`📱 Open your browser to start chatting!`);
});
}
// Export for Vercel
export default app;