Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Having issues making messages persist #46

Open
himanshu-ntml opened this issue Jul 7, 2024 · 3 comments
Open

Having issues making messages persist #46

himanshu-ntml opened this issue Jul 7, 2024 · 3 comments

Comments

@himanshu-ntml
Copy link

@sor4chi Thank you so much for making hono-do library. It makes it so easy to work with DO and websockets.

I am trying to use it to build a real-time quiz app where I want to use DO and web sockets for real-time results. I am trying to implement persistence in hibernate-chat example. But I am running into issues. Could you please help? I am not sure whether my approach is right or wrong or i am missing something.

import { generateHonoObject } from 'hono-do';
import { defineStorage } from 'hono-do/storage';

function uuidv4() {
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
    const r = (Math.random() * 16) | 0,
      v = c == 'x' ? r : (r & 0x3) | 0x8;
    return v.toString(16);
  });
}

declare module 'hono-do' {
  interface HonoObjectVars {
    messages: {
      timestamp: string;
      text: string;
    }[];
  }
}

export const Chat = generateHonoObject('/chat', async (app, state, vars) => {
  let { storage } = state;
  const [getValue] = await defineStorage(state.storage, 'value', {});

  // console.log(await state.storage.get('value'), 'aaa');

  vars.messages = [];

  app.get('/messages', async (c) => c.json(await getValue()));

  app.get('/websocket', async (c) => {
    if (c.req.header('Upgrade') === 'websocket') {
      return await handleWebSocketUpgrade();
    }
    return c.text('Not found', 404);
  });

  async function handleWebSocketUpgrade() {
    const [client, server] = Object.values(new WebSocketPair());
    const clientId = uuidv4();
    state.acceptWebSocket(server);
    server.serializeAttachment({ clientId });

    return new Response(null, { status: 101, webSocket: client });
  }
});

Chat.webSocketMessage(async (webSocket, msg, state, vars) => {
  const { clientId: senderClientId } = await webSocket.deserializeAttachment();
  const [getValue, setValue] = await defineStorage(state.storage, 'value', {});
  let oldMessages: any = await getValue();
  console.log('msg', msg, vars, state, webSocket);
  oldMessages.push(msg);
  // await state.storage.put('value', oldMessages);
  setValue(oldMessages);
  state.getWebSockets().forEach((ws) => {
    const { clientId } = ws.deserializeAttachment();
    if (clientId === senderClientId) {
      return;
    }
    try {
      vars.messages.push(JSON.parse(msg.toString()));
      ws.send(msg.toString());
    } catch (error) {
      ws.close();
    }
  });
});
@himanshu-ntml
Copy link
Author

I was able to solve... there was some mistake.

import { generateHonoObject } from 'hono-do';
import { defineStorage } from 'hono-do/storage';
import { cors } from 'hono/cors';

function uuidv4() {
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
    const r = (Math.random() * 16) | 0,
      v = c === 'x' ? r : (r & 0x3) | 0x8;
    return v.toString(16);
  });
}

declare module 'hono-do' {
  interface HonoObjectVars {
    messages: {
      timestamp: string;
      text: string;
      id: string;
    }[];
  }
}

export const Chat = generateHonoObject('/chat', async (app, state, vars) => {
  const [getValue, setValue] = await defineStorage(state.storage, 'messages', []);

  vars.messages = (await getValue()) || [];

  app.get('/connect', async (c) => {
    if (c.req.header('Upgrade') === 'websocket') {
      return handleWebSocketUpgrade();
    }
    return c.text('Not found', 404);
  });

  app.use(
    '*',
    cors({
      origin: ['http://localhost:3000', 'http://localhost:8008', '*'],
      allowHeaders: ['Content-Type', 'Authorization'],
      allowMethods: ['POST', 'GET', 'OPTIONS', 'DELETE', 'PATCH'],
      exposeHeaders: ['Content-Length'],
      maxAge: 600,
      credentials: true,
    })
  );

  app.options('*', (c) => {
    return c.text('', 204);
  });

  app.delete('/:id', async (c) => {
    const id = c.req.param('id');
    const [getValue, setValue] = await defineStorage(state.storage, 'messages', []);
    let messages = await getValue();

    const initialLength = messages.length;
    messages = messages.filter((message) => message.id !== id);

    if (messages.length === initialLength) {
      return c.text('Message not found', 404);
    }

    await setValue(messages);
    return c.text('Message deleted successfully', 200);
  });

  app.get('/messages', async (c) => {
    const messages = await getValue();
    // setValue([]);
    return c.json(messages);
  });

  async function handleWebSocketUpgrade() {
    const [client, server] = Object.values(new WebSocketPair());
    const clientId = uuidv4();
    state.acceptWebSocket(server);
    server.serializeAttachment({ clientId });

    return new Response(null, { status: 101, webSocket: client });
  }
});

