Initial version
This commit is contained in:
145
todo-service.js
Normal file
145
todo-service.js
Normal file
@ -0,0 +1,145 @@
|
||||
import { SignalClient } from "./signal-crypto.js";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
export class EncryptedTodoService {
|
||||
constructor() {
|
||||
this.users = new Map(); // userId -> SignalClient
|
||||
this.todos = new Map(); // todoId -> encrypted todo
|
||||
this.userTodos = new Map(); // userId -> Set of todoIds
|
||||
this.sharedSessions = new Map(); // userId+recipientId -> shared session
|
||||
}
|
||||
|
||||
async createUser(userId) {
|
||||
if (this.users.has(userId)) {
|
||||
console.log(`User ${userId} already exists. Logging in.`);
|
||||
return { userId };
|
||||
}
|
||||
|
||||
const client = await SignalClient.create(userId);
|
||||
this.users.set(userId, client);
|
||||
this.userTodos.set(userId, new Set());
|
||||
|
||||
return { userId };
|
||||
}
|
||||
|
||||
getUser(userId) {
|
||||
const user = this.users.get(userId);
|
||||
if (!user) {
|
||||
throw new Error("User not found. Please create the user first.");
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
async addTodo(userId, todoText) {
|
||||
const client = this.getUser(userId);
|
||||
const todoId = uuidv4();
|
||||
|
||||
// Self-encrypt the todo
|
||||
const encryptedTodo = await client.encrypt(todoText);
|
||||
|
||||
this.todos.set(todoId, {
|
||||
id: todoId,
|
||||
userId,
|
||||
encrypted: encryptedTodo,
|
||||
createdAt: new Date().toISOString(),
|
||||
originalText: todoText, // Store original for sharing (in a real app, this would be encrypted)
|
||||
});
|
||||
|
||||
this.userTodos.get(userId).add(todoId);
|
||||
console.log(`Todo added for user ${userId}: ${todoId}`);
|
||||
|
||||
return todoId;
|
||||
}
|
||||
|
||||
async getTodos(userId) {
|
||||
const client = this.getUser(userId);
|
||||
const todoIds = this.userTodos.get(userId) || new Set();
|
||||
const todos = [];
|
||||
|
||||
for (const todoId of todoIds) {
|
||||
const encryptedTodo = this.todos.get(todoId);
|
||||
if (encryptedTodo) {
|
||||
try {
|
||||
const decryptedText = await client.decrypt(encryptedTodo.encrypted);
|
||||
todos.push({
|
||||
id: todoId,
|
||||
text: decryptedText,
|
||||
createdAt: encryptedTodo.createdAt,
|
||||
sharedBy: encryptedTodo.sharedBy,
|
||||
sharedWith: encryptedTodo.sharedWith || [],
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(`Failed to decrypt todo ${todoId}:`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return todos.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt));
|
||||
}
|
||||
|
||||
async deleteTodo(userId, todoId) {
|
||||
const userTodos = this.userTodos.get(userId);
|
||||
if (!userTodos || !userTodos.has(todoId)) {
|
||||
throw new Error("Todo not found");
|
||||
}
|
||||
|
||||
userTodos.delete(todoId);
|
||||
|
||||
// Don't delete from todos map if it's shared with others
|
||||
const todo = this.todos.get(todoId);
|
||||
if (todo && (!todo.sharedWith || todo.sharedWith.length === 0)) {
|
||||
this.todos.delete(todoId);
|
||||
}
|
||||
|
||||
console.log(`Todo deleted: ${todoId}`);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Share a todo with another user
|
||||
async shareTodo(userId, todoId, recipientId) {
|
||||
// Validate users and todo
|
||||
const senderClient = this.getUser(userId);
|
||||
const recipientClient = this.getUser(recipientId);
|
||||
|
||||
const todo = this.todos.get(todoId);
|
||||
if (!todo) {
|
||||
throw new Error("Todo not found");
|
||||
}
|
||||
|
||||
// Get the original text (in a real app, this would be re-encrypted for the recipient)
|
||||
const originalText = todo.originalText;
|
||||
if (!originalText) {
|
||||
throw new Error("Cannot share this todo");
|
||||
}
|
||||
|
||||
// Encrypt the todo for the recipient
|
||||
const encryptedForRecipient = await recipientClient.encrypt(originalText);
|
||||
|
||||
// Create a new todo ID for the shared copy
|
||||
const sharedTodoId = uuidv4();
|
||||
|
||||
// Store the shared todo
|
||||
this.todos.set(sharedTodoId, {
|
||||
id: sharedTodoId,
|
||||
userId: recipientId,
|
||||
encrypted: encryptedForRecipient,
|
||||
createdAt: new Date().toISOString(),
|
||||
originalText: originalText,
|
||||
sharedBy: userId,
|
||||
});
|
||||
|
||||
// Add to recipient's todos
|
||||
this.userTodos.get(recipientId).add(sharedTodoId);
|
||||
|
||||
// Track sharing in the original todo
|
||||
if (!todo.sharedWith) {
|
||||
todo.sharedWith = [];
|
||||
}
|
||||
todo.sharedWith.push(recipientId);
|
||||
|
||||
console.log(
|
||||
`Todo ${todoId} shared from ${userId} to ${recipientId} as ${sharedTodoId}`,
|
||||
);
|
||||
return sharedTodoId;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user