fix circular impport

This commit is contained in:
Vikhyath Mondreti
2026-02-03 00:52:12 -08:00
parent 7570e509ff
commit 1c857cdcda
4 changed files with 170 additions and 174 deletions

View File

@@ -5,12 +5,9 @@ import { checkInternalAuth } from '@/lib/auth/hybrid'
import { secureFetchWithValidation } from '@/lib/core/security/input-validation.server'
import { generateRequestId } from '@/lib/core/utils/request'
import { RawFileInputArraySchema } from '@/lib/uploads/utils/file-schemas'
import { uploadFilesForTeamsMessage } from '@/tools/microsoft_teams/server-utils'
import type { GraphApiErrorResponse, GraphChatMessage } from '@/tools/microsoft_teams/types'
import {
resolveMentionsForChannel,
type TeamsMention,
uploadFilesForTeamsMessage,
} from '@/tools/microsoft_teams/utils'
import { resolveMentionsForChannel, type TeamsMention } from '@/tools/microsoft_teams/utils'
export const dynamic = 'force-dynamic'

View File

@@ -5,12 +5,9 @@ import { checkInternalAuth } from '@/lib/auth/hybrid'
import { secureFetchWithValidation } from '@/lib/core/security/input-validation.server'
import { generateRequestId } from '@/lib/core/utils/request'
import { RawFileInputArraySchema } from '@/lib/uploads/utils/file-schemas'
import { uploadFilesForTeamsMessage } from '@/tools/microsoft_teams/server-utils'
import type { GraphApiErrorResponse, GraphChatMessage } from '@/tools/microsoft_teams/types'
import {
resolveMentionsForChat,
type TeamsMention,
uploadFilesForTeamsMessage,
} from '@/tools/microsoft_teams/utils'
import { resolveMentionsForChat, type TeamsMention } from '@/tools/microsoft_teams/utils'
export const dynamic = 'force-dynamic'

View File

