chore: import upstream snapshot with attribution
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:20:55 +08:00
commit d25d482dc2
13754 changed files with 4996608 additions and 0 deletions
@@ -0,0 +1,119 @@
import { createLogger } from '@sim/logger'
import { getErrorMessage } from '@sim/utils/errors'
import { type NextRequest, NextResponse } from 'next/server'
import { teamsDeleteChatMessageContract } from '@/lib/api/contracts/tools/microsoft'
import { parseRequest } from '@/lib/api/server'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { generateRequestId } from '@/lib/core/utils/request'
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
export const dynamic = 'force-dynamic'
const logger = createLogger('TeamsDeleteChatMessageAPI')
export const POST = withRouteHandler(async (request: NextRequest) => {
const requestId = generateRequestId()
try {
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
if (!authResult.success) {
logger.warn(`[${requestId}] Unauthorized Teams chat delete attempt: ${authResult.error}`)
return NextResponse.json(
{
success: false,
error: authResult.error || 'Authentication required',
},
{ status: 401 }
)
}
logger.info(
`[${requestId}] Authenticated Teams chat message delete request via ${authResult.authType}`,
{
userId: authResult.userId,
}
)
const parsed = await parseRequest(teamsDeleteChatMessageContract, request, {})
if (!parsed.success) return parsed.response
const validatedData = parsed.data.body
logger.info(`[${requestId}] Deleting Teams chat message`, {
chatId: validatedData.chatId,
messageId: validatedData.messageId,
})
// First, get the current user's ID (required for chat message deletion endpoint)
const meUrl = 'https://graph.microsoft.com/v1.0/me'
const meResponse = await fetch(meUrl, {
headers: {
Authorization: `Bearer ${validatedData.accessToken}`,
},
})
if (!meResponse.ok) {
const errorData = await meResponse.json().catch(() => ({}))
logger.error(`[${requestId}] Failed to get user ID:`, errorData)
return NextResponse.json(
{
success: false,
error: errorData.error?.message || 'Failed to get user information',
},
{ status: meResponse.status }
)
}
const userData = await meResponse.json()
const userId = userData.id
logger.info(`[${requestId}] Retrieved user ID: ${userId}`)
// Now perform the softDelete operation using the correct endpoint format
const deleteUrl = `https://graph.microsoft.com/v1.0/users/${encodeURIComponent(userId)}/chats/${encodeURIComponent(validatedData.chatId)}/messages/${encodeURIComponent(validatedData.messageId)}/softDelete`
const deleteResponse = await fetch(deleteUrl, {
method: 'POST',
headers: {
Authorization: `Bearer ${validatedData.accessToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({}), // softDelete requires an empty JSON body
})
if (!deleteResponse.ok) {
const errorData = await deleteResponse.json().catch(() => ({}))
logger.error(`[${requestId}] Microsoft Teams API delete error:`, errorData)
return NextResponse.json(
{
success: false,
error: errorData.error?.message || 'Failed to delete Teams message',
},
{ status: deleteResponse.status }
)
}
logger.info(`[${requestId}] Teams message deleted successfully`)
return NextResponse.json({
success: true,
output: {
deleted: true,
messageId: validatedData.messageId,
metadata: {
messageId: validatedData.messageId,
chatId: validatedData.chatId,
},
},
})
} catch (error) {
logger.error(`[${requestId}] Error deleting Teams chat message:`, error)
return NextResponse.json(
{
success: false,
error: getErrorMessage(error, 'Unknown error occurred'),
},
{ status: 500 }
)
}
})
@@ -0,0 +1,182 @@
import { createLogger } from '@sim/logger'
import { getErrorMessage } from '@sim/utils/errors'
import { type NextRequest, NextResponse } from 'next/server'
import { teamsWriteChannelContract } from '@/lib/api/contracts/tools/microsoft'
import { parseRequest } from '@/lib/api/server'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { secureFetchWithValidation } from '@/lib/core/security/input-validation.server'
import { generateRequestId } from '@/lib/core/utils/request'
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
import { docNotReadyResponse } from '@/lib/uploads/utils/servable-file-response'
import { FileAccessDeniedError } from '@/app/api/files/authorization'
import { uploadFilesForTeamsMessage } from '@/tools/microsoft_teams/server-utils'
import type { GraphApiErrorResponse, GraphChatMessage } from '@/tools/microsoft_teams/types'
import { resolveMentionsForChannel, type TeamsMention } from '@/tools/microsoft_teams/utils'
export const dynamic = 'force-dynamic'
const logger = createLogger('TeamsWriteChannelAPI')
export const POST = withRouteHandler(async (request: NextRequest) => {
const requestId = generateRequestId()
try {
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
if (!authResult.success || !authResult.userId) {
logger.warn(`[${requestId}] Unauthorized Teams channel write attempt: ${authResult.error}`)
return NextResponse.json(
{
success: false,
error: authResult.error || 'Authentication required',
},
{ status: 401 }
)
}
const userId = authResult.userId
logger.info(
`[${requestId}] Authenticated Teams channel write request via ${authResult.authType}`,
{
userId,
}
)
const parsed = await parseRequest(teamsWriteChannelContract, request, {})
if (!parsed.success) return parsed.response
const validatedData = parsed.data.body
logger.info(`[${requestId}] Sending Teams channel message`, {
teamId: validatedData.teamId,
channelId: validatedData.channelId,
hasFiles: !!(validatedData.files && validatedData.files.length > 0),
fileCount: validatedData.files?.length || 0,
})
const { attachments, filesOutput } = await uploadFilesForTeamsMessage({
rawFiles: validatedData.files || [],
accessToken: validatedData.accessToken,
requestId,
logger,
userId,
})
let messageContent = validatedData.content
let contentType: 'text' | 'html' = 'text'
const mentionEntities: TeamsMention[] = []
try {
const mentionResult = await resolveMentionsForChannel(
validatedData.content,
validatedData.teamId,
validatedData.channelId,
validatedData.accessToken
)
if (mentionResult.hasMentions) {
contentType = 'html'
messageContent = mentionResult.updatedContent
mentionEntities.push(...mentionResult.mentions)
logger.info(`[${requestId}] Resolved ${mentionResult.mentions.length} mention(s)`)
}
} catch (error) {
logger.warn(`[${requestId}] Failed to resolve mentions, continuing without them:`, error)
}
if (attachments.length > 0) {
contentType = 'html'
const attachmentTags = attachments
.map((att) => `<attachment id="${att.id}"></attachment>`)
.join(' ')
messageContent = `${messageContent}<br/>${attachmentTags}`
}
const messageBody: {
body: {
contentType: 'text' | 'html'
content: string
}
attachments?: any[]
mentions?: TeamsMention[]
} = {
body: {
contentType,
content: messageContent,
},
}
if (attachments.length > 0) {
messageBody.attachments = attachments
}
if (mentionEntities.length > 0) {
messageBody.mentions = mentionEntities
}
logger.info(`[${requestId}] Sending message to Teams channel: ${validatedData.channelId}`)
const teamsUrl = `https://graph.microsoft.com/v1.0/teams/${encodeURIComponent(validatedData.teamId)}/channels/${encodeURIComponent(validatedData.channelId)}/messages`
const teamsResponse = await secureFetchWithValidation(
teamsUrl,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${validatedData.accessToken}`,
},
body: JSON.stringify(messageBody),
},
'teamsUrl'
)
if (!teamsResponse.ok) {
const errorData = (await teamsResponse.json().catch(() => ({}))) as GraphApiErrorResponse
logger.error(`[${requestId}] Microsoft Teams API error:`, errorData)
return NextResponse.json(
{
success: false,
error: errorData.error?.message || 'Failed to send Teams channel message',
},
{ status: teamsResponse.status }
)
}
const responseData = (await teamsResponse.json()) as GraphChatMessage
logger.info(`[${requestId}] Teams channel message sent successfully`, {
messageId: responseData.id,
attachmentCount: attachments.length,
})
return NextResponse.json({
success: true,
output: {
updatedContent: true,
metadata: {
messageId: responseData.id,
teamId: responseData.channelIdentity?.teamId || validatedData.teamId,
channelId: responseData.channelIdentity?.channelId || validatedData.channelId,
content: responseData.body?.content || validatedData.content,
createdTime: responseData.createdDateTime || new Date().toISOString(),
url: responseData.webUrl || '',
attachmentCount: attachments.length,
},
files: filesOutput,
},
})
} catch (error) {
if (error instanceof FileAccessDeniedError) {
return NextResponse.json({ success: false, error: 'File not found' }, { status: 404 })
}
const notReady = docNotReadyResponse(error)
if (notReady) return notReady
logger.error(`[${requestId}] Error sending Teams channel message:`, error)
return NextResponse.json(
{
success: false,
error: getErrorMessage(error, 'Unknown error occurred'),
},
{ status: 500 }
)
}
})
@@ -0,0 +1,179 @@
import { createLogger } from '@sim/logger'
import { getErrorMessage } from '@sim/utils/errors'
import { type NextRequest, NextResponse } from 'next/server'
import { teamsWriteChatContract } from '@/lib/api/contracts/tools/microsoft'
import { parseRequest } from '@/lib/api/server'
import { checkInternalAuth } from '@/lib/auth/hybrid'
import { secureFetchWithValidation } from '@/lib/core/security/input-validation.server'
import { generateRequestId } from '@/lib/core/utils/request'
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
import { docNotReadyResponse } from '@/lib/uploads/utils/servable-file-response'
import { FileAccessDeniedError } from '@/app/api/files/authorization'
import { uploadFilesForTeamsMessage } from '@/tools/microsoft_teams/server-utils'
import type { GraphApiErrorResponse, GraphChatMessage } from '@/tools/microsoft_teams/types'
import { resolveMentionsForChat, type TeamsMention } from '@/tools/microsoft_teams/utils'
export const dynamic = 'force-dynamic'
const logger = createLogger('TeamsWriteChatAPI')
export const POST = withRouteHandler(async (request: NextRequest) => {
const requestId = generateRequestId()
try {
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
if (!authResult.success || !authResult.userId) {
logger.warn(`[${requestId}] Unauthorized Teams chat write attempt: ${authResult.error}`)
return NextResponse.json(
{
success: false,
error: authResult.error || 'Authentication required',
},
{ status: 401 }
)
}
const userId = authResult.userId
logger.info(
`[${requestId}] Authenticated Teams chat write request via ${authResult.authType}`,
{
userId,
}
)
const parsed = await parseRequest(teamsWriteChatContract, request, {})
if (!parsed.success) return parsed.response
const validatedData = parsed.data.body
logger.info(`[${requestId}] Sending Teams chat message`, {
chatId: validatedData.chatId,
hasFiles: !!(validatedData.files && validatedData.files.length > 0),
fileCount: validatedData.files?.length || 0,
})
const { attachments, filesOutput } = await uploadFilesForTeamsMessage({
rawFiles: validatedData.files || [],
accessToken: validatedData.accessToken,
requestId,
logger,
userId,
})
let messageContent = validatedData.content
let contentType: 'text' | 'html' = 'text'
const mentionEntities: TeamsMention[] = []
try {
const mentionResult = await resolveMentionsForChat(
validatedData.content,
validatedData.chatId,
validatedData.accessToken
)
if (mentionResult.hasMentions) {
contentType = 'html'
messageContent = mentionResult.updatedContent
mentionEntities.push(...mentionResult.mentions)
logger.info(`[${requestId}] Resolved ${mentionResult.mentions.length} mention(s)`)
}
} catch (error) {
logger.warn(`[${requestId}] Failed to resolve mentions, continuing without them:`, error)
}
if (attachments.length > 0) {
contentType = 'html'
const attachmentTags = attachments
.map((att) => `<attachment id="${att.id}"></attachment>`)
.join(' ')
messageContent = `${messageContent}<br/>${attachmentTags}`
}
const messageBody: {
body: {
contentType: 'text' | 'html'
content: string
}
attachments?: any[]
mentions?: TeamsMention[]
} = {
body: {
contentType,
content: messageContent,
},
}
if (attachments.length > 0) {
messageBody.attachments = attachments
}
if (mentionEntities.length > 0) {
messageBody.mentions = mentionEntities
}
logger.info(`[${requestId}] Sending message to Teams chat: ${validatedData.chatId}`)
const teamsUrl = `https://graph.microsoft.com/v1.0/chats/${encodeURIComponent(validatedData.chatId)}/messages`
const teamsResponse = await secureFetchWithValidation(
teamsUrl,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${validatedData.accessToken}`,
},
body: JSON.stringify(messageBody),
},
'teamsUrl'
)
if (!teamsResponse.ok) {
const errorData = (await teamsResponse.json().catch(() => ({}))) as GraphApiErrorResponse
logger.error(`[${requestId}] Microsoft Teams API error:`, errorData)
return NextResponse.json(
{
success: false,
error: errorData.error?.message || 'Failed to send Teams message',
},
{ status: teamsResponse.status }
)
}
const responseData = (await teamsResponse.json()) as GraphChatMessage
logger.info(`[${requestId}] Teams message sent successfully`, {
messageId: responseData.id,
attachmentCount: attachments.length,
})
return NextResponse.json({
success: true,
output: {
updatedContent: true,
metadata: {
messageId: responseData.id,
chatId: responseData.chatId || validatedData.chatId,
content: responseData.body?.content || validatedData.content,
createdTime: responseData.createdDateTime || new Date().toISOString(),
url: responseData.webUrl || '',
attachmentCount: attachments.length,
},
files: filesOutput,
},
})
} catch (error) {
if (error instanceof FileAccessDeniedError) {
return NextResponse.json({ success: false, error: 'File not found' }, { status: 404 })
}
const notReady = docNotReadyResponse(error)
if (notReady) return notReady
logger.error(`[${requestId}] Error sending Teams chat message:`, error)
return NextResponse.json(
{
success: false,
error: getErrorMessage(error, 'Unknown error occurred'),
},
{ status: 500 }
)
}
})