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/form": "^7.0.0",
|
||||||
"@mantine/hooks": "^7.0.0",
|
"@mantine/hooks": "^7.0.0",
|
||||||
"@mantine/notifications": "^7.0.0",
|
"@mantine/notifications": "^7.0.0",
|
||||||
|
"@monaco-editor/react": "^4.7.0",
|
||||||
"@tabler/icons-react": "^2.40.0",
|
"@tabler/icons-react": "^2.40.0",
|
||||||
"axios": "^1.11.0",
|
"axios": "^1.11.0",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
|
"monaco-editor": "^0.52.2",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-router-dom": "^6.8.0"
|
"react-router-dom": "^6.8.0"
|
||||||
@ -801,6 +803,29 @@
|
|||||||
"react": "^18.x || ^19.x"
|
"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": {
|
"node_modules/@remix-run/router": {
|
||||||
"version": "1.23.0",
|
"version": "1.23.0",
|
||||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.0.tgz",
|
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.0.tgz",
|
||||||
@ -3837,6 +3862,12 @@
|
|||||||
"node": "*"
|
"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": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
@ -5240,6 +5271,12 @@
|
|||||||
"wbuf": "^1.7.3"
|
"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": {
|
"node_modules/statuses": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
||||||
|
|||||||
@ -3,18 +3,20 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"react": "^18.2.0",
|
"@mantine/code-highlight": "^7.0.0",
|
||||||
"react-dom": "^18.2.0",
|
|
||||||
"react-router-dom": "^6.8.0",
|
|
||||||
"@mantine/core": "^7.0.0",
|
"@mantine/core": "^7.0.0",
|
||||||
"@mantine/hooks": "^7.0.0",
|
|
||||||
"@mantine/notifications": "^7.0.0",
|
|
||||||
"@mantine/dates": "^7.0.0",
|
"@mantine/dates": "^7.0.0",
|
||||||
"@mantine/form": "^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",
|
"@tabler/icons-react": "^2.40.0",
|
||||||
"axios": "^1.11.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": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.22.0",
|
"@babel/core": "^7.22.0",
|
||||||
|
|||||||
@ -4,7 +4,6 @@ import {
|
|||||||
TextInput,
|
TextInput,
|
||||||
Select,
|
Select,
|
||||||
NumberInput,
|
NumberInput,
|
||||||
Textarea,
|
|
||||||
Button,
|
Button,
|
||||||
Group,
|
Group,
|
||||||
Stack,
|
Stack,
|
||||||
@ -12,9 +11,11 @@ import {
|
|||||||
Paper,
|
Paper,
|
||||||
Divider,
|
Divider,
|
||||||
JsonInput,
|
JsonInput,
|
||||||
|
Box,
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import { useForm } from '@mantine/form';
|
import { useForm } from '@mantine/form';
|
||||||
import { notifications } from '@mantine/notifications';
|
import { notifications } from '@mantine/notifications';
|
||||||
|
import Editor from '@monaco-editor/react';
|
||||||
import { functionApi, runtimeApi } from '../services/apiService';
|
import { functionApi, runtimeApi } from '../services/apiService';
|
||||||
import { FunctionDefinition, CreateFunctionRequest, UpdateFunctionRequest, RuntimeType } from '../types';
|
import { FunctionDefinition, CreateFunctionRequest, UpdateFunctionRequest, RuntimeType } from '../types';
|
||||||
|
|
||||||
@ -41,6 +42,79 @@ export const FunctionForm: React.FC<FunctionFormProps> = ({
|
|||||||
'go1.20': 'golang:1.20-alpine',
|
'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(() => {
|
useEffect(() => {
|
||||||
// Fetch available runtimes from backend
|
// Fetch available runtimes from backend
|
||||||
const fetchRuntimes = async () => {
|
const fetchRuntimes = async () => {
|
||||||
@ -90,7 +164,7 @@ export const FunctionForm: React.FC<FunctionFormProps> = ({
|
|||||||
runtime: 'nodejs18' as RuntimeType,
|
runtime: 'nodejs18' as RuntimeType,
|
||||||
image: DEFAULT_IMAGES['nodejs18'] || '',
|
image: DEFAULT_IMAGES['nodejs18'] || '',
|
||||||
handler: 'index.handler',
|
handler: 'index.handler',
|
||||||
code: '',
|
code: getDefaultCode('nodejs18'),
|
||||||
environment: '{}',
|
environment: '{}',
|
||||||
timeout: '30s',
|
timeout: '30s',
|
||||||
memory: 128,
|
memory: 128,
|
||||||
@ -110,7 +184,7 @@ export const FunctionForm: React.FC<FunctionFormProps> = ({
|
|||||||
runtime: 'nodejs18' as RuntimeType,
|
runtime: 'nodejs18' as RuntimeType,
|
||||||
image: DEFAULT_IMAGES['nodejs18'] || '',
|
image: DEFAULT_IMAGES['nodejs18'] || '',
|
||||||
handler: 'index.handler',
|
handler: 'index.handler',
|
||||||
code: '',
|
code: getDefaultCode('nodejs18'),
|
||||||
environment: '{}',
|
environment: '{}',
|
||||||
timeout: '30s',
|
timeout: '30s',
|
||||||
memory: 128,
|
memory: 128,
|
||||||
@ -136,6 +210,11 @@ export const FunctionForm: React.FC<FunctionFormProps> = ({
|
|||||||
form.setFieldValue('image', DEFAULT_IMAGES[runtime]);
|
form.setFieldValue('image', DEFAULT_IMAGES[runtime]);
|
||||||
}
|
}
|
||||||
form.setFieldValue('runtime', runtime as RuntimeType);
|
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) => {
|
const handleSubmit = async (values: typeof form.values) => {
|
||||||
@ -288,27 +367,52 @@ export const FunctionForm: React.FC<FunctionFormProps> = ({
|
|||||||
{...form.getInputProps('handler')}
|
{...form.getInputProps('handler')}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Textarea
|
<Box>
|
||||||
label="Function Code"
|
<Text size="sm" fw={500} mb={5}>
|
||||||
rows={16}
|
Function Code
|
||||||
resize={"vertical"}
|
</Text>
|
||||||
placeholder={`// Node.js example:
|
<Box
|
||||||
exports.handler = async (event, context) => {
|
style={{
|
||||||
return {
|
border: '1px solid #dee2e6',
|
||||||
statusCode: 200,
|
borderRadius: '4px',
|
||||||
body: JSON.stringify({ message: 'Hello World!' })
|
overflow: 'hidden'
|
||||||
};
|
}}
|
||||||
};
|
>
|
||||||
|
<Editor
|
||||||
// Python example:
|
height="400px"
|
||||||
def handler(event, context):
|
language={getEditorLanguage(form.values.runtime)}
|
||||||
return {
|
value={form.values.code}
|
||||||
'statusCode': 200,
|
onChange={(value) => form.setFieldValue('code', value || '')}
|
||||||
'body': json.dumps({'message': 'Hello World!'})
|
options={{
|
||||||
}`}
|
minimap: { enabled: false },
|
||||||
minRows={16}
|
scrollBeyondLastLine: false,
|
||||||
{...form.getInputProps('code')}
|
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
|
<JsonInput
|
||||||
label="Environment Variables"
|
label="Environment Variables"
|
||||||
|
|||||||
Reference in New Issue
Block a user