adding the public_password field for chats table

This commit is contained in:
Wells Bunker 2025-09-29 11:48:22 -06:00
parent 1f4276e6ee
commit 16892fe64e
No known key found for this signature in database
GPG Key ID: DB16D6F2679B78FC
10 changed files with 5859 additions and 30 deletions

View File

@ -1,12 +1,10 @@
import { checkPermission } from '@buster/access-controls'; import { checkPermission } from '@buster/access-controls';
import type { User } from '@buster/database/queries'; import type { User } from '@buster/database/queries';
import { getChatWithDetails, getMessagesForChatWithUserDetails } from '@buster/database/queries';
import { import {
getChatWithDetails, GetChatRequestParamsSchema,
getMessagesForChatWithUserDetails, GetChatRequestQuerySchema,
} from '@buster/database/queries';
import {
type GetChatResponse, type GetChatResponse,
GetChatRequestSchema,
} from '@buster/server-shared/chats'; } from '@buster/server-shared/chats';
import { zValidator } from '@hono/zod-validator'; import { zValidator } from '@hono/zod-validator';
import { Hono } from 'hono'; import { Hono } from 'hono';
@ -17,18 +15,34 @@ import { buildChatWithMessages } from '../services/chat-helpers';
interface GetChatHandlerParams { interface GetChatHandlerParams {
chatId: string; chatId: string;
user: User; user: User;
userSuppliedPassword?: string;
} }
const app = new Hono().get('/', zValidator('param', GetChatRequestSchema), async (c) => { const app = new Hono().get(
const { id } = c.req.valid('param'); '/',
const user = c.get('busterUser'); zValidator('param', GetChatRequestParamsSchema),
zValidator('query', GetChatRequestQuerySchema),
async (c) => {
const { id } = c.req.valid('param');
const { password } = c.req.valid('query');
const user = c.get('busterUser');
console.info(`Processing GET request for chat with ID: ${id}, user_id: ${user.id}`); console.info(`Processing GET request for chat with ID: ${id}, user_id: ${user.id}`);
const response: GetChatResponse = await getChatHandler({ chatId: id, user }); const getChatHandlerParams: GetChatHandlerParams = {
chatId: id,
user,
};
return c.json(response); if (password) {
}); getChatHandlerParams.userSuppliedPassword = password;
}
const response: GetChatResponse = await getChatHandler(getChatHandlerParams);
return c.json(response);
}
);
export default app; export default app;
@ -37,7 +51,7 @@ export default app;
* This is the TypeScript equivalent of the Rust get_chat_handler * This is the TypeScript equivalent of the Rust get_chat_handler
*/ */
export async function getChatHandler(params: GetChatHandlerParams): Promise<GetChatResponse> { export async function getChatHandler(params: GetChatHandlerParams): Promise<GetChatResponse> {
const { chatId, user } = params; const { chatId, user, userSuppliedPassword } = params;
// Fetch chat with messages and related data // Fetch chat with messages and related data
const chatData = await getChatWithDetails({ const chatData = await getChatWithDetails({
@ -64,6 +78,8 @@ export async function getChatHandler(params: GetChatHandlerParams): Promise<GetC
workspaceSharing: chat.workspaceSharing || 'none', workspaceSharing: chat.workspaceSharing || 'none',
publiclyAccessible: chat.publiclyAccessible || false, publiclyAccessible: chat.publiclyAccessible || false,
publicExpiryDate: chat.publicExpiryDate ?? undefined, publicExpiryDate: chat.publicExpiryDate ?? undefined,
publicPassword: chat.publicPassword ?? undefined,
userSuppliedPassword,
}); });
if (!hasAccess || !effectiveRole) { if (!hasAccess || !effectiveRole) {

View File

@ -203,7 +203,7 @@ export async function buildChatWithMessages(
publicly_accessible: chat.publiclyAccessible || false, publicly_accessible: chat.publiclyAccessible || false,
public_expiry_date: chat.publicExpiryDate || null, public_expiry_date: chat.publicExpiryDate || null,
public_enabled_by: publiclyEnabledBy, public_enabled_by: publiclyEnabledBy,
public_password: null, // password not implemented yet public_password: chat.publicPassword || null,
permission, permission,
workspace_sharing: chat.workspaceSharing || 'none', workspace_sharing: chat.workspaceSharing || 'none',
workspace_member_count: workspaceMemberCount, workspace_member_count: workspaceMemberCount,

View File

@ -2,7 +2,7 @@ import type {
DeleteChatsRequest, DeleteChatsRequest,
DuplicateChatRequest, DuplicateChatRequest,
DuplicateChatResponse, DuplicateChatResponse,
GetChatRequest, GetChatRequestParams,
GetChatResponse, GetChatResponse,
GetLogsListRequest, GetLogsListRequest,
GetLogsListResponse, GetLogsListResponse,
@ -30,7 +30,7 @@ export const getListLogs = async (params?: GetLogsListRequest): Promise<GetLogsL
.then((res) => res.data); .then((res) => res.data);
}; };
export const getChat = async ({ id }: GetChatRequest): Promise<GetChatResponse> => { export const getChat = async ({ id }: GetChatRequestParams): Promise<GetChatResponse> => {
return mainApiV2.get<GetChatResponse>(`${CHATS_BASE}/${id}`).then((res) => res.data); return mainApiV2.get<GetChatResponse>(`${CHATS_BASE}/${id}`).then((res) => res.data);
}; };

View File

@ -62,7 +62,11 @@ export async function checkMetricDashboardAccess(
* Check if a user has access to a metric through any chat that contains it. * Check if a user has access to a metric through any chat that contains it.
* If a user has access to a chat (direct, public, or workspace), they can view the metrics in it. * If a user has access to a chat (direct, public, or workspace), they can view the metrics in it.
*/ */
export async function checkMetricChatAccess(metricId: string, user: User): Promise<boolean> { export async function checkMetricChatAccess(
metricId: string,
user: User,
userSuppliedPassword?: string
): Promise<boolean> {
try { try {
// Get all chats containing this metric with their workspace sharing info // Get all chats containing this metric with their workspace sharing info
const chats = await checkChatsContainingAsset(metricId, 'metric_file'); const chats = await checkChatsContainingAsset(metricId, 'metric_file');
@ -82,8 +86,8 @@ export async function checkMetricChatAccess(metricId: string, user: User): Promi
workspaceSharing: (chat.workspaceSharing as WorkspaceSharing) ?? 'none', workspaceSharing: (chat.workspaceSharing as WorkspaceSharing) ?? 'none',
publiclyAccessible: chat.publiclyAccessible, publiclyAccessible: chat.publiclyAccessible,
publicExpiryDate: chat.publicExpiryDate ?? undefined, publicExpiryDate: chat.publicExpiryDate ?? undefined,
publicPassword: undefined, // We don't support passwords on the chats table publicPassword: chat.publicPassword ?? undefined,
userSuppliedPassword: undefined, // We don't support passwords on the chats table userSuppliedPassword: userSuppliedPassword,
}); });
if (hasAccess) { if (hasAccess) {
@ -152,7 +156,11 @@ export async function checkMetricReportAccess(
* Check if a user has access to a dashboard through any chat that contains it. * Check if a user has access to a dashboard through any chat that contains it.
* If a user has access to a chat (direct, public, or workspace), they can view the dashboards in it. * If a user has access to a chat (direct, public, or workspace), they can view the dashboards in it.
*/ */
export async function checkDashboardChatAccess(dashboardId: string, user: User): Promise<boolean> { export async function checkDashboardChatAccess(
dashboardId: string,
user: User,
userSuppliedPassword?: string
): Promise<boolean> {
try { try {
// Get all chats containing this dashboard with their workspace sharing info // Get all chats containing this dashboard with their workspace sharing info
const chats = await checkChatsContainingAsset(dashboardId, 'dashboard_file'); const chats = await checkChatsContainingAsset(dashboardId, 'dashboard_file');
@ -172,8 +180,8 @@ export async function checkDashboardChatAccess(dashboardId: string, user: User):
workspaceSharing: (chat.workspaceSharing as WorkspaceSharing) ?? 'none', workspaceSharing: (chat.workspaceSharing as WorkspaceSharing) ?? 'none',
publiclyAccessible: chat.publiclyAccessible, publiclyAccessible: chat.publiclyAccessible,
publicExpiryDate: chat.publicExpiryDate ?? undefined, publicExpiryDate: chat.publicExpiryDate ?? undefined,
publicPassword: undefined, // We don't support passwords on the chats table publicPassword: chat.publicPassword ?? undefined,
userSuppliedPassword: undefined, // We don't support passwords on the chats table userSuppliedPassword: userSuppliedPassword,
}); });
if (hasAccess) { if (hasAccess) {
@ -315,7 +323,11 @@ export async function checkChatCollectionAccess(chatId: string, user: User): Pro
* Check if a user has access to a report through any chat that contains it. * Check if a user has access to a report through any chat that contains it.
* If a user has access to a chat (direct, public, or workspace), they can view the reports in it. * If a user has access to a chat (direct, public, or workspace), they can view the reports in it.
*/ */
export async function checkReportChatAccess(reportId: string, user: User): Promise<boolean> { export async function checkReportChatAccess(
reportId: string,
user: User,
userSuppliedPassword?: string
): Promise<boolean> {
try { try {
// Get all chats containing this dashboard with their workspace sharing info // Get all chats containing this dashboard with their workspace sharing info
const chats = await checkChatsContainingAsset(reportId, 'report_file'); const chats = await checkChatsContainingAsset(reportId, 'report_file');
@ -335,8 +347,8 @@ export async function checkReportChatAccess(reportId: string, user: User): Promi
workspaceSharing: (chat.workspaceSharing as WorkspaceSharing) ?? 'none', workspaceSharing: (chat.workspaceSharing as WorkspaceSharing) ?? 'none',
publiclyAccessible: chat.publiclyAccessible, publiclyAccessible: chat.publiclyAccessible,
publicExpiryDate: chat.publicExpiryDate ?? undefined, publicExpiryDate: chat.publicExpiryDate ?? undefined,
publicPassword: undefined, // We don't support passwords on the chats table publicPassword: chat.publicPassword ?? undefined,
userSuppliedPassword: undefined, // We don't support passwords on the chats table userSuppliedPassword: userSuppliedPassword,
}); });
if (hasAccess) { if (hasAccess) {
@ -425,7 +437,7 @@ export async function checkCascadingPermissions(
break; break;
} }
const chatAccess = await checkMetricChatAccess(assetId, user); const chatAccess = await checkMetricChatAccess(assetId, user, userSuppliedPassword);
if (chatAccess) { if (chatAccess) {
hasAccess = true; hasAccess = true;
break; break;
@ -447,7 +459,11 @@ export async function checkCascadingPermissions(
case 'dashboard_file': { case 'dashboard_file': {
// Check access through chats and collections // Check access through chats and collections
const dashboardChatAccess = await checkDashboardChatAccess(assetId, user); const dashboardChatAccess = await checkDashboardChatAccess(
assetId,
user,
userSuppliedPassword
);
if (dashboardChatAccess) { if (dashboardChatAccess) {
hasAccess = true; hasAccess = true;
break; break;
@ -473,7 +489,7 @@ export async function checkCascadingPermissions(
case 'report_file': { case 'report_file': {
// Check access through chats and collections // Check access through chats and collections
const reportChatAccess = await checkReportChatAccess(assetId, user); const reportChatAccess = await checkReportChatAccess(assetId, user, userSuppliedPassword);
if (reportChatAccess) { if (reportChatAccess) {
hasAccess = true; hasAccess = true;
break; break;

View File

@ -0,0 +1 @@
ALTER TABLE "chats" ADD COLUMN "public_password" text;

File diff suppressed because it is too large Load Diff

View File

@ -771,6 +771,13 @@
"when": 1758658933810, "when": 1758658933810,
"tag": "0110_third_ozymandias", "tag": "0110_third_ozymandias",
"breakpoints": true "breakpoints": true
},
{
"idx": 111,
"version": "7",
"when": 1759167570252,
"tag": "0111_happy_peter_quill",
"breakpoints": true
} }
] ]
} }

View File

@ -9,6 +9,7 @@ export interface ChatWithSharing {
workspaceSharing: WorkspaceSharing | null; workspaceSharing: WorkspaceSharing | null;
publiclyAccessible: boolean; publiclyAccessible: boolean;
publicExpiryDate: string | null; publicExpiryDate: string | null;
publicPassword: string | null;
} }
export async function checkChatsContainingAsset( export async function checkChatsContainingAsset(
@ -22,6 +23,7 @@ export async function checkChatsContainingAsset(
workspaceSharing: chats.workspaceSharing, workspaceSharing: chats.workspaceSharing,
publiclyAccessible: chats.publiclyAccessible, publiclyAccessible: chats.publiclyAccessible,
publicExpiryDate: chats.publicExpiryDate, publicExpiryDate: chats.publicExpiryDate,
publicPassword: chats.publicPassword,
}) })
.from(messagesToFiles) .from(messagesToFiles)
.innerJoin(messages, eq(messages.id, messagesToFiles.messageId)) .innerJoin(messages, eq(messages.id, messagesToFiles.messageId))

View File

@ -847,6 +847,7 @@ export const chats = pgTable(
withTimezone: true, withTimezone: true,
mode: 'string', mode: 'string',
}), }),
publicPassword: text('public_password'),
mostRecentFileId: uuid('most_recent_file_id'), mostRecentFileId: uuid('most_recent_file_id'),
mostRecentFileType: assetTypeEnum('most_recent_file_type'), mostRecentFileType: assetTypeEnum('most_recent_file_type'),
mostRecentVersionNumber: integer('most_recent_version_number'), mostRecentVersionNumber: integer('most_recent_version_number'),

View File

@ -13,11 +13,16 @@ export const GetChatsRequestSchemaV2 = PaginatedRequestSchema;
export type GetChatsRequestV2 = z.infer<typeof GetChatsRequestSchemaV2>; export type GetChatsRequestV2 = z.infer<typeof GetChatsRequestSchemaV2>;
// Request for getting a single chat // Request for getting a single chat
export const GetChatRequestSchema = z.object({ export const GetChatRequestParamsSchema = z.object({
id: z.string(), id: z.string(),
}); });
export type GetChatRequest = z.infer<typeof GetChatRequestSchema>; export const GetChatRequestQuerySchema = z.object({
password: z.string().optional(),
});
export type GetChatRequestQuery = z.infer<typeof GetChatRequestQuerySchema>;
export type GetChatRequestParams = z.infer<typeof GetChatRequestParamsSchema>;
// Request for deleting multiple chats // Request for deleting multiple chats
export const DeleteChatsRequestSchema = z.array(z.string()); export const DeleteChatsRequestSchema = z.array(z.string());