@@ -0,0 +1,165 @@
/**
* Server-side utilities for Microsoft Teams integration.
* This file contains functions that require server-side dependencies and should
* only be imported by API routes, NOT by tool definitions (to avoid circular imports).
*/
import type { Logger } from '@sim/logger'
import { secureFetchWithValidation } from '@/lib/core/security/input-validation.server'
import { processFilesToUserFiles, type RawFileInput } from '@/lib/uploads/utils/file-utils'
import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
import type { UserFile } from '@/executor/types'
import type { GraphApiErrorResponse, GraphDriveItem } from '@/tools/microsoft_teams/types'
/** Maximum file size for Teams direct upload (4MB) */
const MAX_TEAMS_FILE_SIZE = 4 * 1024 * 1024
/** Output format for uploaded files */
export interface TeamsFileOutput {
name: string
mimeType: string
data: string
size: number
}
/** Attachment reference for Teams message */
export interface TeamsAttachmentRef {
id: string
contentType: 'reference'
contentUrl: string
name: string
}
/** Result from processing and uploading files for Teams */
export interface TeamsFileUploadResult {
attachments: TeamsAttachmentRef[]
filesOutput: TeamsFileOutput[]
}
/**
* Process and upload files to OneDrive for Teams message attachments.
* Handles size validation, downloading from storage, uploading to OneDrive,
* and creating attachment references.
*/
export async function uploadFilesForTeamsMessage(params: {
rawFiles: RawFileInput[]
accessToken: string
requestId: string
logger: Logger
}): Promise<TeamsFileUploadResult> {
const { rawFiles, accessToken, requestId, logger: log } = params
const attachments: TeamsAttachmentRef[] = []
const filesOutput: TeamsFileOutput[] = []
if (!rawFiles || rawFiles.length === 0) {
return { attachments, filesOutput }
}
log.info(`[${requestId}] Processing ${rawFiles.length} file(s) for upload to OneDrive`)
const userFiles = processFilesToUserFiles(rawFiles, requestId, log) as UserFile[]
for (const file of userFiles) {
// Check size limit
if (file.size > MAX_TEAMS_FILE_SIZE) {
const sizeMB = (file.size / (1024 * 1024)).toFixed(2)
log.error(
`[${requestId}] File ${file.name} is ${sizeMB}MB, exceeds 4MB limit for direct upload`
)
throw new Error(
`File "${file.name}" (${sizeMB}MB) exceeds the 4MB limit for Teams attachments. Use smaller files or upload to SharePoint/OneDrive first.`
)
}
log.info(`[${requestId}] Uploading file to Teams: ${file.name} (${file.size} bytes)`)
// Download file from storage
const buffer = await downloadFileFromStorage(file, requestId, log)
filesOutput.push({
name: file.name,
mimeType: file.type || 'application/octet-stream',
data: buffer.toString('base64'),
size: buffer.length,
})
// Upload to OneDrive
const uploadUrl =
'https://graph.microsoft.com/v1.0/me/drive/root:/TeamsAttachments/' +
encodeURIComponent(file.name) +
':/content'
log.info(`[${requestId}] Uploading to OneDrive: ${uploadUrl}`)
const uploadResponse = await secureFetchWithValidation(
uploadUrl,
{
method: 'PUT',
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': file.type || 'application/octet-stream',
},
body: buffer,
},
'uploadUrl'
)
if (!uploadResponse.ok) {
const errorData = (await uploadResponse.json().catch(() => ({}))) as GraphApiErrorResponse
log.error(`[${requestId}] Teams upload failed:`, errorData)
throw new Error(
`Failed to upload file to Teams: ${errorData.error?.message || 'Unknown error'}`
)
}
const uploadedFile = (await uploadResponse.json()) as GraphDriveItem
log.info(`[${requestId}] File uploaded to OneDrive successfully`, {
id: uploadedFile.id,
webUrl: uploadedFile.webUrl,
})
// Get file details for attachment reference
const fileDetailsUrl = `https://graph.microsoft.com/v1.0/me/drive/items/${uploadedFile.id}?$select=id,name,webDavUrl,eTag,size`
const fileDetailsResponse = await secureFetchWithValidation(
fileDetailsUrl,
{
method: 'GET',
headers: {
Authorization: `Bearer ${accessToken}`,
},
},
'fileDetailsUrl'
)
if (!fileDetailsResponse.ok) {
const errorData = (await fileDetailsResponse
.json()
.catch(() => ({}))) as GraphApiErrorResponse
log.error(`[${requestId}] Failed to get file details:`, errorData)
throw new Error(`Failed to get file details: ${errorData.error?.message || 'Unknown error'}`)
}
const fileDetails = (await fileDetailsResponse.json()) as GraphDriveItem
log.info(`[${requestId}] Got file details`, {
webDavUrl: fileDetails.webDavUrl,
eTag: fileDetails.eTag,
})
// Create attachment reference
const attachmentId = fileDetails.eTag?.match(/\{([a-f0-9-]+)\}/i)?.[1] || fileDetails.id
attachments.push({
id: attachmentId,
contentType: 'reference',
contentUrl: fileDetails.webDavUrl!,
name: file.name,
})
log.info(`[${requestId}] Created attachment reference for ${file.name}`)
}
log.info(
`[${requestId}] All ${attachments.length} file(s) uploaded and attachment references created`
)
return { attachments, filesOutput }
}

View File

@@ -1,172 +1,9 @@
import type { Logger } from '@sim/logger'
import { createLogger } from '@sim/logger'
import { secureFetchWithValidation } from '@/lib/core/security/input-validation.server'
import { processFilesToUserFiles, type RawFileInput } from '@/lib/uploads/utils/file-utils'
import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
import type { UserFile } from '@/executor/types'
import type {
GraphApiErrorResponse,
GraphDriveItem,
MicrosoftTeamsAttachment,
} from '@/tools/microsoft_teams/types'
import type { MicrosoftTeamsAttachment } from '@/tools/microsoft_teams/types'
import type { ToolFileData } from '@/tools/types'
const logger = createLogger('MicrosoftTeamsUtils')
/** Maximum file size for Teams direct upload (4MB) */
const MAX_TEAMS_FILE_SIZE = 4 * 1024 * 1024
/** Output format for uploaded files */
export interface TeamsFileOutput {
name: string
mimeType: string
data: string
size: number
}
/** Attachment reference for Teams message */
export interface TeamsAttachmentRef {
id: string
contentType: 'reference'
contentUrl: string
name: string
}
/** Result from processing and uploading files for Teams */
export interface TeamsFileUploadResult {
attachments: TeamsAttachmentRef[]
filesOutput: TeamsFileOutput[]
}
/**
* Process and upload files to OneDrive for Teams message attachments.
* Handles size validation, downloading from storage, uploading to OneDrive,
* and creating attachment references.
*/
export async function uploadFilesForTeamsMessage(params: {
rawFiles: RawFileInput[]
accessToken: string
requestId: string
logger: Logger
}): Promise<TeamsFileUploadResult> {
const { rawFiles, accessToken, requestId, logger: log } = params
const attachments: TeamsAttachmentRef[] = []
const filesOutput: TeamsFileOutput[] = []
if (!rawFiles || rawFiles.length === 0) {
return { attachments, filesOutput }
}
log.info(`[${requestId}] Processing ${rawFiles.length} file(s) for upload to OneDrive`)
const userFiles = processFilesToUserFiles(rawFiles, requestId, log) as UserFile[]
for (const file of userFiles) {
// Check size limit
if (file.size > MAX_TEAMS_FILE_SIZE) {
const sizeMB = (file.size / (1024 * 1024)).toFixed(2)
log.error(
`[${requestId}] File ${file.name} is ${sizeMB}MB, exceeds 4MB limit for direct upload`
)
throw new Error(
`File "${file.name}" (${sizeMB}MB) exceeds the 4MB limit for Teams attachments. Use smaller files or upload to SharePoint/OneDrive first.`
)
}
log.info(`[${requestId}] Uploading file to Teams: ${file.name} (${file.size} bytes)`)
// Download file from storage
const buffer = await downloadFileFromStorage(file, requestId, log)
filesOutput.push({
name: file.name,
mimeType: file.type || 'application/octet-stream',
data: buffer.toString('base64'),
size: buffer.length,
})
// Upload to OneDrive
const uploadUrl =
'https://graph.microsoft.com/v1.0/me/drive/root:/TeamsAttachments/' +
encodeURIComponent(file.name) +
':/content'
log.info(`[${requestId}] Uploading to OneDrive: ${uploadUrl}`)
const uploadResponse = await secureFetchWithValidation(
uploadUrl,
{
method: 'PUT',
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': file.type || 'application/octet-stream',
},
body: buffer,
},
'uploadUrl'
)
if (!uploadResponse.ok) {
const errorData = (await uploadResponse.json().catch(() => ({}))) as GraphApiErrorResponse
log.error(`[${requestId}] Teams upload failed:`, errorData)
throw new Error(
`Failed to upload file to Teams: ${errorData.error?.message || 'Unknown error'}`
)
}
const uploadedFile = (await uploadResponse.json()) as GraphDriveItem
log.info(`[${requestId}] File uploaded to OneDrive successfully`, {
id: uploadedFile.id,
webUrl: uploadedFile.webUrl,
})
// Get file details for attachment reference
const fileDetailsUrl = `https://graph.microsoft.com/v1.0/me/drive/items/${uploadedFile.id}?$select=id,name,webDavUrl,eTag,size`
const fileDetailsResponse = await secureFetchWithValidation(
fileDetailsUrl,
{
method: 'GET',
headers: {
Authorization: `Bearer ${accessToken}`,
},
},
'fileDetailsUrl'
)
if (!fileDetailsResponse.ok) {
const errorData = (await fileDetailsResponse
.json()
.catch(() => ({}))) as GraphApiErrorResponse
log.error(`[${requestId}] Failed to get file details:`, errorData)
throw new Error(`Failed to get file details: ${errorData.error?.message || 'Unknown error'}`)
}
const fileDetails = (await fileDetailsResponse.json()) as GraphDriveItem
log.info(`[${requestId}] Got file details`, {
webDavUrl: fileDetails.webDavUrl,
eTag: fileDetails.eTag,
})
// Create attachment reference
const attachmentId = fileDetails.eTag?.match(/\{([a-f0-9-]+)\}/i)?.[1] || fileDetails.id
attachments.push({
id: attachmentId,
contentType: 'reference',
contentUrl: fileDetails.webDavUrl!,
name: file.name,
})
log.info(`[${requestId}] Created attachment reference for ${file.name}`)
}
log.info(
`[${requestId}] All ${attachments.length} file(s) uploaded and attachment references created`
)
return { attachments, filesOutput }
}
interface ParsedMention {
name: string
fullTag: string