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:
2026-10-22 11:18:19 +01:00
parent a8d8b680ad
commit 16f1e7904a
14 changed files with 402 additions and 10 deletions
@@ -1,5 +1,6 @@
import userAuth from "@/src/functions/backend/auth/user-auth";
import grabConversationProps from "@/src/functions/backend/db/bot-sessions/grab-conversation-props";
import withLastVisitedBotCookies from "@/src/functions/server/with-last-visited-bot-cookies";
import type { PagePropsType, PageQueryObject } from "@/src/types";
import canManageCrew from "@/src/utils/rbac/can-manage-crew";
import type { BunextPageServerFn } from "@moduletrace/bunext/types";
@@ -8,7 +9,8 @@ import type { BunextPageServerFn } from "@moduletrace/bunext/types";
* A bot's conversation inside a workspace: the bot works with the
* workspace's context, computers and models. Only the coordinator and the
* workspace's bots have one; anyone else goes back to the workspace
* settings (managers) or their bots.
* settings (managers) or their bots. Rendering it records the conversation
* in the last-visited cookies, so `/admin/bots` opens it again.
*/
export const server: BunextPageServerFn<PagePropsType> = async (params) => {
const query: PageQueryObject = params.query;
@@ -24,7 +26,7 @@ export const server: BunextPageServerFn<PagePropsType> = async (params) => {
workspace_id: String(query.workspace_id),
});
if (!props) {
if (!props?.bot?.id) {
return {
redirect: {
destination: canManageCrew({ user_types })
@@ -34,5 +36,13 @@ export const server: BunextPageServerFn<PagePropsType> = async (params) => {
};
}
return { props };
const bot_id = props.bot.id;
const workspace_id = props.workspace?.id ?? query.workspace_id;
return {
props,
res_transform(res) {
return withLastVisitedBotCookies({ req: params.req, res, bot_id, workspace_id });
},
};
};