code editor
This commit is contained in:
37
faas/web/package-lock.json
generated
37
faas/web/package-lock.json
generated
@ -14,9 +14,11 @@
|
||||
"@mantine/form": "^7.0.0",
|
||||
"@mantine/hooks": "^7.0.0",
|
||||
"@mantine/notifications": "^7.0.0",
|
||||
"@monaco-editor/react": "^4.7.0",
|
||||
"@tabler/icons-react": "^2.40.0",
|
||||
"axios": "^1.11.0",
|
||||
"dayjs": "^1.11.13",
|
||||
"monaco-editor": "^0.52.2",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router-dom": "^6.8.0"
|
||||
@ -801,6 +803,29 @@
|
||||
"react": "^18.x || ^19.x"
|
||||
}
|
||||
},
|
||||
"node_modules/@monaco-editor/loader": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.5.0.tgz",
|
||||
"integrity": "sha512-hKoGSM+7aAc7eRTRjpqAZucPmoNOC4UUbknb/VNoTkEIkCPhqV8LfbsgM1webRM7S/z21eHEx9Fkwx8Z/C/+Xw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"state-local": "^1.0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@monaco-editor/react": {
|
||||
"version": "4.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@monaco-editor/react/-/react-4.7.0.tgz",
|
||||
"integrity": "sha512-cyzXQCtO47ydzxpQtCGSQGOC8Gk3ZUeBXFAxD+CWXYFo5OqZyZUonFl0DwUlTyAfRHntBfw2p3w4s9R6oe1eCA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@monaco-editor/loader": "^1.5.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"monaco-editor": ">= 0.25.0 < 1",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@remix-run/router": {
|
||||
"version": "1.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.0.tgz",
|
||||
@ -3837,6 +3862,12 @@
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/monaco-editor": {
|
||||
"version": "0.52.2",
|
||||
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.52.2.tgz",
|
||||
"integrity": "sha512-GEQWEZmfkOGLdd3XK8ryrfWz3AIP8YymVXiPHEdewrUq7mh0qrKrfHLNCXcbB6sTnMLnOZ3ztSiKcciFUkIJwQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
@ -5240,6 +5271,12 @@
|
||||
"wbuf": "^1.7.3"
|
||||
}
|
||||
},
|
||||
"node_modules/state-local": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz",
|
||||
"integrity": "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/statuses": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
||||
|
||||
@ -3,18 +3,20 @@
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router-dom": "^6.8.0",
|
||||
"@mantine/code-highlight": "^7.0.0",
|
||||
"@mantine/core": "^7.0.0",
|
||||
"@mantine/hooks": "^7.0.0",
|
||||
"@mantine/notifications": "^7.0.0",
|
||||
"@mantine/dates": "^7.0.0",
|
||||
"@mantine/form": "^7.0.0",
|
||||
"@mantine/code-highlight": "^7.0.0",
|
||||
"@mantine/hooks": "^7.0.0",
|
||||
"@mantine/notifications": "^7.0.0",
|
||||
"@monaco-editor/react": "^4.7.0",
|
||||
"@tabler/icons-react": "^2.40.0",
|
||||
"axios": "^1.11.0",
|
||||
"dayjs": "^1.11.13"
|
||||
"dayjs": "^1.11.13",
|
||||
"monaco-editor": "^0.52.2",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router-dom": "^6.8.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.22.0",
|
||||
@ -36,4 +38,4 @@
|
||||
"build": "webpack --mode production",
|
||||
"dev": "webpack serve --mode development"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,7 +4,6 @@ import {
|
||||
TextInput,
|
||||
Select,
|
||||
NumberInput,
|
||||
Textarea,
|
||||
Button,
|
||||
Group,
|
||||
Stack,
|
||||
@ -12,9 +11,11 @@ import {
|
||||
Paper,
|
||||
Divider,
|
||||
JsonInput,
|
||||
Box,
|
||||
} from '@mantine/core';
|
||||
import { useForm } from '@mantine/form';
|
||||
import { notifications } from '@mantine/notifications';
|
||||
import Editor from '@monaco-editor/react';
|
||||
import { functionApi, runtimeApi } from '../services/apiService';
|
||||
import { FunctionDefinition, CreateFunctionRequest, UpdateFunctionRequest, RuntimeType } from '../types';
|
||||
|
||||
@ -41,6 +42,79 @@ export const FunctionForm: React.FC<FunctionFormProps> = ({
|
||||
'go1.20': 'golang:1.20-alpine',
|
||||
};
|
||||
|
||||
// Map runtime to Monaco editor language
|
||||
const getEditorLanguage = (runtime: string): string => {
|
||||
const languageMap: Record<string, string> = {
|
||||
'nodejs18': 'javascript',
|
||||
'python3.9': 'python',
|
||||
'go1.20': 'go',
|
||||
};
|
||||
return languageMap[runtime] || 'javascript';
|
||||
};
|
||||
|
||||
// Get default code template based on runtime
|
||||
const getDefaultCode = (runtime: string): string => {
|
||||
const templates: Record<string, string> = {
|
||||
'nodejs18': `exports.handler = async (event, context) => {
|
||||
console.log('Event:', JSON.stringify(event, null, 2));
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
body: JSON.stringify({
|
||||
message: 'Hello from Node.js!',
|
||||
timestamp: new Date().toISOString()
|
||||
})
|
||||
};
|
||||
};`,
|
||||
'python3.9': `import json
|
||||
from datetime import datetime
|
||||
|
||||
def handler(event, context):
|
||||
print('Event:', json.dumps(event, indent=2))
|
||||
|
||||
return {
|
||||
'statusCode': 200,
|
||||
'body': json.dumps({
|
||||
'message': 'Hello from Python!',
|
||||
'timestamp': datetime.now().isoformat()
|
||||
})
|
||||
}`,
|
||||
'go1.20': `package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Event map[string]interface{}
|
||||
type Response struct {
|
||||
StatusCode int \`json:"statusCode"\`
|
||||
Body string \`json:"body"\`
|
||||
}
|
||||
|
||||
func Handler(ctx context.Context, event Event) (Response, error) {
|
||||
eventJSON, _ := json.MarshalIndent(event, "", " ")
|
||||
log.Printf("Event: %s", eventJSON)
|
||||
|
||||
body := map[string]interface{}{
|
||||
"message": "Hello from Go!",
|
||||
"timestamp": time.Now().Format(time.RFC3339),
|
||||
}
|
||||
|
||||
bodyJSON, _ := json.Marshal(body)
|
||||
|
||||
return Response{
|
||||
StatusCode: 200,
|
||||
Body: string(bodyJSON),
|
||||
}, nil
|
||||
}`
|
||||
};
|
||||
return templates[runtime] || templates['nodejs18'];
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
// Fetch available runtimes from backend
|
||||
const fetchRuntimes = async () => {
|
||||
@ -90,7 +164,7 @@ export const FunctionForm: React.FC<FunctionFormProps> = ({
|
||||
runtime: 'nodejs18' as RuntimeType,
|
||||
image: DEFAULT_IMAGES['nodejs18'] || '',
|
||||
handler: 'index.handler',
|
||||
code: '',
|
||||
code: getDefaultCode('nodejs18'),
|
||||
environment: '{}',
|
||||
timeout: '30s',
|
||||
memory: 128,
|
||||
@ -110,7 +184,7 @@ export const FunctionForm: React.FC<FunctionFormProps> = ({
|
||||
runtime: 'nodejs18' as RuntimeType,
|
||||
image: DEFAULT_IMAGES['nodejs18'] || '',
|
||||
handler: 'index.handler',
|
||||
code: '',
|
||||
code: getDefaultCode('nodejs18'),
|
||||
environment: '{}',
|
||||
timeout: '30s',
|
||||
memory: 128,
|
||||
@ -136,6 +210,11 @@ export const FunctionForm: React.FC<FunctionFormProps> = ({
|
||||
form.setFieldValue('image', DEFAULT_IMAGES[runtime]);
|
||||
}
|
||||
form.setFieldValue('runtime', runtime as RuntimeType);
|
||||
|
||||
// If creating a new function and no code is set, provide default template
|
||||
if (!isEditing && runtime && (!form.values.code || form.values.code.trim() === '')) {
|
||||
form.setFieldValue('code', getDefaultCode(runtime));
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = async (values: typeof form.values) => {
|
||||
@ -288,27 +367,52 @@ export const FunctionForm: React.FC<FunctionFormProps> = ({
|
||||
{...form.getInputProps('handler')}
|
||||
/>
|
||||
|
||||
<Textarea
|
||||
label="Function Code"
|
||||
rows={16}
|
||||
resize={"vertical"}
|
||||
placeholder={`// Node.js example:
|
||||
exports.handler = async (event, context) => {
|
||||
return {
|
||||
statusCode: 200,
|
||||
body: JSON.stringify({ message: 'Hello World!' })
|
||||
};
|
||||
};
|
||||
|
||||
// Python example:
|
||||
def handler(event, context):
|
||||
return {
|
||||
'statusCode': 200,
|
||||
'body': json.dumps({'message': 'Hello World!'})
|
||||
}`}
|
||||
minRows={16}
|
||||
{...form.getInputProps('code')}
|
||||
/>
|
||||
<Box>
|
||||
<Text size="sm" fw={500} mb={5}>
|
||||
Function Code
|
||||
</Text>
|
||||
<Box
|
||||
style={{
|
||||
border: '1px solid #dee2e6',
|
||||
borderRadius: '4px',
|
||||
overflow: 'hidden'
|
||||
}}
|
||||
>
|
||||
<Editor
|
||||
height="400px"
|
||||
language={getEditorLanguage(form.values.runtime)}
|
||||
value={form.values.code}
|
||||
onChange={(value) => form.setFieldValue('code', value || '')}
|
||||
options={{
|
||||
minimap: { enabled: false },
|
||||
scrollBeyondLastLine: false,
|
||||
fontSize: 14,
|
||||
lineNumbers: 'on',
|
||||
roundedSelection: false,
|
||||
scrollbar: {
|
||||
vertical: 'visible',
|
||||
horizontal: 'visible'
|
||||
},
|
||||
automaticLayout: true,
|
||||
wordWrap: 'on',
|
||||
tabSize: 2,
|
||||
insertSpaces: true,
|
||||
folding: true,
|
||||
lineDecorationsWidth: 0,
|
||||
lineNumbersMinChars: 3,
|
||||
renderLineHighlight: 'line',
|
||||
selectOnLineNumbers: true,
|
||||
theme: 'vs-light'
|
||||
}}
|
||||
loading={<Text ta="center" p="xl">Loading editor...</Text>}
|
||||
/>
|
||||
</Box>
|
||||
{form.errors.code && (
|
||||
<Text size="xs" c="red" mt={5}>
|
||||
{form.errors.code}
|
||||
</Text>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
<JsonInput
|
||||
label="Environment Variables"
|
||||
|
||||
Reference in New Issue
Block a user