Fixed Wrong IP being loged #160
19
apps/hub/app/(app)/admin/log/layout.tsx
Normal file
19
apps/hub/app/(app)/admin/log/layout.tsx
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { Error } from "_components/Error";
|
||||||
|
import { getServerSession } from "api/auth/[...nextauth]/auth";
|
||||||
|
|
||||||
|
const AdminAccountLogLayout = async ({ children }: { children: React.ReactNode }) => {
|
||||||
|
const session = await getServerSession();
|
||||||
|
|
||||||
|
if (!session) return <Error title="Nicht eingeloggt" statusCode={401} />;
|
||||||
|
|
||||||
|
const user = session.user;
|
||||||
|
|
||||||
|
if (!user?.permissions.includes("ADMIN_USER_ADVANCED"))
|
||||||
|
return <Error title="Keine Berechtigung" statusCode={403} />;
|
||||||
|
|
||||||
|
return <>{children}</>;
|
||||||
|
};
|
||||||
|
|
||||||
|
AdminAccountLogLayout.displayName = "AdminAccountLogLayout";
|
||||||
|
|
||||||
|
export default AdminAccountLogLayout;
|
||||||
91
apps/hub/app/(app)/admin/log/page.tsx
Normal file
91
apps/hub/app/(app)/admin/log/page.tsx
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
"use client";
|
||||||
|
import { LogsIcon } from "lucide-react";
|
||||||
|
import { PaginatedTable } from "../../../_components/PaginatedTable";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { ColumnDef } from "@tanstack/react-table";
|
||||||
|
import { Log, Prisma, User } from "@repo/db";
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<PaginatedTable
|
||||||
|
stickyHeaders
|
||||||
|
initialOrderBy={[{ id: "timestamp", desc: true }]}
|
||||||
|
prismaModel="log"
|
||||||
|
showSearch
|
||||||
|
include={{
|
||||||
|
User: true,
|
||||||
|
}}
|
||||||
|
getFilter={(searchTerm) =>
|
||||||
|
({
|
||||||
|
OR: [
|
||||||
|
{
|
||||||
|
User: {
|
||||||
|
firstname: { contains: searchTerm, mode: "insensitive" },
|
||||||
|
lastname: { contains: searchTerm, mode: "insensitive" },
|
||||||
|
publicId: { contains: searchTerm, mode: "insensitive" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ deviceId: { contains: searchTerm, mode: "insensitive" } },
|
||||||
|
{ ip: { contains: searchTerm, mode: "insensitive" } },
|
||||||
|
],
|
||||||
|
}) as Prisma.LogWhereInput
|
||||||
|
}
|
||||||
|
columns={
|
||||||
|
[
|
||||||
|
{
|
||||||
|
header: "ID",
|
||||||
|
accessorKey: "id",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: "Aktion",
|
||||||
|
accessorKey: "action",
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const action = row.original.type;
|
||||||
|
|
||||||
|
if (action !== "PROFILE_CHANGE") {
|
||||||
|
return <span className="text-blue-500">{action}</span>;
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<span className="text-yellow-500">{`${row.original.field} von "${row.original.oldValue}" zu "${row.original.newValue}"`}</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: "IP",
|
||||||
|
accessorKey: "ip",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: "Browser-ID",
|
||||||
|
accessorKey: "deviceId",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: "Zeitstempel",
|
||||||
|
accessorKey: "timestamp",
|
||||||
|
cell: (info) => new Date(info.getValue<string>()).toLocaleString("de-DE"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: "Benutzer",
|
||||||
|
accessorKey: "userId",
|
||||||
|
cell: ({ row }) => {
|
||||||
|
return (
|
||||||
|
<Link href={`/admin/user/${row.original.userId}`} className={"link"}>
|
||||||
|
{row.original.User
|
||||||
|
? `${row.original.User.firstname} ${row.original.User.lastname} - ${row.original.User.publicId}`
|
||||||
|
: "Unbekannt"}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
] as ColumnDef<Log & { User: User }>[]
|
||||||
|
}
|
||||||
|
leftOfSearch={
|
||||||
|
<span className="flex items-center gap-2">
|
||||||
|
<LogsIcon className="h-5 w-5" /> Account Log
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -102,8 +102,13 @@ export const ProfileForm = ({
|
|||||||
userId: user.id,
|
userId: user.id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
const ip = await fetch("https://api.ipify.org/?format=json")
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((data) => data.ip);
|
||||||
|
|
||||||
if (user.firstname !== values.firstname) {
|
if (user.firstname !== values.firstname) {
|
||||||
await logAction("PROFILE_CHANGE", {
|
await logAction("PROFILE_CHANGE", {
|
||||||
|
ip,
|
||||||
field: "firstname",
|
field: "firstname",
|
||||||
oldValue: user.firstname,
|
oldValue: user.firstname,
|
||||||
newValue: values.firstname,
|
newValue: values.firstname,
|
||||||
@@ -111,6 +116,7 @@ export const ProfileForm = ({
|
|||||||
}
|
}
|
||||||
if (user.lastname !== values.lastname) {
|
if (user.lastname !== values.lastname) {
|
||||||
await logAction("PROFILE_CHANGE", {
|
await logAction("PROFILE_CHANGE", {
|
||||||
|
ip,
|
||||||
field: "lastname",
|
field: "lastname",
|
||||||
oldValue: user.lastname,
|
oldValue: user.lastname,
|
||||||
newValue: values.lastname,
|
newValue: values.lastname,
|
||||||
@@ -118,6 +124,7 @@ export const ProfileForm = ({
|
|||||||
}
|
}
|
||||||
if (user.email !== values.email) {
|
if (user.email !== values.email) {
|
||||||
await logAction("PROFILE_CHANGE", {
|
await logAction("PROFILE_CHANGE", {
|
||||||
|
ip,
|
||||||
field: "email",
|
field: "email",
|
||||||
oldValue: user.email,
|
oldValue: user.email,
|
||||||
newValue: values.email,
|
newValue: values.email,
|
||||||
|
|||||||
@@ -48,9 +48,11 @@ export const Login = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("data", data);
|
const ip = await fetch("https://api.ipify.org/?format=json")
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((data) => data.ip);
|
||||||
|
|
||||||
await logAction("LOGIN");
|
await logAction("LOGIN", { ip });
|
||||||
redirect(searchParams.get("redirect") || "/");
|
redirect(searchParams.get("redirect") || "/");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showBoundary(error);
|
showBoundary(error);
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ export async function getOrSetDeviceId() {
|
|||||||
export const logAction = async (
|
export const logAction = async (
|
||||||
type: LOG_TYPE,
|
type: LOG_TYPE,
|
||||||
otherValues?: {
|
otherValues?: {
|
||||||
|
ip: string;
|
||||||
field?: string;
|
field?: string;
|
||||||
oldValue?: string;
|
oldValue?: string;
|
||||||
newValue?: string;
|
newValue?: string;
|
||||||
@@ -35,13 +36,6 @@ export const logAction = async (
|
|||||||
const headersList = await headers();
|
const headersList = await headers();
|
||||||
const user = await getServerSession();
|
const user = await getServerSession();
|
||||||
|
|
||||||
console.log(Array.from(headersList.entries()));
|
|
||||||
|
|
||||||
const ip =
|
|
||||||
headersList.get("X-Forwarded-For") ||
|
|
||||||
headersList.get("Forwarded") ||
|
|
||||||
headersList.get("X-Real-IP");
|
|
||||||
|
|
||||||
const deviceId = await getOrSetDeviceId();
|
const deviceId = await getOrSetDeviceId();
|
||||||
if (type == "LOGIN" || type == "REGISTER") {
|
if (type == "LOGIN" || type == "REGISTER") {
|
||||||
const existingLogs = await prisma.log.findMany({
|
const existingLogs = await prisma.log.findMany({
|
||||||
@@ -52,7 +46,7 @@ export const logAction = async (
|
|||||||
},
|
},
|
||||||
OR: [
|
OR: [
|
||||||
{
|
{
|
||||||
ip: ip,
|
ip: otherValues?.ip,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
deviceId: deviceId,
|
deviceId: deviceId,
|
||||||
@@ -82,7 +76,7 @@ export const logAction = async (
|
|||||||
browser: headersList.get("user-agent") || "unknown",
|
browser: headersList.get("user-agent") || "unknown",
|
||||||
userId: user?.user.id || otherValues?.userId,
|
userId: user?.user.id || otherValues?.userId,
|
||||||
deviceId: deviceId,
|
deviceId: deviceId,
|
||||||
ip,
|
ip: otherValues?.ip,
|
||||||
...otherValues,
|
...otherValues,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -94,7 +94,12 @@ export const Register = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await sendVerificationLink(user.id);
|
await sendVerificationLink(user.id);
|
||||||
|
const ip = await fetch("https://api.ipify.org/?format=json")
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((data) => data.ip);
|
||||||
|
|
||||||
await logAction("REGISTER", {
|
await logAction("REGISTER", {
|
||||||
|
ip: ip,
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
});
|
});
|
||||||
await signIn("credentials", {
|
await signIn("credentials", {
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import {
|
|||||||
RocketIcon,
|
RocketIcon,
|
||||||
ReaderIcon,
|
ReaderIcon,
|
||||||
DownloadIcon,
|
DownloadIcon,
|
||||||
UpdateIcon,
|
|
||||||
ActivityLogIcon,
|
ActivityLogIcon,
|
||||||
} from "@radix-ui/react-icons";
|
} from "@radix-ui/react-icons";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
@@ -14,7 +13,7 @@ import { WarningAlert } from "./ui/PageAlert";
|
|||||||
import { getServerSession } from "api/auth/[...nextauth]/auth";
|
import { getServerSession } from "api/auth/[...nextauth]/auth";
|
||||||
import { Error } from "./Error";
|
import { Error } from "./Error";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { Loader, Plane, Radar, Workflow } from "lucide-react";
|
import { Plane, Radar, Workflow } from "lucide-react";
|
||||||
import { BookingButton } from "./BookingButton";
|
import { BookingButton } from "./BookingButton";
|
||||||
|
|
||||||
export const VerticalNav = async () => {
|
export const VerticalNav = async () => {
|
||||||
@@ -103,6 +102,11 @@ export const VerticalNav = async () => {
|
|||||||
<Link href="/admin/penalty">Audit-Log</Link>
|
<Link href="/admin/penalty">Audit-Log</Link>
|
||||||
</li>
|
</li>
|
||||||
)}
|
)}
|
||||||
|
{session.user.permissions.includes("ADMIN_USER_ADVANCED") && (
|
||||||
|
<li>
|
||||||
|
<Link href="/admin/account-log">Account Log</Link>
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
{session.user.permissions.includes("ADMIN_CHANGELOG") && (
|
{session.user.permissions.includes("ADMIN_CHANGELOG") && (
|
||||||
<li>
|
<li>
|
||||||
<Link href="/admin/changelog">Changelog</Link>
|
<Link href="/admin/changelog">Changelog</Link>
|
||||||
|
|||||||
Reference in New Issue
Block a user