Skip to content

Commit 8dc8414

Browse files
committed
Temp
1 parent c50fc88 commit 8dc8414

2 files changed

Lines changed: 189 additions & 7 deletions

File tree

apps/sim/app/api/templates/approved/sanitized/route.ts

Lines changed: 65 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,43 @@ export const revalidate = 0
1515
* GET /api/templates/approved/sanitized
1616
* Returns all approved templates with their sanitized JSONs, names, and descriptions
1717
* Requires internal API secret authentication via X-API-Key header
18+
*
19+
* Example usage:
20+
* curl -X GET https://your-domain.com/api/templates/approved/sanitized \
21+
* -H "X-API-Key: your_internal_api_secret"
1822
*/
1923
export async function GET(request: NextRequest) {
2024
const requestId = generateRequestId()
2125

2226
try {
27+
// Log incoming request details
28+
const url = new URL(request.url)
29+
const hasApiKey = !!request.headers.get('x-api-key')
30+
31+
logger.info(`[${requestId}] Incoming request to /api/templates/approved/sanitized`, {
32+
method: request.method,
33+
url: url.pathname,
34+
fullUrl: url.toString(),
35+
hasApiKey,
36+
userAgent: request.headers.get('user-agent'),
37+
origin: request.headers.get('origin'),
38+
})
39+
2340
// Check internal API key authentication
2441
const authResult = checkInternalApiKey(request)
2542
if (!authResult.success) {
26-
logger.warn(`[${requestId}] Unauthorized access to approved sanitized templates: ${authResult.error}`)
27-
return NextResponse.json({ error: authResult.error }, { status: 401 })
43+
logger.warn(`[${requestId}] Authentication failed for approved sanitized templates`, {
44+
error: authResult.error,
45+
hasApiKey,
46+
howToUse: 'Add header: X-API-Key: <INTERNAL_API_SECRET>',
47+
})
48+
return NextResponse.json({
49+
error: authResult.error,
50+
hint: 'Include X-API-Key header with INTERNAL_API_SECRET value'
51+
}, { status: 401 })
2852
}
2953

30-
logger.info(`[${requestId}] Fetching all approved templates with sanitized JSON`)
54+
logger.info(`[${requestId}] Authentication successful, fetching approved templates`)
3155

3256
// Fetch all approved templates
3357
const approvedTemplates = await db
@@ -37,11 +61,15 @@ export async function GET(request: NextRequest) {
3761
details: templates.details,
3862
state: templates.state,
3963
tags: templates.tags,
64+
requiredCredentials: templates.requiredCredentials,
4065
})
4166
.from(templates)
4267
.where(eq(templates.status, 'approved'))
4368

44-
logger.info(`[${requestId}] Found ${approvedTemplates.length} approved templates`)
69+
logger.info(`[${requestId}] Found ${approvedTemplates.length} approved templates`, {
70+
templateIds: approvedTemplates.map(t => t.id).slice(0, 5), // Log first 5 IDs
71+
totalCount: approvedTemplates.length,
72+
})
4573

4674
// Process each template to sanitize for copilot
4775
const sanitizedTemplates = approvedTemplates.map((template) => {
@@ -58,6 +86,7 @@ export async function GET(request: NextRequest) {
5886
name: template.name,
5987
description,
6088
tags: template.tags,
89+
requiredCredentials: template.requiredCredentials,
6190
sanitizedJson: copilotSanitized,
6291
}
6392
} catch (error) {
@@ -70,18 +99,47 @@ export async function GET(request: NextRequest) {
7099
}).filter((t): t is NonNullable<typeof t> => t !== null)
71100

72101
logger.info(
73-
`[${requestId}] Successfully sanitized ${sanitizedTemplates.length} templates for copilot`
102+
`[${requestId}] Successfully sanitized ${sanitizedTemplates.length} templates for copilot`,
103+
{
104+
totalTemplates: sanitizedTemplates.length,
105+
templateNames: sanitizedTemplates.map(t => t.name).slice(0, 5), // Log first 5 names
106+
}
74107
)
75108

76-
return NextResponse.json({
109+
const response = {
77110
templates: sanitizedTemplates,
78111
count: sanitizedTemplates.length,
112+
}
113+
114+
logger.info(`[${requestId}] Sending response`, {
115+
responseSize: JSON.stringify(response).length,
116+
templateCount: sanitizedTemplates.length,
79117
})
118+
119+
return NextResponse.json(response)
80120
} catch (error) {
81121
logger.error(`[${requestId}] Error fetching approved sanitized templates`, {
82122
error: error instanceof Error ? error.message : String(error),
123+
stack: error instanceof Error ? error.stack : undefined,
83124
})
84-
return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
125+
return NextResponse.json({
126+
error: 'Internal server error',
127+
requestId,
128+
}, { status: 500 })
85129
}
86130
}
87131

132+
// Add a helpful OPTIONS handler for CORS preflight
133+
export async function OPTIONS(request: NextRequest) {
134+
const requestId = generateRequestId()
135+
logger.info(`[${requestId}] OPTIONS request received for /api/templates/approved/sanitized`)
136+
137+
return new NextResponse(null, {
138+
status: 200,
139+
headers: {
140+
'Access-Control-Allow-Methods': 'GET, OPTIONS',
141+
'Access-Control-Allow-Headers': 'X-API-Key, Content-Type',
142+
},
143+
})
144+
}
145+
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
#!/usr/bin/env bun
2+
3+
/**
4+
* Export workflow JSON from database
5+
*
6+
* Usage:
7+
* bun apps/sim/scripts/export-workflow.ts <workflow-id>
8+
*
9+
* This script exports a workflow in the same format as the export API route.
10+
* It fetches the workflow state from normalized tables, combines it with metadata
11+
* and variables, sanitizes it, and outputs the JSON.
12+
*
13+
* Make sure DATABASE_URL or POSTGRES_URL is set in your environment.
14+
*/
15+
16+
// Suppress console logs from imported modules - only JSON should go to stdout
17+
const originalConsole = {
18+
log: console.log,
19+
warn: console.warn,
20+
error: console.error,
21+
}
22+
console.log = () => {}
23+
console.warn = () => {}
24+
console.error = () => {}
25+
26+
import { db, workflow } from '../../../packages/db/index.js'
27+
import { eq } from 'drizzle-orm'
28+
import { writeFileSync } from 'fs'
29+
import { loadWorkflowFromNormalizedTables } from '../lib/workflows/db-helpers.js'
30+
import { sanitizeForExport } from '../lib/workflows/json-sanitizer.js'
31+
32+
// ---------- CLI argument parsing ----------
33+
const args = process.argv.slice(2)
34+
const workflowId = args[0]
35+
const outputFile = args[1] // Optional output filename
36+
37+
if (!workflowId) {
38+
process.stderr.write('Usage: bun apps/sim/scripts/export-workflow.ts <workflow-id> [output-file]\n')
39+
process.stderr.write('\n')
40+
process.stderr.write('Examples:\n')
41+
process.stderr.write(' bun apps/sim/scripts/export-workflow.ts abc123\n')
42+
process.stderr.write(' bun apps/sim/scripts/export-workflow.ts abc123 workflow.json\n')
43+
process.stderr.write('\n')
44+
process.stderr.write('Make sure DATABASE_URL or POSTGRES_URL is set in your environment.\n')
45+
process.exit(1)
46+
}
47+
48+
// ---------- Main export function ----------
49+
async function exportWorkflow(workflowId: string, outputFile?: string): Promise<void> {
50+
try {
51+
// Fetch workflow metadata
52+
const [workflowData] = await db
53+
.select()
54+
.from(workflow)
55+
.where(eq(workflow.id, workflowId))
56+
.limit(1)
57+
58+
if (!workflowData) {
59+
process.stderr.write(`Error: Workflow ${workflowId} not found\n`)
60+
process.exit(1)
61+
}
62+
63+
// Load workflow from normalized tables
64+
const normalizedData = await loadWorkflowFromNormalizedTables(workflowId)
65+
66+
if (!normalizedData) {
67+
process.stderr.write(`Error: Workflow ${workflowId} has no normalized data\n`)
68+
process.exit(1)
69+
}
70+
71+
// Convert variables to array format
72+
let workflowVariables: any[] = []
73+
if (workflowData.variables && typeof workflowData.variables === 'object') {
74+
workflowVariables = Object.values(workflowData.variables).map((v: any) => ({
75+
id: v.id,
76+
name: v.name,
77+
type: v.type,
78+
value: v.value,
79+
}))
80+
}
81+
82+
// Prepare export state - match the exact format from the UI
83+
const workflowState = {
84+
blocks: normalizedData.blocks,
85+
edges: normalizedData.edges,
86+
loops: normalizedData.loops,
87+
parallels: normalizedData.parallels,
88+
metadata: {
89+
name: workflowData.name,
90+
description: workflowData.description ?? undefined,
91+
color: workflowData.color ?? undefined,
92+
exportedAt: new Date().toISOString(),
93+
},
94+
variables: workflowVariables,
95+
}
96+
97+
// Sanitize and export - this returns { version, exportedAt, state }
98+
const exportState = sanitizeForExport(workflowState)
99+
const jsonString = JSON.stringify(exportState, null, 2)
100+
101+
// Write to file or stdout
102+
if (outputFile) {
103+
writeFileSync(outputFile, jsonString, 'utf-8')
104+
process.stderr.write(`Workflow exported to ${outputFile}\n`)
105+
} else {
106+
// Output the JSON to stdout only
107+
process.stdout.write(jsonString + '\n')
108+
}
109+
} catch (error) {
110+
process.stderr.write(`Error exporting workflow: ${error}\n`)
111+
process.exit(1)
112+
}
113+
}
114+
115+
// ---------- Execute ----------
116+
exportWorkflow(workflowId, outputFile)
117+
.then(() => {
118+
process.exit(0)
119+
})
120+
.catch((error) => {
121+
process.stderr.write(`Unexpected error: ${error}\n`)
122+
process.exit(1)
123+
})
124+

0 commit comments

Comments
 (0)