Skip to content

Commit 1d493ec

Browse files
committed
feat: logs in the ui
1 parent f7b96e6 commit 1d493ec

File tree

3 files changed

+109
-0
lines changed

3 files changed

+109
-0
lines changed

apps/api/src/controllers/data.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,4 +74,24 @@ export function dataRoutes(fastify: FastifyInstance) {
7474
}
7575
}
7676
);
77+
78+
// Get all logs
79+
fastify.get(
80+
"/api/v1/data/logs",
81+
async (request: FastifyRequest, reply: FastifyReply) => {
82+
const bearer = request.headers.authorization!.split(" ")[1];
83+
const token = checkToken(bearer);
84+
85+
if (token) {
86+
try {
87+
const logs = await import("fs/promises").then((fs) =>
88+
fs.readFile("logs.log", "utf-8")
89+
);
90+
reply.send({ logs: logs });
91+
} catch (error) {
92+
reply.code(500).send({ error: "Failed to read logs file" });
93+
}
94+
}
95+
}
96+
);
7797
}

apps/client/layouts/adminLayout.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
import { Button } from "@radix-ui/themes";
1010
import {
1111
ContactIcon,
12+
FileText,
1213
KeyRound,
1314
Mail,
1415
Mailbox,
@@ -81,6 +82,12 @@ export default function AdminLayout({ children }: any) {
8182
current: location.pathname === "/admin/authentication",
8283
icon: KeyRound,
8384
},
85+
{
86+
name: "Logs",
87+
href: "/admin/logs",
88+
current: location.pathname === "/admin/logs",
89+
icon: FileText,
90+
},
8491
];
8592

8693
return (

apps/client/pages/admin/logs.tsx

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import { Card, CardContent, CardHeader, CardTitle } from "@/shadcn/ui/card";
2+
import { getCookie } from "cookies-next";
3+
import { useEffect, useState } from "react";
4+
5+
const Logs = () => {
6+
const [logs, setLogs] = useState([]);
7+
const [loading, setLoading] = useState(true);
8+
9+
const fetchLogs = async () => {
10+
const response = await fetch("/api/v1/data/logs", {
11+
headers: {
12+
Authorization: `Bearer ${getCookie("session")}`,
13+
},
14+
});
15+
const data = await response.json();
16+
17+
// Split logs by newline and parse each line as JSON
18+
const parsedLogs = data.logs
19+
.split("\n")
20+
.filter((line) => line.trim()) // Remove empty lines
21+
.map((line) => {
22+
try {
23+
return JSON.parse(line);
24+
} catch (e) {
25+
console.error("Failed to parse log line:", e);
26+
return null;
27+
}
28+
})
29+
.filter((log) => log !== null) // Remove any
30+
.sort((a, b) => b.time - a.time);
31+
32+
setLogs(parsedLogs);
33+
setLoading(false);
34+
};
35+
36+
useEffect(() => {
37+
fetchLogs();
38+
}, []);
39+
40+
if (loading) {
41+
return <div>Loading...</div>;
42+
}
43+
44+
return (
45+
<div className="p-4">
46+
<button
47+
className="mb-4"
48+
onClick={() => {
49+
setLoading(true);
50+
fetchLogs();
51+
}}
52+
>
53+
Refresh Logs
54+
</button>
55+
<Card>
56+
<CardHeader>
57+
<CardTitle>Logs</CardTitle>
58+
</CardHeader>
59+
<CardContent>
60+
{logs.length === 0 ? (
61+
<div>No logs available</div>
62+
) : (
63+
<ul>
64+
{logs.map((log, index) => (
65+
<li key={index} className="border-b py-2">
66+
<div className="flex flex-row gap-x-2 text-xs">
67+
<span className="text-xs text-gray-500">
68+
{new Date(log.time).toLocaleString()}
69+
</span>
70+
<strong>{log.msg}</strong>
71+
</div>
72+
</li>
73+
))}
74+
</ul>
75+
)}
76+
</CardContent>
77+
</Card>
78+
</div>
79+
);
80+
};
81+
82+
export default Logs;

0 commit comments

Comments
 (0)