Open the bot last visited, else the coordinator
`/admin/bots` sent the super admin to the coordinator and everyone else to their latest bot, which lost the thread each time the tab was reopened. The conversation pages now record where the browser is in two HttpOnly cookies (`cobble_last_visited_bot_id`, `..._workspace_id`, a year, `Secure` only over HTTPS), and the landing page opens that conversation again, in its workspace when it was one. A stale cookie can't loop: `grabReachableConversation` re-checks it with `grabUserConversation`, the gate the conversation pages use, so a revoked grant, a deleted bot or an archived workspace falls back to the coordinator (super admin) or the latest bot, first workspace, empty page. Signing out clears both cookies so the next user on that browser doesn't land in the previous one's conversation.
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
import grabUserConversation from "./grab-user-conversation";
|
||||
|
||||
type Params = {
|
||||
user_id: number | string;
|
||||
bot_id: number | string;
|
||||
/**
|
||||
* Null or undefined: the bot's own chat.
|
||||
*/
|
||||
workspace_id?: number | string | null;
|
||||
};
|
||||
|
||||
export type ReachableConversation = {
|
||||
bot_id: number;
|
||||
workspace_id: number | null;
|
||||
};
|
||||
|
||||
/**
|
||||
* A conversation from the last-visited cookie, only while the user still
|
||||
* reaches it: `grabUserConversation` is the same access check the
|
||||
* conversation pages use, so a revoked grant, a deleted bot or an archived
|
||||
* workspace answers null and the landing page falls back instead of
|
||||
* redirecting into a page that would bounce it back.
|
||||
*/
|
||||
export default async function grabReachableConversation({
|
||||
user_id,
|
||||
bot_id,
|
||||
workspace_id,
|
||||
}: Params): Promise<ReachableConversation | null> {
|
||||
const resolved_workspace_id = workspace_id ? Number(workspace_id) : null;
|
||||
|
||||
const conversation = await grabUserConversation({
|
||||
user_id,
|
||||
bot_id,
|
||||
workspace_id: resolved_workspace_id,
|
||||
}).catch(() => null);
|
||||
|
||||
if (!conversation?.bot.id) return null;
|
||||
|
||||
return { bot_id: conversation.bot.id, workspace_id: conversation.workspace_id };
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import { setCookies } from "@/src/utils/cookies";
|
||||
import isSecureRequest from "@/src/utils/is-secure-request";
|
||||
import lastVisitedBotCookies from "@/src/utils/last-visited-bot-cookies";
|
||||
|
||||
type Params = {
|
||||
req: Request;
|
||||
res: Response;
|
||||
bot_id: number | string;
|
||||
/**
|
||||
* Null or undefined: the bot's own chat.
|
||||
*/
|
||||
workspace_id?: number | string | null;
|
||||
};
|
||||
|
||||
/**
|
||||
* The page's response with the last-visited cookies set, so `/admin/bots`
|
||||
* opens this conversation again next time. Called from the conversation
|
||||
* pages' `res_transform`, which is why it takes and returns the response.
|
||||
*/
|
||||
export default function withLastVisitedBotCookies({
|
||||
req,
|
||||
res,
|
||||
bot_id,
|
||||
workspace_id,
|
||||
}: Params): Response {
|
||||
const new_res = res.clone();
|
||||
|
||||
setCookies(
|
||||
new_res,
|
||||
lastVisitedBotCookies({
|
||||
bot_id,
|
||||
workspace_id,
|
||||
secure: isSecureRequest({ req }),
|
||||
}),
|
||||
);
|
||||
|
||||
return new_res;
|
||||
}
|
||||
Reference in New Issue
Block a user