fix
All checks were successful
Deploy Encrypted Todo App / build-and-push (push) Successful in 2m36s
All checks were successful
Deploy Encrypted Todo App / build-and-push (push) Successful in 2m36s
This commit is contained in:
257
server.js
257
server.js
@ -31,130 +31,129 @@ wss.on("connection", (ws, req) => {
|
||||
connections.delete(userId);
|
||||
console.log(`User ${userId} disconnected`);
|
||||
});
|
||||
|
||||
ws.on("error", (error) => {
|
||||
console.error(`WebSocket error for user ${userId}:`, error);
|
||||
connections.delete(userId);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Broadcast to all connected clients
|
||||
// Helper function to broadcast to all connected users
|
||||
function broadcast(message) {
|
||||
const messageStr = JSON.stringify(message);
|
||||
connections.forEach((ws) => {
|
||||
connections.forEach((ws, userId) => {
|
||||
if (ws.readyState === WebSocket.OPEN) {
|
||||
ws.send(messageStr);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Broadcast to specific user
|
||||
// Helper function to broadcast to a specific user
|
||||
function broadcastToUser(userId, message) {
|
||||
const ws = connections.get(userId);
|
||||
console.log(
|
||||
`broadcastToUser called for ${userId}, connection exists: ${!!ws}, readyState: ${ws ? ws.readyState : "N/A"}`,
|
||||
);
|
||||
if (ws && ws.readyState === WebSocket.OPEN) {
|
||||
console.log(
|
||||
`✓ Sending WebSocket message to ${userId}:`,
|
||||
message.type,
|
||||
JSON.stringify(message.data),
|
||||
);
|
||||
ws.send(JSON.stringify(message));
|
||||
} else {
|
||||
console.log(
|
||||
`✗ WebSocket not available for user ${userId} - connection: ${!!ws}, readyState: ${ws ? ws.readyState : "N/A"}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
app.use(express.json());
|
||||
app.use(express.static("public"));
|
||||
|
||||
// Create user
|
||||
app.post("/api/users", (req, res) => {
|
||||
try {
|
||||
const { userId } = req.body;
|
||||
const user = todoService.createUser(userId);
|
||||
broadcast({ type: "users", data: todoService.getAllUsers() });
|
||||
res.json(user);
|
||||
} catch (error) {
|
||||
res.status(400).json({ error: error.message });
|
||||
}
|
||||
// Middleware to handle async errors
|
||||
const asyncHandler = (fn) => (req, res, next) => {
|
||||
Promise.resolve(fn(req, res, next)).catch(next);
|
||||
};
|
||||
|
||||
// Error handling middleware
|
||||
app.use((err, req, res, next) => {
|
||||
console.error("Error:", err);
|
||||
res.status(500).json({ error: err.message || "Internal server error" });
|
||||
});
|
||||
|
||||
// Create a new user
|
||||
app.post(
|
||||
"/api/users",
|
||||
asyncHandler(async (req, res) => {
|
||||
const { userId } = req.body;
|
||||
const user = await todoService.createUser(userId);
|
||||
const users = await todoService.getAllUsers();
|
||||
broadcast({ type: "users", data: users });
|
||||
res.json(user);
|
||||
}),
|
||||
);
|
||||
|
||||
// Get all users
|
||||
app.get("/api/users", (req, res) => {
|
||||
try {
|
||||
const users = todoService.getAllUsers();
|
||||
app.get(
|
||||
"/api/users",
|
||||
asyncHandler(async (req, res) => {
|
||||
const users = await todoService.getAllUsers();
|
||||
res.json(users);
|
||||
} catch (error) {
|
||||
res.status(400).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
// Add todo
|
||||
app.post("/api/users/:userId/todos", (req, res) => {
|
||||
try {
|
||||
// Add a new todo
|
||||
app.post(
|
||||
"/api/users/:userId/todos",
|
||||
asyncHandler(async (req, res) => {
|
||||
const { userId } = req.params;
|
||||
const { text } = req.body;
|
||||
const todoGroupId = todoService.addTodo(userId, text);
|
||||
const todo = todoService.getTodo(userId, todoGroupId);
|
||||
const todoGroupId = await todoService.addTodo(userId, text);
|
||||
const todo = await todoService.getTodo(userId, todoGroupId);
|
||||
broadcastToUser(userId, { type: "todo_added", data: todo });
|
||||
res.json({ id: todoGroupId });
|
||||
} catch (error) {
|
||||
res.status(400).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
// Get todos
|
||||
app.get("/api/users/:userId/todos", (req, res) => {
|
||||
try {
|
||||
// Get todos for a user
|
||||
app.get(
|
||||
"/api/users/:userId/todos",
|
||||
asyncHandler(async (req, res) => {
|
||||
const { userId } = req.params;
|
||||
const todos = todoService.getTodos(userId);
|
||||
const todos = await todoService.getTodos(userId);
|
||||
res.json(todos);
|
||||
} catch (error) {
|
||||
res.status(400).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
// Get encrypted todos
|
||||
app.get("/api/users/:userId/todos/encrypted", (req, res) => {
|
||||
try {
|
||||
// Get encrypted todos (for debugging)
|
||||
app.get(
|
||||
"/api/users/:userId/todos/encrypted",
|
||||
asyncHandler(async (req, res) => {
|
||||
const { userId } = req.params;
|
||||
const todos = todoService.getTodos(userId);
|
||||
const encryptedTodos = todos.map((todo) => {
|
||||
const row = todoService.db
|
||||
.prepare(
|
||||
"SELECT encrypted, createdAt FROM todos WHERE todoGroupId = ? AND userId = ?",
|
||||
)
|
||||
.get(todo.id, userId);
|
||||
const todos = await todoService.getTodos(userId);
|
||||
const encryptedTodos = [];
|
||||
|
||||
if (!row) {
|
||||
for (const todo of todos) {
|
||||
const row = await todoService.db.execute(
|
||||
"SELECT encrypted, createdAt FROM todos WHERE todoGroupId = ? AND userId = ?",
|
||||
[todo.id, userId],
|
||||
);
|
||||
|
||||
if (row.rows.length === 0) {
|
||||
console.warn(
|
||||
`No encrypted data found for todo ${todo.id} and user ${userId}`,
|
||||
);
|
||||
return {
|
||||
id: todo.id,
|
||||
encrypted: { body: "Encryption data not available" },
|
||||
createdAt: todo.createdAt,
|
||||
};
|
||||
continue;
|
||||
}
|
||||
|
||||
return {
|
||||
encryptedTodos.push({
|
||||
id: todo.id,
|
||||
encrypted: JSON.parse(row.encrypted),
|
||||
createdAt: row.createdAt,
|
||||
};
|
||||
});
|
||||
res.json(encryptedTodos);
|
||||
} catch (error) {
|
||||
console.error("Error in encrypted todos endpoint:", error);
|
||||
res.status(400).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
encrypted: row.rows[0].encrypted,
|
||||
createdAt: row.rows[0].createdAt,
|
||||
participants: todo.participants,
|
||||
});
|
||||
}
|
||||
|
||||
// Delete todo
|
||||
app.delete("/api/users/:userId/todos/:todoGroupId", (req, res) => {
|
||||
try {
|
||||
res.json(encryptedTodos);
|
||||
}),
|
||||
);
|
||||
|
||||
// Delete a todo
|
||||
app.delete(
|
||||
"/api/users/:userId/todos/:todoGroupId",
|
||||
asyncHandler(async (req, res) => {
|
||||
const { userId, todoGroupId } = req.params;
|
||||
const { deletedIds, affectedUsers } = todoService.deleteTodo(
|
||||
const { deletedIds, affectedUsers } = await todoService.deleteTodo(
|
||||
userId,
|
||||
todoGroupId,
|
||||
);
|
||||
@ -163,9 +162,6 @@ app.delete("/api/users/:userId/todos/:todoGroupId", (req, res) => {
|
||||
console.log(`Broadcasting deletion to users: ${affectedUsers.join(", ")}`);
|
||||
affectedUsers.forEach((affectedUserId) => {
|
||||
deletedIds.forEach((deletedId) => {
|
||||
console.log(
|
||||
`Sending todo_deleted event for todo ${deletedId} to user ${affectedUserId}`,
|
||||
);
|
||||
broadcastToUser(affectedUserId, {
|
||||
type: "todo_deleted",
|
||||
data: { id: deletedId, deletedBy: userId },
|
||||
@ -173,43 +169,41 @@ app.delete("/api/users/:userId/todos/:todoGroupId", (req, res) => {
|
||||
});
|
||||
});
|
||||
|
||||
res.json({ success: true });
|
||||
} catch (error) {
|
||||
res.status(400).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
res.json({ deletedIds, affectedUsers });
|
||||
}),
|
||||
);
|
||||
|
||||
// Share todo
|
||||
app.post("/api/users/:userId/todos/:todoGroupId/share", (req, res) => {
|
||||
try {
|
||||
// Share a todo with another user
|
||||
app.post(
|
||||
"/api/users/:userId/todos/:todoGroupId/share",
|
||||
asyncHandler(async (req, res) => {
|
||||
const { userId, todoGroupId } = req.params;
|
||||
const { recipientId } = req.body;
|
||||
if (!recipientId) {
|
||||
return res.status(400).json({ error: "Recipient ID is required" });
|
||||
}
|
||||
const sharedTodoGroupId = todoService.shareTodo(
|
||||
const sharedTodoGroupId = await todoService.shareTodo(
|
||||
userId,
|
||||
todoGroupId,
|
||||
recipientId,
|
||||
);
|
||||
const sharedTodo = todoService.getTodo(recipientId, sharedTodoGroupId);
|
||||
const sharedTodo = await todoService.getTodo(
|
||||
recipientId,
|
||||
sharedTodoGroupId,
|
||||
);
|
||||
broadcastToUser(recipientId, { type: "todo_shared", data: sharedTodo });
|
||||
|
||||
// Also notify the original user that the todo was shared
|
||||
broadcastToUser(userId, {
|
||||
type: "todo_share_confirmed",
|
||||
data: { todoGroupId, sharedWith: recipientId },
|
||||
res.json({
|
||||
sharedTodoGroupId,
|
||||
message: `Todo shared with ${recipientId}`,
|
||||
});
|
||||
|
||||
res.json({ success: true });
|
||||
} catch (error) {
|
||||
res.status(400).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
// Update todo completion status
|
||||
app.patch("/api/users/:userId/todos/:todoGroupId", (req, res) => {
|
||||
try {
|
||||
app.patch(
|
||||
"/api/users/:userId/todos/:todoGroupId",
|
||||
asyncHandler(async (req, res) => {
|
||||
const { userId, todoGroupId } = req.params;
|
||||
const { completed } = req.body;
|
||||
|
||||
@ -217,41 +211,38 @@ app.patch("/api/users/:userId/todos/:todoGroupId", (req, res) => {
|
||||
`PATCH request: user ${userId} updating todo ${todoGroupId} to completed=${completed}`,
|
||||
);
|
||||
|
||||
const affectedUsers = todoService.updateTodoStatus(
|
||||
const affectedUsers = await todoService.updateTodoStatus(
|
||||
userId,
|
||||
todoGroupId,
|
||||
completed,
|
||||
);
|
||||
|
||||
console.log(`updateTodoStatus returned affected users:`, affectedUsers);
|
||||
console.log(`Broadcasting update to users: ${affectedUsers.join(", ")}`);
|
||||
|
||||
// Broadcast update to all affected users
|
||||
console.log(
|
||||
`Broadcasting todo update to users: ${affectedUsers.join(", ")}`,
|
||||
);
|
||||
affectedUsers.forEach((affectedUserId) => {
|
||||
console.log(
|
||||
`Sending todo_updated event for todo ${todoGroupId} to user ${affectedUserId}`,
|
||||
);
|
||||
const message = {
|
||||
type: "todo_updated",
|
||||
data: { id: todoGroupId, completed, updatedBy: userId },
|
||||
};
|
||||
console.log(
|
||||
`Message being sent to ${affectedUserId}:`,
|
||||
JSON.stringify(message),
|
||||
);
|
||||
broadcastToUser(affectedUserId, message);
|
||||
});
|
||||
// Broadcast the update to all affected users
|
||||
for (const affectedUserId of affectedUsers) {
|
||||
try {
|
||||
const todo = await todoService.getTodo(affectedUserId, todoGroupId);
|
||||
const message = {
|
||||
type: "todo_updated",
|
||||
data: { id: todoGroupId, completed, updatedBy: userId },
|
||||
};
|
||||
broadcastToUser(affectedUserId, message);
|
||||
console.log(`Sent update to user ${affectedUserId}:`, message);
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`Error getting todo for user ${affectedUserId}:`,
|
||||
error.message,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
res.json({ success: true });
|
||||
} catch (error) {
|
||||
console.error(`Error updating todo status:`, error);
|
||||
res.status(400).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
res.json({ message: "Todo updated successfully", affectedUsers });
|
||||
}),
|
||||
);
|
||||
|
||||
const PORT = process.env.APP_PORT || 3000;
|
||||
|
||||
const PORT = 3000;
|
||||
server.listen(PORT, () => {
|
||||
console.log(`Server running on port 3000`);
|
||||
console.log(`Server running on port ${PORT}`);
|
||||
});
|
||||
|
Reference in New Issue
Block a user