Spaces:
Running
Running
Update app.js
Browse files
app.js
CHANGED
|
@@ -56,7 +56,7 @@ async function verifyThrustToken(token) {
|
|
| 56 |
}
|
| 57 |
}
|
| 58 |
|
| 59 |
-
// ---
|
| 60 |
app.get('/api/projects', async (req, res) => {
|
| 61 |
const authHeader = req.headers.authorization;
|
| 62 |
if (!authHeader?.startsWith('Bearer ')) return res.status(401).json({ error: 'Unauthorized' });
|
|
@@ -89,6 +89,72 @@ app.get('/api/projects', async (req, res) => {
|
|
| 89 |
});
|
| 90 |
});
|
| 91 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 92 |
// WS Upgrade
|
| 93 |
server.on('upgrade', async (request, socket, head) => {
|
| 94 |
const url = new URL(request.url, `http://${request.headers.host}`);
|
|
@@ -101,17 +167,12 @@ server.on('upgrade', async (request, socket, head) => {
|
|
| 101 |
if (!decodedData) { socket.write('HTTP/1.1 403 Forbidden\r\n\r\n'); socket.destroy(); return; }
|
| 102 |
|
| 103 |
wss.handleUpgrade(request, socket, head, (ws) => {
|
| 104 |
-
wss.emit('connection', ws, request, decodedData.uid);
|
| 105 |
});
|
| 106 |
});
|
| 107 |
|
| 108 |
// WS Logic
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
wss.on('connection', (ws, req, user) => {
|
| 113 |
-
const userId = user.uid;
|
| 114 |
-
|
| 115 |
if (!clients.has(userId)) clients.set(userId, new Set());
|
| 116 |
clients.get(userId).add(ws);
|
| 117 |
|
|
@@ -122,7 +183,7 @@ wss.on('connection', (ws, req, user) => {
|
|
| 122 |
try {
|
| 123 |
const data = JSON.parse(message.toString());
|
| 124 |
|
| 125 |
-
// 1. CHAT PROMPTS
|
| 126 |
if (data.type === 'prompt') {
|
| 127 |
ws.send(JSON.stringify({ type: 'status', status: 'thinking' }));
|
| 128 |
const response = await fetch(`${CORE_URL}/process`, {
|
|
@@ -163,8 +224,6 @@ wss.on('connection', (ws, req, user) => {
|
|
| 163 |
});
|
| 164 |
});
|
| 165 |
|
| 166 |
-
|
| 167 |
-
|
| 168 |
setInterval(() => {
|
| 169 |
wss.clients.forEach((ws) => {
|
| 170 |
if (ws.isAlive === false) return ws.terminate();
|
|
@@ -173,5 +232,4 @@ setInterval(() => {
|
|
| 173 |
});
|
| 174 |
}, 30000);
|
| 175 |
|
| 176 |
-
server.listen(PORT, () => console.log(`🚀 Gateway on ${PORT}`));
|
| 177 |
-
|
|
|
|
| 56 |
}
|
| 57 |
}
|
| 58 |
|
| 59 |
+
// --- FETCH PAGINATED PROJECTS VIA JWT ---
|
| 60 |
app.get('/api/projects', async (req, res) => {
|
| 61 |
const authHeader = req.headers.authorization;
|
| 62 |
if (!authHeader?.startsWith('Bearer ')) return res.status(401).json({ error: 'Unauthorized' });
|
|
|
|
| 89 |
});
|
| 90 |
});
|
| 91 |
|
| 92 |
+
// --- NEW: FETCH ACTIVE THRUST & TASKS ---
|
| 93 |
+
app.get('/api/projects/:projectId/thrusts/active', async (req, res) => {
|
| 94 |
+
const authHeader = req.headers.authorization;
|
| 95 |
+
if (!authHeader?.startsWith('Bearer ')) return res.status(401).json({ error: 'Unauthorized' });
|
| 96 |
+
|
| 97 |
+
const token = authHeader.split(' ')[1];
|
| 98 |
+
const decoded = await verifyThrustToken(token);
|
| 99 |
+
if (!decoded || !decoded.uid) return res.status(403).json({ error: 'Invalid Token' });
|
| 100 |
+
|
| 101 |
+
const { projectId } = req.params;
|
| 102 |
+
|
| 103 |
+
// Fetch the thrust AND join its associated tasks
|
| 104 |
+
const { data, error } = await supabase
|
| 105 |
+
.from('thrusts')
|
| 106 |
+
.select('*, thrust_tasks(*)')
|
| 107 |
+
.eq('lead_id', projectId)
|
| 108 |
+
.eq('status', 'active')
|
| 109 |
+
.order('created_at', { ascending: false })
|
| 110 |
+
.limit(1);
|
| 111 |
+
|
| 112 |
+
if (error) return res.status(500).json({ error: error.message });
|
| 113 |
+
|
| 114 |
+
// Order tasks by creation ID/time if needed, but Supabase usually returns them in insert order.
|
| 115 |
+
res.json(data);
|
| 116 |
+
});
|
| 117 |
+
|
| 118 |
+
// --- NEW: MARK TASK COMPLETE & LOG TO TIMELINE ---
|
| 119 |
+
app.post('/api/projects/:projectId/tasks/:taskId/complete', async (req, res) => {
|
| 120 |
+
const authHeader = req.headers.authorization;
|
| 121 |
+
if (!authHeader?.startsWith('Bearer ')) return res.status(401).json({ error: 'Unauthorized' });
|
| 122 |
+
|
| 123 |
+
const token = authHeader.split(' ')[1];
|
| 124 |
+
const decoded = await verifyThrustToken(token);
|
| 125 |
+
if (!decoded || !decoded.uid) return res.status(403).json({ error: 'Invalid Token' });
|
| 126 |
+
|
| 127 |
+
const { projectId, taskId } = req.params;
|
| 128 |
+
const { taskTitle } = req.body;
|
| 129 |
+
|
| 130 |
+
try {
|
| 131 |
+
// 1. Update Task Status
|
| 132 |
+
const { error: updateError } = await supabase
|
| 133 |
+
.from('thrust_tasks')
|
| 134 |
+
.update({ is_completed: true })
|
| 135 |
+
.eq('id', taskId);
|
| 136 |
+
|
| 137 |
+
if (updateError) throw updateError;
|
| 138 |
+
|
| 139 |
+
// 2. Add to Timeline so AI sees it on next context sync
|
| 140 |
+
const { error: timelineError } = await supabase
|
| 141 |
+
.from('timeline_events')
|
| 142 |
+
.insert({
|
| 143 |
+
lead_id: projectId,
|
| 144 |
+
title: "Task Completed",
|
| 145 |
+
description: `User manually completed: ${taskTitle || 'a task'}`,
|
| 146 |
+
type: "chore"
|
| 147 |
+
});
|
| 148 |
+
|
| 149 |
+
if (timelineError) throw timelineError;
|
| 150 |
+
|
| 151 |
+
res.json({ success: true });
|
| 152 |
+
} catch (error) {
|
| 153 |
+
console.error("Task Completion Error:", error.message);
|
| 154 |
+
res.status(500).json({ error: error.message });
|
| 155 |
+
}
|
| 156 |
+
});
|
| 157 |
+
|
| 158 |
// WS Upgrade
|
| 159 |
server.on('upgrade', async (request, socket, head) => {
|
| 160 |
const url = new URL(request.url, `http://${request.headers.host}`);
|
|
|
|
| 167 |
if (!decodedData) { socket.write('HTTP/1.1 403 Forbidden\r\n\r\n'); socket.destroy(); return; }
|
| 168 |
|
| 169 |
wss.handleUpgrade(request, socket, head, (ws) => {
|
| 170 |
+
wss.emit('connection', ws, request, decodedData.uid);
|
| 171 |
});
|
| 172 |
});
|
| 173 |
|
| 174 |
// WS Logic
|
| 175 |
+
wss.on('connection', (ws, req, userId) => {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 176 |
if (!clients.has(userId)) clients.set(userId, new Set());
|
| 177 |
clients.get(userId).add(ws);
|
| 178 |
|
|
|
|
| 183 |
try {
|
| 184 |
const data = JSON.parse(message.toString());
|
| 185 |
|
| 186 |
+
// 1. CHAT PROMPTS & OVERRIDES
|
| 187 |
if (data.type === 'prompt') {
|
| 188 |
ws.send(JSON.stringify({ type: 'status', status: 'thinking' }));
|
| 189 |
const response = await fetch(`${CORE_URL}/process`, {
|
|
|
|
| 224 |
});
|
| 225 |
});
|
| 226 |
|
|
|
|
|
|
|
| 227 |
setInterval(() => {
|
| 228 |
wss.clients.forEach((ws) => {
|
| 229 |
if (ws.isAlive === false) return ws.terminate();
|
|
|
|
| 232 |
});
|
| 233 |
}, 30000);
|
| 234 |
|
| 235 |
+
server.listen(PORT, () => console.log(`🚀 Gateway on ${PORT}`));
|
|
|