Skip to content

Instantly share code, notes, and snippets.

@asim
Created March 15, 2021 14:49
Show Gist options
  • Save asim/af104dd0e83b8c16bbb6918c8d9bab97 to your computer and use it in GitHub Desktop.
Save asim/af104dd0e83b8c16bbb6918c8d9bab97 to your computer and use it in GitHub Desktop.

Revisions

  1. asim created this gist Mar 15, 2021.
    214 changes: 214 additions & 0 deletions groups.tsx
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,214 @@
    import { NextApiRequest, NextApiResponse } from 'next'
    import call, { BaseURL } from '../../../../lib/micro'
    import TokenFromReq from '../../../../lib/token'

    export default async function handler(req: NextApiRequest, res: NextApiResponse) {
    const { query: { group_id } } = req

    if(req.method !== 'GET' && req.method !== 'PATCH') {
    res.status(405)
    return
    }

    // get the token from cookies
    const token = TokenFromReq(req)
    if(!token) {
    res.status(401).json({ error: "No token cookie set" })
    return
    }

    // authenticate the request
    var user: any
    try {
    const rsp = await call("/users/Validate", { token })
    user = rsp.user
    } catch ({ error, code }) {
    if(code === 400) code = 401
    res.status(code).json({ error })
    return
    }

    // load the group
    var group: any
    try {
    const rsp = await call("/groups/Read", { ids: [group_id] })
    group = rsp.groups[group_id as string]
    } catch ({ error, code }) {
    console.error(`Error loading groups: ${error}, code: ${code}`)
    res.status(500).json({ error: "Error loading groups" })
    return
    }
    if(!group) {
    res.status(404).json({ error: "Group not found"})
    return
    }

    // ensure the user is a member of the group
    if(!group.member_ids?.includes(user.id)) {
    res.status(403).json({ error: "Not a member of this group" })
    return
    }

    // update the groups name
    if(req.method === 'PATCH') {
    var body: { name?: string } = {}
    try {
    body = JSON.parse(req.body)
    } catch (error) {
    res.status(400).json({ error: "Error parsing request body" })
    }
    if(!body.name?.length) {
    res.status(400).json({ error: "Name required" })
    }

    try {
    await call("/groups/Update", { id: group.id, name: body.name })
    } catch ({ error, code }) {
    console.error(`Error updating group: ${error}, code: ${code}`)
    res.status(500).json({ error: "Error updating group" })
    return
    }

    // publish the message to the other users in the group
    try {
    group.member_ids.forEach(async(id: string) => {
    await call("/streams/Publish", {
    topic: id,
    message: JSON.stringify({
    type: "group.updated",
    group_id: group.id,
    payload: { name: body.name },
    })
    })
    })
    res.status(200).json({})
    return
    } catch ({ error, code }) {
    console.error(`Error publishing to stream: ${error}, code: ${code}`)
    res.status(500).json({ error: "Error publishing to stream"})
    return
    }
    }

    // load the conversations and the recent messages within them
    var threads: any;
    try {
    const rsp = await call("/threads/ListConversations", { group_id })
    threads = rsp.conversations || [];
    } catch ({ error, code }) {
    console.error(`Error loading conversations: ${error}, code: ${code}`)
    res.status(500).json({ error: "Error loading conversations" })
    return
    }
    var messages: any = {}
    var user_ids: any = [...(group.member_ids || [])];
    if(threads.length > 0) {
    try {
    const rsp = await call("/threads/RecentMessages", { conversation_ids: threads.map(s => s.id) })
    if(rsp.messages) {
    user_ids.push(...rsp.messages.map(m => m.author_id))
    messages = rsp.messages.reduce((res, m) => {
    return { ...res, [m.conversation_id]: [...(res[m.conversation_id] || []), m] }
    }, {})
    }
    } catch ({ error, code }) {
    console.error(`Error loading recent messages: ${error}, code: ${code}`)
    res.status(500).json({ error: "Error loading recent messages" })
    return
    }
    }

    // load the recent messages for all members
    var chatMessages: Record<string,any[]> = {}
    await Promise.all(group.member_ids.filter(id => user.id !== id).map(async (id: string) => {
    var chat_id: any
    try {
    const rsp = await call("/chats/CreateChat", { user_ids: [user.id, id] })
    chat_id = rsp.chat.id
    } catch ({ error, code }) {
    console.error(`Error loading chat: ${error}, code: ${code}`)
    return
    }

    try {
    const rsp = await call("/chats/ListMessages", { chat_id })
    chatMessages[id] = rsp.messages || []
    } catch ({ error, code }) {
    console.error(`Error loading messages: ${error}, code: ${code}`)
    }
    }));

    // load the details of the users
    var users: any
    try {
    users = (await call("/users/Read", { ids: user_ids })).users
    } catch ({ error, code }) {
    console.error(`Error loading users: ${error}, code: ${code}`)
    res.status(500).json({ error: "Error loading users" })
    return
    }

    // load the last time each thread and chat was seen
    var threadLastSeens = {}
    if (threads.length > 0) {
    try {
    const req = { user_id: user.id, resource_type: "thread", resource_ids: threads.map(s => s.id) }
    threadLastSeens = (await call("/seen/Read", req)).timestamps || {}
    } catch ({ error, code }) {
    console.error(`Error loading last seen: ${error}, code: ${code}`)
    res.status(500).json({ error: "Error loading last seen times"})
    return
    }
    }
    var chatLastSeens = {}
    try {
    const req = { user_id: user.id, resource_type: "chat", resource_ids: Object.keys(users) }
    chatLastSeens = (await call("/seen/Read", req)).timestamps || {}
    } catch ({ error, code }) {
    console.error(`Error loading last seen: ${error}, code: ${code}`)
    res.status(500).json({ error: "Error loading last seen times"})
    return
    }

    // generate a token for the websocket
    var websocket: any = { topic: user.id }
    try {
    websocket.token = (await call("/streams/Token", websocket)).token
    websocket.url = BaseURL.replace('http', 'ws') + "/streams/Subscribe"
    } catch ({ error, code }) {
    console.error(`Error loading websocket token: ${error}, code: ${code}`)
    res.status(500).json({ error: "Error loading websocket token"})
    return
    }

    // return the data
    res.status(200).json({
    id: group.id,
    name: group.name,
    members: group.member_ids.map(id => ({
    ...users[id],
    current_user: users[id].id === user.id,
    chat: {
    last_seen: chatLastSeens[id],
    messages: (chatMessages[id] || []).map(m => ({
    id: m.id,
    text: m.text,
    sent_at: m.sent_at,
    author: { ...users[m.author_id], current_user: m.author_id === user.id },
    })),
    },
    })),
    threads: threads.map(s => ({
    id: s.id,
    topic: s.topic,
    last_seen: threadLastSeens[s.id],
    messages: (messages[s.id] || []).map(m => ({
    id: m.id,
    text: m.text,
    sent_at: m.sent_at,
    author: { ...users[m.author_id], current_user: m.author_id === user.id },
    }))
    })),
    websocket
    })
    }