Improved error handling and data fetching
This commit is contained in:
parent
c237d0063d
commit
2e4d7bc8c5
4 changed files with 64 additions and 90 deletions
|
|
@ -1,5 +1,6 @@
|
||||||
import { getConfig, getRoutes, getServer } from '@/lib/serverActions';
|
import { getConfig, getRoutes, getServer } from '@/lib/serverActions';
|
||||||
import { NextApiRequest } from 'next';
|
import { NextApiRequest } from 'next';
|
||||||
|
import { revalidateTag } from 'next/cache';
|
||||||
import { NextResponse } from 'next/server';
|
import { NextResponse } from 'next/server';
|
||||||
|
|
||||||
export async function GET() {
|
export async function GET() {
|
||||||
|
|
@ -30,6 +31,8 @@ export async function DELETE(req: Request) {
|
||||||
|
|
||||||
let index = routesMap.indexOf(targetRoute);
|
let index = routesMap.indexOf(targetRoute);
|
||||||
|
|
||||||
|
revalidateTag('routes');
|
||||||
|
|
||||||
if(index == -1) return new NextResponse(JSON.stringify({error: true, message: "Route not found"}), {
|
if(index == -1) return new NextResponse(JSON.stringify({error: true, message: "Route not found"}), {
|
||||||
status: 404,
|
status: 404,
|
||||||
headers: {
|
headers: {
|
||||||
|
|
|
||||||
35
app/page.tsx
35
app/page.tsx
|
|
@ -1,26 +1,22 @@
|
||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { CardTitle, Card, CardDescription, CardHeader, CardContent } from '@/components/ui/card';
|
import { CardTitle, Card, CardDescription, CardHeader, CardContent, CardFooter } from '@/components/ui/card';
|
||||||
import { getRoutes } from '@/lib/serverActions';
|
|
||||||
import RouteCards from '@/components/RouteCards';
|
import RouteCards from '@/components/RouteCards';
|
||||||
import { useEffect, useState } from 'react';
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { useRoutes } from '@/lib/clientActions';
|
||||||
|
|
||||||
async function fetchRoutes() {
|
|
||||||
let routes: Route[] = await getRoutes();
|
|
||||||
return routes;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
|
|
||||||
useEffect(() => {
|
const { data: routes, error, isLoading } = useRoutes();
|
||||||
async function fetchData() {
|
let routesMap: Route[] = routes as Route[];
|
||||||
const data: Route[] = await fetchRoutes();
|
|
||||||
setRoutes(data);
|
|
||||||
}
|
|
||||||
fetchData();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const [routes, setRoutes] = useState<Route[]>([])
|
if (error) return <div>Error loading routes</div>
|
||||||
|
if (isLoading) return (
|
||||||
|
<div className='justify-center'>
|
||||||
|
Loading...
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main>
|
<main>
|
||||||
|
|
@ -31,14 +27,17 @@ export default function Home() {
|
||||||
<CardDescription>Amount of configured routes</CardDescription>
|
<CardDescription>Amount of configured routes</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className='flex items-center'>
|
<CardContent className='flex items-center'>
|
||||||
<div className=''>
|
<div>
|
||||||
<p className='text-xl font-semibold translate-y-2'>{routes.length}</p>
|
<p className='text-xl font-semibold translate-y-2'>{routesMap.length}</p>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
|
<CardFooter>
|
||||||
|
<Button className='translate-y-2.5'>Create</Button>
|
||||||
|
</CardFooter>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
<div className='grid grid-flow-row-dense md:grid-cols-6 sm:grid-cols-2 p-2'>
|
<div className='grid grid-flow-row-dense md:grid-cols-6 sm:grid-cols-2 p-2'>
|
||||||
<RouteCards routes={routes} />
|
<RouteCards routes={routes as Route[]} />
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -3,83 +3,54 @@
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from "./ui/card";
|
import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from "./ui/card";
|
||||||
import { getHosts, getRouteUpstreams } from '@/lib/utils';
|
import { getHosts, getRouteUpstreams } from '@/lib/utils';
|
||||||
import { deleteRoute } from '@/lib/serverActions';
|
|
||||||
import { toast } from './ui/use-toast';
|
import { toast } from './ui/use-toast';
|
||||||
import React, { useState } from 'react';
|
import React from 'react';
|
||||||
|
import useSWRMutation from 'swr/mutation';
|
||||||
|
import { deleteRoute } from '@/lib/clientActions';
|
||||||
|
|
||||||
class RouteCards extends React.Component<{routes: Route[]}> {
|
export default function RouteCards({ routes }: { routes: Route[] }) {
|
||||||
constructor(props: { routes: Route[] }) {
|
|
||||||
super(props);
|
|
||||||
this.handleClick = this.handleClick.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
state = {
|
const { trigger } = useSWRMutation("/api/caddy/routes", deleteRoute, {
|
||||||
routes: this.props.routes,
|
onError: () => {
|
||||||
}
|
return toast({
|
||||||
|
title: "Error",
|
||||||
componentDidUpdate(prevProps: Readonly<{ routes: Route[]; }>, prevState: Readonly<{}>) {
|
description: "There was an error deleting the route",
|
||||||
if (this.props.routes !== prevProps.routes) {
|
variant: "destructive"
|
||||||
this.setState({
|
});
|
||||||
routes: this.props.routes
|
},
|
||||||
|
onSuccess: () => {
|
||||||
|
toast({
|
||||||
|
title: "Success",
|
||||||
|
description: "Route deleted successfully!",
|
||||||
|
variant: "success"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
handleDelete = async (route: Route) => {
|
|
||||||
let res = await deleteRoute(route);
|
|
||||||
|
|
||||||
if (res.error) return toast({
|
|
||||||
title: "Error",
|
|
||||||
description: res.message,
|
|
||||||
variant: "destructive"
|
|
||||||
});
|
|
||||||
|
|
||||||
this.state.routes.splice(this.state.routes.indexOf(route), 1)
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
routes: this.state.routes
|
|
||||||
});
|
|
||||||
|
|
||||||
return toast({
|
|
||||||
title: "Success",
|
|
||||||
description: "Route deleted successfully!",
|
|
||||||
variant: "success"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
handleClick = (route: Route) => {
|
|
||||||
console.log(route);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
this.state.routes.map((route, index) => (
|
|
||||||
<div key={index} className='p-2'>
|
|
||||||
<Card key={index} className='w-[300px]'>
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle>{getHosts(route).values().next().value}</CardTitle>
|
|
||||||
{getHosts(route).map((route => <CardDescription key={route}>{route}</ CardDescription>))}
|
|
||||||
<CardDescription></CardDescription>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<p className="text-sm font-medium leading-none">
|
|
||||||
Tunneling to
|
|
||||||
</p>
|
|
||||||
<p className="text-sm text-muted-foreground">{getRouteUpstreams(route)?.map((upstream => upstream.dial))}</p>
|
|
||||||
</CardContent>
|
|
||||||
<CardFooter className="flex justify-between">
|
|
||||||
<Button onClick={() => { this.handleClick(route) }}>Edit</Button>
|
|
||||||
<Button variant="destructive" onClick={() => { this.handleDelete(route) }}>Delete</Button>
|
|
||||||
</CardFooter>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
routes.map((route, index) => (
|
||||||
|
<div key={index} className='p-2'>
|
||||||
|
<Card key={index} className='w-[300px]'>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>{getHosts(route).values().next().value}</CardTitle>
|
||||||
|
{getHosts(route).map((route => <CardDescription key={route}>{route}</ CardDescription>))}
|
||||||
|
<CardDescription></CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<p className="text-sm font-medium leading-none">
|
||||||
|
Tunneling to
|
||||||
|
</p>
|
||||||
|
<p className="text-sm text-muted-foreground">{getRouteUpstreams(route)?.map((upstream => upstream.dial))}</p>
|
||||||
|
</CardContent>
|
||||||
|
<CardFooter className="flex justify-between">
|
||||||
|
<Button onClick={() => { trigger(route) }}>Edit</Button>
|
||||||
|
<Button variant="destructive" onClick={() => { trigger(route) }}>Delete</Button>
|
||||||
|
</CardFooter>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default RouteCards;
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@
|
||||||
"next-themes": "^0.2.1",
|
"next-themes": "^0.2.1",
|
||||||
"react": "^18",
|
"react": "^18",
|
||||||
"react-dom": "^18",
|
"react-dom": "^18",
|
||||||
|
"swr": "^2.2.4",
|
||||||
"tailwind-merge": "^2.0.0",
|
"tailwind-merge": "^2.0.0",
|
||||||
"tailwindcss-animate": "^1.0.7"
|
"tailwindcss-animate": "^1.0.7"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue