@@ -5,14 +5,12 @@ import { checkInternalAuth } from '@/lib/auth/hybrid'
55import { secureFetchWithValidation } from '@/lib/core/security/input-validation.server'
66import { generateRequestId } from '@/lib/core/utils/request'
77import { RawFileInputArraySchema } from '@/lib/uploads/utils/file-schemas'
8- import { processFilesToUserFiles } from '@/lib/uploads/utils/file-utils'
9- import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
10- import type {
11- GraphApiErrorResponse ,
12- GraphChatMessage ,
13- GraphDriveItem ,
14- } from '@/tools/microsoft_teams/types'
15- import { resolveMentionsForChannel , type TeamsMention } from '@/tools/microsoft_teams/utils'
8+ import type { GraphApiErrorResponse , GraphChatMessage } from '@/tools/microsoft_teams/types'
9+ import {
10+ resolveMentionsForChannel ,
11+ type TeamsMention ,
12+ uploadFilesForTeamsMessage ,
13+ } from '@/tools/microsoft_teams/utils'
1614
1715export const dynamic = 'force-dynamic'
1816
@@ -60,130 +58,12 @@ export async function POST(request: NextRequest) {
6058 fileCount : validatedData . files ?. length || 0 ,
6159 } )
6260
63- const attachments : any [ ] = [ ]
64- const filesOutput : Array < {
65- name : string
66- mimeType : string
67- data : string
68- size : number
69- } > = [ ]
70- if ( validatedData . files && validatedData . files . length > 0 ) {
71- const rawFiles = validatedData . files
72- logger . info ( `[${ requestId } ] Processing ${ rawFiles . length } file(s) for upload to OneDrive` )
73-
74- const userFiles = processFilesToUserFiles ( rawFiles , requestId , logger )
75-
76- for ( const file of userFiles ) {
77- try {
78- // Microsoft Graph API limits direct uploads to 4MB
79- const maxSize = 4 * 1024 * 1024
80- if ( file . size > maxSize ) {
81- const sizeMB = ( file . size / ( 1024 * 1024 ) ) . toFixed ( 2 )
82- logger . error (
83- `[${ requestId } ] File ${ file . name } is ${ sizeMB } MB, exceeds 4MB limit for direct upload`
84- )
85- throw new Error (
86- `File "${ file . name } " (${ sizeMB } MB) exceeds the 4MB limit for Teams attachments. Use smaller files or upload to SharePoint/OneDrive first.`
87- )
88- }
89-
90- logger . info ( `[${ requestId } ] Uploading file to Teams: ${ file . name } (${ file . size } bytes)` )
91-
92- const buffer = await downloadFileFromStorage ( file , requestId , logger )
93- filesOutput . push ( {
94- name : file . name ,
95- mimeType : file . type || 'application/octet-stream' ,
96- data : buffer . toString ( 'base64' ) ,
97- size : buffer . length ,
98- } )
99-
100- const uploadUrl =
101- 'https://graph.microsoft.com/v1.0/me/drive/root:/TeamsAttachments/' +
102- encodeURIComponent ( file . name ) +
103- ':/content'
104-
105- logger . info ( `[${ requestId } ] Uploading to Teams: ${ uploadUrl } ` )
106-
107- const uploadResponse = await secureFetchWithValidation (
108- uploadUrl ,
109- {
110- method : 'PUT' ,
111- headers : {
112- Authorization : `Bearer ${ validatedData . accessToken } ` ,
113- 'Content-Type' : file . type || 'application/octet-stream' ,
114- } ,
115- body : buffer ,
116- } ,
117- 'uploadUrl'
118- )
119-
120- if ( ! uploadResponse . ok ) {
121- const errorData = ( await uploadResponse
122- . json ( )
123- . catch ( ( ) => ( { } ) ) ) as GraphApiErrorResponse
124- logger . error ( `[${ requestId } ] Teams upload failed:` , errorData )
125- throw new Error (
126- `Failed to upload file to Teams: ${ errorData . error ?. message || 'Unknown error' } `
127- )
128- }
129-
130- const uploadedFile = ( await uploadResponse . json ( ) ) as GraphDriveItem
131- logger . info ( `[${ requestId } ] File uploaded to Teams successfully` , {
132- id : uploadedFile . id ,
133- webUrl : uploadedFile . webUrl ,
134- } )
135-
136- const fileDetailsUrl = `https://graph.microsoft.com/v1.0/me/drive/items/${ uploadedFile . id } ?$select=id,name,webDavUrl,eTag,size`
137-
138- const fileDetailsResponse = await secureFetchWithValidation (
139- fileDetailsUrl ,
140- {
141- method : 'GET' ,
142- headers : {
143- Authorization : `Bearer ${ validatedData . accessToken } ` ,
144- } ,
145- } ,
146- 'fileDetailsUrl'
147- )
148-
149- if ( ! fileDetailsResponse . ok ) {
150- const errorData = ( await fileDetailsResponse
151- . json ( )
152- . catch ( ( ) => ( { } ) ) ) as GraphApiErrorResponse
153- logger . error ( `[${ requestId } ] Failed to get file details:` , errorData )
154- throw new Error (
155- `Failed to get file details: ${ errorData . error ?. message || 'Unknown error' } `
156- )
157- }
158-
159- const fileDetails = ( await fileDetailsResponse . json ( ) ) as GraphDriveItem
160- logger . info ( `[${ requestId } ] Got file details` , {
161- webDavUrl : fileDetails . webDavUrl ,
162- eTag : fileDetails . eTag ,
163- } )
164-
165- const attachmentId = fileDetails . eTag ?. match ( / \{ ( [ a - f 0 - 9 - ] + ) \} / i) ?. [ 1 ] || fileDetails . id
166-
167- attachments . push ( {
168- id : attachmentId ,
169- contentType : 'reference' ,
170- contentUrl : fileDetails . webDavUrl ,
171- name : file . name ,
172- } )
173-
174- logger . info ( `[${ requestId } ] Created attachment reference for ${ file . name } ` )
175- } catch ( error ) {
176- logger . error ( `[${ requestId } ] Failed to process file ${ file . name } :` , error )
177- throw new Error (
178- `Failed to process file "${ file . name } ": ${ error instanceof Error ? error . message : 'Unknown error' } `
179- )
180- }
181- }
182-
183- logger . info (
184- `[${ requestId } ] All ${ attachments . length } file(s) uploaded and attachment references created`
185- )
186- }
61+ const { attachments, filesOutput } = await uploadFilesForTeamsMessage ( {
62+ rawFiles : validatedData . files || [ ] ,
63+ accessToken : validatedData . accessToken ,
64+ requestId,
65+ logger,
66+ } )
18767
18868 let messageContent = validatedData . content
18969 let contentType : 'text' | 'html' = 'text'
0 commit comments