Add routes (untested)
This commit is contained in:
parent
db20819ad2
commit
d346ccf701
9 changed files with 132 additions and 13 deletions
|
|
@ -1,5 +0,0 @@
|
||||||
<!-- BEGIN:nextjs-agent-rules -->
|
|
||||||
# This is NOT the Next.js you know
|
|
||||||
|
|
||||||
This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices.
|
|
||||||
<!-- END:nextjs-agent-rules -->
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
@AGENTS.md
|
|
||||||
28
app/api/auth/login/route.ts
Normal file
28
app/api/auth/login/route.ts
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
|
|
||||||
|
export async function POST(req: NextRequest) {
|
||||||
|
const { username, password } = await req.json();
|
||||||
|
|
||||||
|
const res = await fetch("http://localhost:3001/auth/login", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
Authorization:
|
||||||
|
"Basic " + Buffer.from(`${username}:${password}`).toString("base64"),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
return NextResponse.json({ error: "Invalid credentials" }, { status: 401 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const { token } = await res.json();
|
||||||
|
const response = NextResponse.json({ success: true });
|
||||||
|
response.cookies.set("token", token, {
|
||||||
|
httpOnly: true,
|
||||||
|
secure: true,
|
||||||
|
sameSite: "strict",
|
||||||
|
maxAge: 60 * 60 * 8,
|
||||||
|
path: "/",
|
||||||
|
});
|
||||||
|
return response;
|
||||||
|
}
|
||||||
7
app/api/auth/logout/route.ts
Normal file
7
app/api/auth/logout/route.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { NextResponse } from "next/server";
|
||||||
|
|
||||||
|
export async function POST() {
|
||||||
|
const response = NextResponse.json({ success: true });
|
||||||
|
response.cookies.delete("token");
|
||||||
|
return response;
|
||||||
|
}
|
||||||
29
app/api/services/[service]/[action]/route.ts
Normal file
29
app/api/services/[service]/[action]/route.ts
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
|
|
||||||
|
const ALLOWED_ACTIONS = ["start", "stop", "restart"];
|
||||||
|
|
||||||
|
export async function POST(
|
||||||
|
req: NextRequest,
|
||||||
|
{ params }: { params: { service: string; action: string } },
|
||||||
|
) {
|
||||||
|
const token = req.cookies.get("token")?.value;
|
||||||
|
if (!token) {
|
||||||
|
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ALLOWED_ACTIONS.includes(params.action)) {
|
||||||
|
return NextResponse.json({ error: "Invalid action" }, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await fetch(
|
||||||
|
`http://localhost:3001/services/${params.service}/${params.action}`,
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return NextResponse.json(await res.json(), { status: res.status });
|
||||||
|
}
|
||||||
22
app/api/services/[service]/logs/route.ts
Normal file
22
app/api/services/[service]/logs/route.ts
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
|
|
||||||
|
export async function GET(
|
||||||
|
req: NextRequest,
|
||||||
|
{ params }: { params: { service: string } },
|
||||||
|
) {
|
||||||
|
const token = req.cookies.get("token")?.value;
|
||||||
|
if (!token) {
|
||||||
|
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await fetch(
|
||||||
|
`http://localhost:3001/services/${params.service}/logs`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return NextResponse.json(await res.json(), { status: res.status });
|
||||||
|
}
|
||||||
17
app/api/system/reboot/route.ts
Normal file
17
app/api/system/reboot/route.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
|
|
||||||
|
export async function POST(req: NextRequest) {
|
||||||
|
const token = req.cookies.get("token")?.value;
|
||||||
|
if (!token) {
|
||||||
|
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await fetch("http://localhost:3001/system/reboot", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return NextResponse.json(await res.json(), { status: res.status });
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
interface HeroProps {
|
interface HeroProps {
|
||||||
lastUpdated: string | null;
|
lastUpdated: string | null;
|
||||||
}
|
}
|
||||||
|
|
@ -8,12 +10,32 @@ export default function Hero({ lastUpdated }: HeroProps) {
|
||||||
<p className="text-xs font-medium tracking-widest uppercase text-blue-500 mb-3">
|
<p className="text-xs font-medium tracking-widest uppercase text-blue-500 mb-3">
|
||||||
dell-xps-nixos-serv
|
dell-xps-nixos-serv
|
||||||
</p>
|
</p>
|
||||||
<h1
|
<div className="flex gap-[10px] items-center">
|
||||||
className="text-4xl md:text-5xl font-normal leading-tight tracking-tight text-gray-900 mb-2"
|
<svg
|
||||||
style={{ fontFamily: "'Playfair Display', serif" }}
|
className="w-[50px] h-[50px]"
|
||||||
>
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
Home server
|
viewBox="0 0 128 128"
|
||||||
</h1>
|
>
|
||||||
|
<path
|
||||||
|
fill="#7EBAE4"
|
||||||
|
d="M50.732 43.771L20.525 96.428l-7.052-12.033 8.14-14.103-16.167-.042L2 64.237l3.519-6.15 23.013.073 8.27-14.352 13.93-.037zm2.318 42.094l60.409.003-6.827 12.164-16.205-.045 8.047 14.115-3.45 6.01-7.05.008-11.445-20.097-16.483-.034-6.996-12.124zm35.16-23.074l-30.202-52.66L71.888 10l8.063 14.148 8.12-14.072 6.897.002 3.532 6.143-11.57 20.024 8.213 14.386-6.933 12.16z"
|
||||||
|
clipRule="evenodd"
|
||||||
|
fillRule="evenodd"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fill="#5277C3"
|
||||||
|
d="M39.831 65.463l30.202 52.66-13.88.131-8.063-14.148-8.12 14.072-6.897-.002-3.532-6.143 11.57-20.024-8.213-14.386 6.933-12.16zm35.08-23.207l-60.409-.003L21.33 30.09l16.204.045-8.047-14.115 3.45-6.01 7.051-.01 11.444 20.097 16.484.034 6.996 12.124zm2.357 42.216l30.207-52.658 7.052 12.034-8.141 14.102 16.168.043L126 64.006l-3.519 6.15-23.013-.073-8.27 14.352-13.93.037z"
|
||||||
|
clipRule="evenodd"
|
||||||
|
fillRule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<h1
|
||||||
|
className="text-4xl md:text-5xl font-normal leading-tight tracking-tight text-gray-900 mb-2"
|
||||||
|
style={{ fontFamily: "'Playfair Display', serif" }}
|
||||||
|
>
|
||||||
|
Home server
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
<p className="text-sm text-gray-400 font-light">
|
<p className="text-sm text-gray-400 font-light">
|
||||||
{lastUpdated
|
{lastUpdated
|
||||||
? `Last updated ${new Date(lastUpdated).toLocaleTimeString()}`
|
? `Last updated ${new Date(lastUpdated).toLocaleTimeString()}`
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ export default function StatsGrid({ stats }: StatsGridProps) {
|
||||||
label="CPU"
|
label="CPU"
|
||||||
value={stats ? `${stats.cpu.percent.toFixed(1)}%` : "—"}
|
value={stats ? `${stats.cpu.percent.toFixed(1)}%` : "—"}
|
||||||
sub={stats?.cpu.model.replace(/\(R\)/g, "").replace(/\(TM\)/g, "").trim()}
|
sub={stats?.cpu.model.replace(/\(R\)/g, "").replace(/\(TM\)/g, "").trim()}
|
||||||
percent={stats?.cpu.percent.toFixed(1)}
|
percent={Number(stats?.cpu.percent.toFixed(1))}
|
||||||
delay={0}
|
delay={0}
|
||||||
/>
|
/>
|
||||||
<StatCard
|
<StatCard
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue