-
This commit is contained in:
@ -1,21 +1,21 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import {
|
import {
|
||||||
Card,
|
|
||||||
Group,
|
|
||||||
Text,
|
|
||||||
Badge,
|
|
||||||
Stack,
|
|
||||||
Table,
|
Table,
|
||||||
Button,
|
Button,
|
||||||
|
Stack,
|
||||||
|
Title,
|
||||||
|
Modal,
|
||||||
Select,
|
Select,
|
||||||
TextInput,
|
TextInput,
|
||||||
Pagination,
|
Pagination,
|
||||||
Container,
|
Group,
|
||||||
Alert,
|
|
||||||
Loader,
|
|
||||||
Code,
|
|
||||||
ActionIcon,
|
ActionIcon,
|
||||||
Modal,
|
Badge,
|
||||||
|
Card,
|
||||||
|
Text,
|
||||||
|
Loader,
|
||||||
|
Alert,
|
||||||
|
Code,
|
||||||
ScrollArea,
|
ScrollArea,
|
||||||
Flex,
|
Flex,
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
@ -183,19 +183,39 @@ const ExecutionList: React.FC = () => {
|
|||||||
|
|
||||||
if (loading && executions.length === 0) {
|
if (loading && executions.length === 0) {
|
||||||
return (
|
return (
|
||||||
<Container size="xl">
|
<Stack gap="lg">
|
||||||
<Flex justify="center" align="center" h={200}>
|
<Group justify="space-between">
|
||||||
|
<Title order={2}>Function Executions</Title>
|
||||||
|
<Button
|
||||||
|
leftSection={<IconRefresh size={16} />}
|
||||||
|
onClick={handleRefresh}
|
||||||
|
loading={loading}
|
||||||
|
>
|
||||||
|
Refresh
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
<Stack align="center" justify="center" h={200}>
|
||||||
<Loader size="lg" />
|
<Loader size="lg" />
|
||||||
</Flex>
|
<Text>Loading executions...</Text>
|
||||||
</Container>
|
</Stack>
|
||||||
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container size="xl">
|
<Stack gap="lg">
|
||||||
<Stack gap="md">
|
<Group justify="space-between">
|
||||||
<Card>
|
<Title order={2}>Function Executions</Title>
|
||||||
<Group justify="space-between" mb="md">
|
<Button
|
||||||
|
leftSection={<IconRefresh size={16} />}
|
||||||
|
onClick={handleRefresh}
|
||||||
|
loading={loading}
|
||||||
|
>
|
||||||
|
Refresh
|
||||||
|
</Button>
|
||||||
|
</Group>
|
||||||
|
|
||||||
<Group>
|
<Group>
|
||||||
<TextInput
|
<TextInput
|
||||||
placeholder="Search executions..."
|
placeholder="Search executions..."
|
||||||
@ -216,24 +236,30 @@ const ExecutionList: React.FC = () => {
|
|||||||
style={{ width: 200 }}
|
style={{ width: 200 }}
|
||||||
/>
|
/>
|
||||||
</Group>
|
</Group>
|
||||||
<Button leftSection={<IconRefresh size={16} />} onClick={handleRefresh} loading={loading}>
|
|
||||||
Refresh
|
|
||||||
</Button>
|
|
||||||
</Group>
|
|
||||||
|
|
||||||
{error && (
|
{error && (
|
||||||
<Alert color="red" mb="md">
|
<Alert color="red" title="Error">
|
||||||
{error}
|
{error}
|
||||||
</Alert>
|
</Alert>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{filteredExecutions.length === 0 ? (
|
{filteredExecutions.length === 0 ? (
|
||||||
<Text c="dimmed" ta="center" py="xl">
|
<Card shadow="sm" radius="md" withBorder p="xl">
|
||||||
|
<Stack align="center" gap="md">
|
||||||
|
<IconClock size={48} color="gray" />
|
||||||
|
<div style={{ textAlign: 'center' }}>
|
||||||
|
<Text fw={500} mb="xs">
|
||||||
No executions found
|
No executions found
|
||||||
</Text>
|
</Text>
|
||||||
|
<Text size="sm" c="dimmed">
|
||||||
|
There are no function executions matching your current filters
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
</Stack>
|
||||||
|
</Card>
|
||||||
) : (
|
) : (
|
||||||
<ScrollArea>
|
<Card shadow="sm" radius="md" withBorder>
|
||||||
<Table striped highlightOnHover>
|
<Table>
|
||||||
<Table.Thead>
|
<Table.Thead>
|
||||||
<Table.Tr>
|
<Table.Tr>
|
||||||
<Table.Th>Function</Table.Th>
|
<Table.Th>Function</Table.Th>
|
||||||
@ -250,11 +276,11 @@ const ExecutionList: React.FC = () => {
|
|||||||
<Table.Td>
|
<Table.Td>
|
||||||
<Stack gap={2}>
|
<Stack gap={2}>
|
||||||
<Text fw={500}>{getFunctionName(execution.function_id)}</Text>
|
<Text fw={500}>{getFunctionName(execution.function_id)}</Text>
|
||||||
<Code size="xs">{execution.id.slice(0, 8)}...</Code>
|
<Code style={{ fontSize: '12px' }}>{execution.id.slice(0, 8)}...</Code>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Table.Td>
|
</Table.Td>
|
||||||
<Table.Td>
|
<Table.Td>
|
||||||
<Badge color={getStatusColor(execution.status)} variant="filled">
|
<Badge color={getStatusColor(execution.status)} variant="light">
|
||||||
{execution.status}
|
{execution.status}
|
||||||
</Badge>
|
</Badge>
|
||||||
</Table.Td>
|
</Table.Td>
|
||||||
@ -300,11 +326,11 @@ const ExecutionList: React.FC = () => {
|
|||||||
))}
|
))}
|
||||||
</Table.Tbody>
|
</Table.Tbody>
|
||||||
</Table>
|
</Table>
|
||||||
</ScrollArea>
|
</Card>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{totalPages > 1 && (
|
{totalPages > 1 && (
|
||||||
<Group justify="center" mt="md">
|
<Group justify="center">
|
||||||
<Pagination
|
<Pagination
|
||||||
value={page}
|
value={page}
|
||||||
onChange={setPage}
|
onChange={setPage}
|
||||||
@ -313,8 +339,6 @@ const ExecutionList: React.FC = () => {
|
|||||||
/>
|
/>
|
||||||
</Group>
|
</Group>
|
||||||
)}
|
)}
|
||||||
</Card>
|
|
||||||
</Stack>
|
|
||||||
|
|
||||||
{/* Logs Modal */}
|
{/* Logs Modal */}
|
||||||
<Modal
|
<Modal
|
||||||
@ -378,7 +402,7 @@ const ExecutionList: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Modal>
|
</Modal>
|
||||||
</Container>
|
</Stack>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,18 +1,18 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
Table,
|
Table,
|
||||||
Badge,
|
|
||||||
Button,
|
Button,
|
||||||
Group,
|
Stack,
|
||||||
Text,
|
|
||||||
ActionIcon,
|
|
||||||
Menu,
|
|
||||||
Paper,
|
|
||||||
Title,
|
Title,
|
||||||
Alert,
|
Group,
|
||||||
|
ActionIcon,
|
||||||
|
Badge,
|
||||||
|
Card,
|
||||||
|
Text,
|
||||||
Loader,
|
Loader,
|
||||||
Center,
|
Alert,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
|
Menu,
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import {
|
import {
|
||||||
IconPlayerPlay,
|
IconPlayerPlay,
|
||||||
@ -114,40 +114,21 @@ export const FunctionList: React.FC<FunctionListProps> = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (loading) {
|
if (loading && functions.length === 0) {
|
||||||
return (
|
return (
|
||||||
<Center py={60}>
|
<Stack gap="lg">
|
||||||
<Loader size="lg" />
|
<Group justify="space-between">
|
||||||
</Center>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
return (
|
|
||||||
<Alert icon={<IconExclamationCircle size={16} />} title="Error" color="red" mb="md">
|
|
||||||
{error}
|
|
||||||
<Button variant="light" size="xs" mt="sm" onClick={loadFunctions}>
|
|
||||||
Retry
|
|
||||||
</Button>
|
|
||||||
</Alert>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Paper shadow="xs" p="md">
|
|
||||||
<Group justify="space-between" mb="md">
|
|
||||||
<Title order={2}>Functions</Title>
|
<Title order={2}>Functions</Title>
|
||||||
<Group>
|
<Group>
|
||||||
<Button
|
<Button
|
||||||
leftSection={<IconRefresh size={14} />}
|
leftSection={<IconRefresh size={16} />}
|
||||||
variant="light"
|
|
||||||
onClick={loadFunctions}
|
onClick={loadFunctions}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
>
|
>
|
||||||
Refresh
|
Refresh
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
leftSection={<IconPlus size={14} />}
|
leftSection={<IconPlus size={16} />}
|
||||||
onClick={onCreateFunction}
|
onClick={onCreateFunction}
|
||||||
>
|
>
|
||||||
Create Function
|
Create Function
|
||||||
@ -155,26 +136,67 @@ export const FunctionList: React.FC<FunctionListProps> = ({
|
|||||||
</Group>
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
{functions.length === 0 ? (
|
<Stack align="center" justify="center" h={200}>
|
||||||
<Center py={40}>
|
<Loader size="lg" />
|
||||||
<div style={{ textAlign: 'center' }}>
|
<Text>Loading functions...</Text>
|
||||||
<IconCode size={48} color="gray" />
|
</Stack>
|
||||||
<Text size="lg" mt="md" c="dimmed">
|
</Stack>
|
||||||
No functions found
|
);
|
||||||
</Text>
|
}
|
||||||
<Text size="sm" c="dimmed" mb="md">
|
|
||||||
Create your first serverless function to get started
|
return (
|
||||||
</Text>
|
<Stack gap="lg">
|
||||||
|
<Group justify="space-between">
|
||||||
|
<Title order={2}>Functions</Title>
|
||||||
|
<Group>
|
||||||
<Button
|
<Button
|
||||||
leftSection={<IconPlus size={14} />}
|
leftSection={<IconRefresh size={16} />}
|
||||||
|
onClick={loadFunctions}
|
||||||
|
loading={loading}
|
||||||
|
>
|
||||||
|
Refresh
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
leftSection={<IconPlus size={16} />}
|
||||||
onClick={onCreateFunction}
|
onClick={onCreateFunction}
|
||||||
>
|
>
|
||||||
Create Function
|
Create Function
|
||||||
</Button>
|
</Button>
|
||||||
|
</Group>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
{error && (
|
||||||
|
<Alert color="red" title="Error">
|
||||||
|
{error}
|
||||||
|
<Button variant="light" size="xs" mt="sm" onClick={loadFunctions}>
|
||||||
|
Retry
|
||||||
|
</Button>
|
||||||
|
</Alert>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{functions.length === 0 ? (
|
||||||
|
<Card shadow="sm" radius="md" withBorder p="xl">
|
||||||
|
<Stack align="center" gap="md">
|
||||||
|
<IconCode size={48} color="gray" />
|
||||||
|
<div style={{ textAlign: 'center' }}>
|
||||||
|
<Text fw={500} mb="xs">
|
||||||
|
No functions found
|
||||||
|
</Text>
|
||||||
|
<Text size="sm" c="dimmed">
|
||||||
|
Create your first serverless function to get started
|
||||||
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
</Center>
|
<Button
|
||||||
|
leftSection={<IconPlus size={16} />}
|
||||||
|
onClick={onCreateFunction}
|
||||||
|
>
|
||||||
|
Create Function
|
||||||
|
</Button>
|
||||||
|
</Stack>
|
||||||
|
</Card>
|
||||||
) : (
|
) : (
|
||||||
<Table striped>
|
<Card shadow="sm" radius="md" withBorder>
|
||||||
|
<Table>
|
||||||
<Table.Thead>
|
<Table.Thead>
|
||||||
<Table.Tr>
|
<Table.Tr>
|
||||||
<Table.Th>Name</Table.Th>
|
<Table.Th>Name</Table.Th>
|
||||||
@ -192,7 +214,7 @@ export const FunctionList: React.FC<FunctionListProps> = ({
|
|||||||
<Table.Tr key={func.id}>
|
<Table.Tr key={func.id}>
|
||||||
<Table.Td>
|
<Table.Td>
|
||||||
<Text fw={500}>{func.name}</Text>
|
<Text fw={500}>{func.name}</Text>
|
||||||
<Text size="xs" c="dimmed">{func.handler}</Text>
|
<Text size="xs" c="dimmed">{func.description || ''}</Text>
|
||||||
</Table.Td>
|
</Table.Td>
|
||||||
<Table.Td>
|
<Table.Td>
|
||||||
<Badge color={getRuntimeColor(func.runtime)} variant="light">
|
<Badge color={getRuntimeColor(func.runtime)} variant="light">
|
||||||
@ -201,33 +223,28 @@ export const FunctionList: React.FC<FunctionListProps> = ({
|
|||||||
</Table.Td>
|
</Table.Td>
|
||||||
<Table.Td>
|
<Table.Td>
|
||||||
<Text size="sm" c="dimmed">
|
<Text size="sm" c="dimmed">
|
||||||
{func.image}
|
{func.image || ''}
|
||||||
</Text>
|
</Text>
|
||||||
</Table.Td>
|
</Table.Td>
|
||||||
<Table.Td>
|
<Table.Td>
|
||||||
<Text size="sm">{func.memory} MB</Text>
|
<Text size="sm">{func.memoryLimit || 'N/A'} MB</Text>
|
||||||
</Table.Td>
|
</Table.Td>
|
||||||
<Table.Td>
|
<Table.Td>
|
||||||
<Text size="sm">{func.timeout}</Text>
|
<Text size="sm">{func.timeout || 'N/A'}</Text>
|
||||||
|
</Table.Td>
|
||||||
|
<Table.Td>
|
||||||
|
<Text size="sm">N/A</Text>
|
||||||
</Table.Td>
|
</Table.Td>
|
||||||
<Table.Td>
|
<Table.Td>
|
||||||
<Text size="sm">
|
<Text size="sm">
|
||||||
{func.owner?.name || 'Unknown'}
|
{func.createdAt ? new Date(func.createdAt).toLocaleDateString() : 'N/A'}
|
||||||
{func.owner?.type && (
|
|
||||||
<Text size="xs" c="dimmed">({func.owner.type})</Text>
|
|
||||||
)}
|
|
||||||
</Text>
|
|
||||||
</Table.Td>
|
|
||||||
<Table.Td>
|
|
||||||
<Text size="sm">
|
|
||||||
{func.created_at ? new Date(func.created_at).toLocaleDateString() : 'N/A'}
|
|
||||||
</Text>
|
</Text>
|
||||||
</Table.Td>
|
</Table.Td>
|
||||||
<Table.Td>
|
<Table.Td>
|
||||||
<Group gap="xs">
|
<Group gap="xs">
|
||||||
<Tooltip label="Execute Function">
|
<Tooltip label="Execute Function">
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
variant="light"
|
variant="subtle"
|
||||||
color="green"
|
color="green"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => onExecuteFunction(func)}
|
onClick={() => onExecuteFunction(func)}
|
||||||
@ -237,7 +254,7 @@ export const FunctionList: React.FC<FunctionListProps> = ({
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Menu position="bottom-end">
|
<Menu position="bottom-end">
|
||||||
<Menu.Target>
|
<Menu.Target>
|
||||||
<ActionIcon variant="light" size="sm">
|
<ActionIcon variant="subtle" size="sm">
|
||||||
<IconDots size={16} />
|
<IconDots size={16} />
|
||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Menu.Target>
|
</Menu.Target>
|
||||||
@ -269,7 +286,8 @@ export const FunctionList: React.FC<FunctionListProps> = ({
|
|||||||
))}
|
))}
|
||||||
</Table.Tbody>
|
</Table.Tbody>
|
||||||
</Table>
|
</Table>
|
||||||
|
</Card>
|
||||||
)}
|
)}
|
||||||
</Paper>
|
</Stack>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -75,16 +75,16 @@ services:
|
|||||||
- ./migrations:/app/migrations:ro,Z
|
- ./migrations:/app/migrations:ro,Z
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
frontend:
|
# frontend:
|
||||||
build:
|
# build:
|
||||||
context: ./kms-frontend
|
# context: ./kms-frontend
|
||||||
dockerfile: Dockerfile
|
# dockerfile: Dockerfile
|
||||||
container_name: kms-frontend
|
# container_name: kms-frontend
|
||||||
ports:
|
# ports:
|
||||||
- "3000:80"
|
# - "3000:80"
|
||||||
networks:
|
# networks:
|
||||||
- kms-network
|
# - kms-network
|
||||||
restart: unless-stopped
|
# restart: unless-stopped
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
postgres_data:
|
postgres_data:
|
||||||
|
|||||||
Reference in New Issue
Block a user