Chat.webSocketMessage(async (webSocket, msg, state, vars) => {
  const { clientId: senderClientId } = await webSocket.deserializeAttachment();
  const [getValue, setValue] = await defineStorage(state.storage, 'messages', []);
  let oldMessages = await getValue();

  try {
    const parsedMsg = JSON.parse(msg.toString());
    oldMessages.push(parsedMsg);
    await setValue(oldMessages);
    state.getWebSockets().forEach((ws) => {
      const { clientId } = ws.deserializeAttachment();
      if (clientId === senderClientId) {
        return;
      }
      try {
        ws.send(msg.toString());
      } catch (error) {
        ws.close();
      }
    });
  } catch (error) {
    console.error('Error parsing or storing message:', error);
  }
});

Now i am running into another issue. I am not able to fetch all the messages using the get route in another nextjs app. I was running into cors error and tried the above code to solve the cors but now i am getting "Can't modify immtable headers"

✘ [ERROR] TypeError: Can't modify immutable headers.

      at set res
  (file:///Users/nicolethomas/Desktop/Membership/hono/node_modules/hono/dist/context.js:71:24)
      at dispatch
  (file:///Users/nicolethomas/Desktop/Membership/hono/node_modules/hono/dist/compose.js:43:17)
      at async cors2
  (file:///Users/nicolethomas/Desktop/Membership/hono/node_modules/hono/dist/middleware/cors/index.js:70:5)
      at async dispatch
  (file:///Users/nicolethomas/Desktop/Membership/hono/node_modules/hono/dist/compose.js:29:17)
      at null.<anonymous> (async
  file:///Users/nicolethomas/Desktop/Membership/hono/.wrangler/tmp/dev-AmZfOC/index.js:2764:25)
      at async jsonError
  (file:///Users/nicolethomas/Desktop/Membership/hono/node_modules/wrangler/templates/middleware/middleware-miniflare3-json-error.ts:22:10)
      at async drainBody
  (file:///Users/nicolethomas/Desktop/Membership/hono/node_modules/wrangler/templates/middleware/middleware-ensure-req-body-drained.ts:5:10)
  TypeError: Can't modify immutable headers.
      at set res
  (file:///Users/nicolethomas/Desktop/Membership/hono/node_modules/hono/dist/context.js:71:24)
      at dispatch
  (file:///Users/nicolethomas/Desktop/Membership/hono/node_modules/hono/dist/compose.js:43:17)
      at async cors2
  (file:///Users/nicolethomas/Desktop/Membership/hono/node_modules/hono/dist/middleware/cors/index.js:70:5)
      at async dispatch
  (file:///Users/nicolethomas/Desktop/Membership/hono/node_modules/hono/dist/compose.js:29:17)
      at null.<anonymous> (async
  file:///Users/nicolethomas/Desktop/Membership/hono/.wrangler/tmp/dev-AmZfOC/index.js:2764:25)
      at async jsonError
  (file:///Users/nicolethomas/Desktop/Membership/hono/node_modules/wrangler/templates/middleware/middleware-miniflare3-json-error.ts:22:10)
      at async drainBody
  (file:///Users/nicolethomas/Desktop/Membership/hono/node_modules/wrangler/templates/middleware/middleware-ensure-req-body-drained.ts:5:10)


[wrangler:inf] GET /chat/connect 500 Internal Server Error (12ms)
[wrangler:inf] GET /chat/messages 500 Internal Server Error (16ms)
[wrangler:inf] GET /chat/messages 500 Internal Server Error (15ms)

@sor4chi
Copy link
Owner

sor4chi commented Jul 8, 2024

Thank you for asking, I'll look at it later!

@sor4chi
Copy link
Owner

sor4chi commented Jul 9, 2024

Hi, @himanshu-ntml

I think this Can't modify immutable headers. issue can't be solved without also looking at the code of the main workers that proxy Durable Objects.
I tried it in my environment to test it and it did not have such an effect.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants