mirror of
https://github.com/dstotijn/hetty.git
synced 2025-07-01 18:47:29 -04:00
Replace SQLite with BadgerDB
This commit is contained in:
@ -24,9 +24,19 @@ const useStyles = makeStyles((theme: Theme) =>
|
||||
})
|
||||
);
|
||||
|
||||
const CREATE_PROJECT = gql`
|
||||
mutation CreateProject($name: String!) {
|
||||
createProject(name: $name) {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const OPEN_PROJECT = gql`
|
||||
mutation OpenProject($name: String!) {
|
||||
openProject(name: $name) {
|
||||
mutation OpenProject($id: ID!) {
|
||||
openProject(id: $id) {
|
||||
id
|
||||
name
|
||||
isActive
|
||||
}
|
||||
@ -37,8 +47,15 @@ function NewProject(): JSX.Element {
|
||||
const classes = useStyles();
|
||||
const [input, setInput] = useState(null);
|
||||
|
||||
const [openProject, { error, loading }] = useMutation(OPEN_PROJECT, {
|
||||
onError: () => {},
|
||||
const [createProject, { error: createProjErr, loading: createProjLoading }] = useMutation(CREATE_PROJECT, {
|
||||
onError: () => { },
|
||||
onCompleted(data) {
|
||||
input.value = "";
|
||||
openProject({ variables: { id: data.createProject.id } });
|
||||
},
|
||||
});
|
||||
const [openProject, { error: openProjErr, loading: openProjLoading }] = useMutation(OPEN_PROJECT, {
|
||||
onError: () => { },
|
||||
onCompleted() {
|
||||
input.value = "";
|
||||
},
|
||||
@ -47,10 +64,11 @@ function NewProject(): JSX.Element {
|
||||
fields: {
|
||||
activeProject() {
|
||||
const activeProjRef = cache.writeFragment({
|
||||
id: openProject.name,
|
||||
id: openProject.id,
|
||||
data: openProject,
|
||||
fragment: gql`
|
||||
fragment ActiveProject on Project {
|
||||
id
|
||||
name
|
||||
isActive
|
||||
type
|
||||
@ -61,10 +79,11 @@ function NewProject(): JSX.Element {
|
||||
},
|
||||
projects(_, { DELETE }) {
|
||||
cache.writeFragment({
|
||||
id: openProject.name,
|
||||
id: openProject.id,
|
||||
data: openProject,
|
||||
fragment: gql`
|
||||
fragment OpenProject on Project {
|
||||
id
|
||||
name
|
||||
isActive
|
||||
type
|
||||
@ -78,9 +97,9 @@ function NewProject(): JSX.Element {
|
||||
},
|
||||
});
|
||||
|
||||
const handleNewProjectForm = (e: React.SyntheticEvent) => {
|
||||
const handleCreateAndOpenProjectForm = (e: React.SyntheticEvent) => {
|
||||
e.preventDefault();
|
||||
openProject({ variables: { name: input.value } });
|
||||
createProject({ variables: { name: input.value } });
|
||||
};
|
||||
|
||||
return (
|
||||
@ -88,7 +107,7 @@ function NewProject(): JSX.Element {
|
||||
<Box mb={3}>
|
||||
<Typography variant="h6">New project</Typography>
|
||||
</Box>
|
||||
<form onSubmit={handleNewProjectForm} autoComplete="off">
|
||||
<form onSubmit={handleCreateAndOpenProjectForm} autoComplete="off">
|
||||
<TextField
|
||||
className={classes.projectName}
|
||||
color="secondary"
|
||||
@ -100,8 +119,8 @@ function NewProject(): JSX.Element {
|
||||
}}
|
||||
label="Project name"
|
||||
placeholder="Project name…"
|
||||
error={Boolean(error)}
|
||||
helperText={error && error.message}
|
||||
error={Boolean(createProjErr || openProjErr)}
|
||||
helperText={createProjErr && createProjErr.message || openProjErr && openProjErr.message}
|
||||
/>
|
||||
<Button
|
||||
className={classes.button}
|
||||
@ -109,8 +128,8 @@ function NewProject(): JSX.Element {
|
||||
variant="contained"
|
||||
color="secondary"
|
||||
size="large"
|
||||
disabled={loading}
|
||||
startIcon={loading ? <CircularProgress size={22} /> : <AddIcon />}
|
||||
disabled={createProjLoading || openProjLoading}
|
||||
startIcon={createProjLoading || openProjLoading ? <CircularProgress size={22} /> : <AddIcon />}
|
||||
>
|
||||
Create & open project
|
||||
</Button>
|
||||
|
@ -48,6 +48,7 @@ const useStyles = makeStyles((theme: Theme) =>
|
||||
const PROJECTS = gql`
|
||||
query Projects {
|
||||
projects {
|
||||
id
|
||||
name
|
||||
isActive
|
||||
}
|
||||
@ -55,8 +56,9 @@ const PROJECTS = gql`
|
||||
`;
|
||||
|
||||
const OPEN_PROJECT = gql`
|
||||
mutation OpenProject($name: String!) {
|
||||
openProject(name: $name) {
|
||||
mutation OpenProject($id: ID!) {
|
||||
openProject(id: $id) {
|
||||
id
|
||||
name
|
||||
isActive
|
||||
}
|
||||
@ -72,8 +74,8 @@ const CLOSE_PROJECT = gql`
|
||||
`;
|
||||
|
||||
const DELETE_PROJECT = gql`
|
||||
mutation DeleteProject($name: String!) {
|
||||
deleteProject(name: $name) {
|
||||
mutation DeleteProject($id: ID!) {
|
||||
deleteProject(id: $id) {
|
||||
success
|
||||
}
|
||||
}
|
||||
@ -89,7 +91,7 @@ function ProjectList(): JSX.Element {
|
||||
{ error: openProjErr, loading: openProjLoading },
|
||||
] = useMutation(OPEN_PROJECT, {
|
||||
errorPolicy: "all",
|
||||
onError: () => {},
|
||||
onError: () => { },
|
||||
update(cache, { data: { openProject } }) {
|
||||
cache.modify({
|
||||
fields: {
|
||||
@ -98,6 +100,7 @@ function ProjectList(): JSX.Element {
|
||||
data: openProject,
|
||||
fragment: gql`
|
||||
fragment ActiveProject on Project {
|
||||
id
|
||||
name
|
||||
isActive
|
||||
type
|
||||
@ -108,10 +111,11 @@ function ProjectList(): JSX.Element {
|
||||
},
|
||||
projects(_, { DELETE }) {
|
||||
cache.writeFragment({
|
||||
id: openProject.name,
|
||||
id: openProject.id,
|
||||
data: openProject,
|
||||
fragment: gql`
|
||||
fragment OpenProject on Project {
|
||||
id
|
||||
name
|
||||
isActive
|
||||
type
|
||||
@ -129,7 +133,7 @@ function ProjectList(): JSX.Element {
|
||||
});
|
||||
const [closeProject, { error: closeProjErr }] = useMutation(CLOSE_PROJECT, {
|
||||
errorPolicy: "all",
|
||||
onError: () => {},
|
||||
onError: () => { },
|
||||
update(cache) {
|
||||
cache.modify({
|
||||
fields: {
|
||||
@ -151,7 +155,7 @@ function ProjectList(): JSX.Element {
|
||||
{ loading: deleteProjLoading, error: deleteProjErr },
|
||||
] = useMutation(DELETE_PROJECT, {
|
||||
errorPolicy: "all",
|
||||
onError: () => {},
|
||||
onError: () => { },
|
||||
update(cache) {
|
||||
cache.modify({
|
||||
fields: {
|
||||
@ -165,14 +169,14 @@ function ProjectList(): JSX.Element {
|
||||
},
|
||||
});
|
||||
|
||||
const [deleteProjName, setDeleteProjName] = useState(null);
|
||||
const [deleteProj, setDeleteProj] = useState(null);
|
||||
const [deleteDiagOpen, setDeleteDiagOpen] = useState(false);
|
||||
const handleDeleteButtonClick = (name: string) => {
|
||||
setDeleteProjName(name);
|
||||
const handleDeleteButtonClick = (project: any) => {
|
||||
setDeleteProj(project);
|
||||
setDeleteDiagOpen(true);
|
||||
};
|
||||
const handleDeleteConfirm = () => {
|
||||
deleteProject({ variables: { name: deleteProjName } });
|
||||
deleteProject({ variables: { id: deleteProj.id } });
|
||||
};
|
||||
const handleDeleteCancel = () => {
|
||||
setDeleteDiagOpen(false);
|
||||
@ -190,7 +194,7 @@ function ProjectList(): JSX.Element {
|
||||
<div>
|
||||
<Dialog open={deleteDiagOpen} onClose={handleDeleteCancel}>
|
||||
<DialogTitle>
|
||||
Delete project “<strong>{deleteProjName}</strong>”?
|
||||
Delete project “<strong>{deleteProj?.name}</strong>”?
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText>
|
||||
@ -223,7 +227,7 @@ function ProjectList(): JSX.Element {
|
||||
onClose={handleCloseDeleteNotif}
|
||||
>
|
||||
<Alert onClose={handleCloseDeleteNotif} severity="info">
|
||||
Project <strong>{deleteProjName}</strong> was deleted.
|
||||
Project <strong>{deleteProj?.name}</strong> was deleted.
|
||||
</Alert>
|
||||
</Snackbar>
|
||||
|
||||
@ -253,7 +257,7 @@ function ProjectList(): JSX.Element {
|
||||
{projData?.projects.length > 0 && (
|
||||
<List className={classes.projectsList}>
|
||||
{projData.projects.map((project) => (
|
||||
<ListItem key={project.name}>
|
||||
<ListItem key={project.id}>
|
||||
<ListItemAvatar>
|
||||
<Avatar
|
||||
className={
|
||||
@ -281,7 +285,7 @@ function ProjectList(): JSX.Element {
|
||||
disabled={openProjLoading || projLoading}
|
||||
onClick={() =>
|
||||
openProject({
|
||||
variables: { name: project.name },
|
||||
variables: { id: project.id },
|
||||
})
|
||||
}
|
||||
>
|
||||
@ -293,7 +297,7 @@ function ProjectList(): JSX.Element {
|
||||
<Tooltip title="Delete project">
|
||||
<span>
|
||||
<IconButton
|
||||
onClick={() => handleDeleteButtonClick(project.name)}
|
||||
onClick={() => handleDeleteButtonClick(project)}
|
||||
disabled={project.isActive}
|
||||
>
|
||||
<DeleteIcon />
|
||||
|
@ -32,7 +32,7 @@ const HTTP_REQUEST_LOG = gql`
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
requestId: number;
|
||||
requestId: string;
|
||||
}
|
||||
|
||||
function LogDetail({ requestId: id }: Props): JSX.Element {
|
||||
|
@ -15,12 +15,10 @@ import { useHttpRequestLogs } from "./hooks/useHttpRequestLogs";
|
||||
|
||||
function LogsOverview(): JSX.Element {
|
||||
const router = useRouter();
|
||||
const detailReqLogId =
|
||||
router.query.id && parseInt(router.query.id as string, 10);
|
||||
|
||||
const detailReqLogId = router.query.id as string | undefined;
|
||||
const { loading, error, data } = useHttpRequestLogs();
|
||||
|
||||
const handleLogClick = (reqId: number) => {
|
||||
const handleLogClick = (reqId: string) => {
|
||||
router.push("/proxy/logs?id=" + reqId, undefined, {
|
||||
shallow: false,
|
||||
});
|
||||
|
@ -31,8 +31,8 @@ const useStyles = makeStyles((theme: Theme) =>
|
||||
|
||||
interface Props {
|
||||
logs: Array<any>;
|
||||
selectedReqLogId?: number;
|
||||
onLogClick(requestId: number): void;
|
||||
selectedReqLogId?: string;
|
||||
onLogClick(requestId: string): void;
|
||||
theme: Theme;
|
||||
}
|
||||
|
||||
@ -63,8 +63,8 @@ function RequestList({
|
||||
|
||||
interface RequestListTableProps {
|
||||
logs?: any;
|
||||
selectedReqLogId?: number;
|
||||
onLogClick(requestId: number): void;
|
||||
selectedReqLogId?: string;
|
||||
onLogClick(requestId: string): void;
|
||||
theme: Theme;
|
||||
}
|
||||
|
||||
|
@ -9,13 +9,7 @@ function createApolloClient() {
|
||||
link: new HttpLink({
|
||||
uri: "/api/graphql/",
|
||||
}),
|
||||
cache: new InMemoryCache({
|
||||
typePolicies: {
|
||||
Project: {
|
||||
keyFields: ["name"],
|
||||
},
|
||||
},
|
||||
}),
|
||||
cache: new InMemoryCache(),
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,30 +1,17 @@
|
||||
import {
|
||||
Avatar,
|
||||
Box,
|
||||
Button,
|
||||
CircularProgress,
|
||||
createStyles,
|
||||
List,
|
||||
ListItem,
|
||||
ListItemAvatar,
|
||||
ListItemText,
|
||||
makeStyles,
|
||||
TextField,
|
||||
Theme,
|
||||
Typography,
|
||||
} from "@material-ui/core";
|
||||
import AddIcon from "@material-ui/icons/Add";
|
||||
import FolderIcon from "@material-ui/icons/Folder";
|
||||
import DescriptionIcon from "@material-ui/icons/Description";
|
||||
import PlayArrowIcon from "@material-ui/icons/PlayArrow";
|
||||
import Link from "next/link";
|
||||
|
||||
import { useState } from "react";
|
||||
import { gql, useMutation, useQuery } from "@apollo/client";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
import Layout, { Page } from "../components/Layout";
|
||||
import { Alert } from "@material-ui/lab";
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
@ -37,107 +24,14 @@ const useStyles = makeStyles((theme: Theme) =>
|
||||
lineHeight: 2,
|
||||
marginBottom: theme.spacing(5),
|
||||
},
|
||||
projectName: {
|
||||
marginTop: -6,
|
||||
marginRight: theme.spacing(2),
|
||||
},
|
||||
button: {
|
||||
marginRight: theme.spacing(2),
|
||||
},
|
||||
activeProject: {
|
||||
color: theme.palette.getContrastText(theme.palette.secondary.main),
|
||||
backgroundColor: theme.palette.secondary.main,
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
const ACTIVE_PROJECT = gql`
|
||||
query ActiveProject {
|
||||
activeProject {
|
||||
name
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const OPEN_PROJECT = gql`
|
||||
mutation OpenProject($name: String!) {
|
||||
openProject(name: $name) {
|
||||
name
|
||||
isActive
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
function Index(): JSX.Element {
|
||||
const classes = useStyles();
|
||||
const router = useRouter();
|
||||
const [input, setInput] = useState(null);
|
||||
const { error: activeProjErr, data: activeProjData } = useQuery(
|
||||
ACTIVE_PROJECT,
|
||||
{
|
||||
pollInterval: 1000,
|
||||
}
|
||||
);
|
||||
const [
|
||||
openProject,
|
||||
{ error: openProjErr, data: openProjData, loading: openProjLoading },
|
||||
] = useMutation(OPEN_PROJECT, {
|
||||
onError: () => {},
|
||||
onCompleted({ openProject }) {
|
||||
if (openProject) {
|
||||
router.push("/get-started");
|
||||
}
|
||||
},
|
||||
update(cache, { data: { openProject } }) {
|
||||
cache.modify({
|
||||
fields: {
|
||||
activeProject() {
|
||||
const activeProjRef = cache.writeFragment({
|
||||
id: openProject.name,
|
||||
data: openProject,
|
||||
fragment: gql`
|
||||
fragment ActiveProject on Project {
|
||||
name
|
||||
isActive
|
||||
type
|
||||
}
|
||||
`,
|
||||
});
|
||||
return activeProjRef;
|
||||
},
|
||||
projects(_, { DELETE }) {
|
||||
cache.writeFragment({
|
||||
id: openProject.name,
|
||||
data: openProject,
|
||||
fragment: gql`
|
||||
fragment OpenProject on Project {
|
||||
name
|
||||
isActive
|
||||
type
|
||||
}
|
||||
`,
|
||||
});
|
||||
return DELETE;
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const handleForm = (e: React.SyntheticEvent) => {
|
||||
e.preventDefault();
|
||||
openProject({ variables: { name: input.value } });
|
||||
};
|
||||
|
||||
if (activeProjErr) {
|
||||
return (
|
||||
<Layout page={Page.Home} title="">
|
||||
<Alert severity="error">
|
||||
Error fetching active project: {activeProjErr.message}
|
||||
</Alert>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Layout page={Page.Home} title="">
|
||||
@ -157,97 +51,18 @@ function Index(): JSX.Element {
|
||||
<code>:8080</code>…
|
||||
</Typography>
|
||||
|
||||
{activeProjData?.activeProject?.name ? (
|
||||
<div>
|
||||
<Box mb={1}>
|
||||
<Typography variant="h6">Active project:</Typography>
|
||||
</Box>
|
||||
<Box ml={-2} mb={2}>
|
||||
<List>
|
||||
<ListItem>
|
||||
<ListItemAvatar>
|
||||
<Avatar className={classes.activeProject}>
|
||||
<DescriptionIcon />
|
||||
</Avatar>
|
||||
</ListItemAvatar>
|
||||
<ListItemText primary={activeProjData.activeProject.name} />
|
||||
</ListItem>
|
||||
</List>
|
||||
</Box>
|
||||
<div>
|
||||
<Link href="/get-started" passHref>
|
||||
<Button
|
||||
className={classes.button}
|
||||
variant="outlined"
|
||||
component="a"
|
||||
color="secondary"
|
||||
size="large"
|
||||
startIcon={<PlayArrowIcon />}
|
||||
>
|
||||
Get started
|
||||
</Button>
|
||||
</Link>
|
||||
<Link href="/projects" passHref>
|
||||
<Button
|
||||
className={classes.button}
|
||||
variant="outlined"
|
||||
component="a"
|
||||
size="large"
|
||||
startIcon={<FolderIcon />}
|
||||
>
|
||||
Manage projects
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<form onSubmit={handleForm} autoComplete="off">
|
||||
<TextField
|
||||
className={classes.projectName}
|
||||
color="secondary"
|
||||
inputProps={{
|
||||
id: "projectName",
|
||||
ref: (node) => {
|
||||
setInput(node);
|
||||
},
|
||||
}}
|
||||
label="Project name"
|
||||
placeholder="Project name…"
|
||||
error={Boolean(openProjErr)}
|
||||
helperText={openProjErr && openProjErr.message}
|
||||
/>
|
||||
<Button
|
||||
className={classes.button}
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="secondary"
|
||||
size="large"
|
||||
disabled={
|
||||
openProjLoading || Boolean(openProjData?.openProject?.name)
|
||||
}
|
||||
startIcon={
|
||||
openProjLoading || openProjData?.openProject ? (
|
||||
<CircularProgress size={22} />
|
||||
) : (
|
||||
<AddIcon />
|
||||
)
|
||||
}
|
||||
>
|
||||
Create project
|
||||
</Button>
|
||||
<Link href="/projects" passHref>
|
||||
<Button
|
||||
className={classes.button}
|
||||
variant="outlined"
|
||||
component="a"
|
||||
size="large"
|
||||
startIcon={<FolderIcon />}
|
||||
>
|
||||
Open project…
|
||||
</Button>
|
||||
</Link>
|
||||
</form>
|
||||
)}
|
||||
<Link href="/projects" passHref>
|
||||
<Button
|
||||
className={classes.button}
|
||||
variant="contained"
|
||||
color="secondary"
|
||||
component="a"
|
||||
size="large"
|
||||
startIcon={<FolderIcon />}
|
||||
>
|
||||
Manage projects
|
||||
</Button>
|
||||
</Link>
|
||||
</Box>
|
||||
</Layout>
|
||||
);
|
||||
|
@ -12,7 +12,7 @@ function Index(): JSX.Element {
|
||||
</Box>
|
||||
<Typography paragraph>
|
||||
Projects contain settings and data generated/processed by Hetty. They
|
||||
are stored as SQLite database files on disk.
|
||||
are stored in a single database on disk.
|
||||
</Typography>
|
||||
<Box my={4}>
|
||||
<Divider />
|
||||
|
Reference in New Issue
Block a user