Merge pull request #160 from VAR-Virtual-Air-Rescue/staging
Fixed Wrong IP being loged
This commit was merged in pull request #160.
This commit is contained in:
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,
|
||||
});
|
||||
}
|
||||
const ip = await fetch("https://api.ipify.org/?format=json")
|
||||
.then((res) => res.json())
|
||||
.then((data) => data.ip);
|
||||
|
||||
if (user.firstname !== values.firstname) {
|
||||
await logAction("PROFILE_CHANGE", {
|
||||
ip,
|
||||
field: "firstname",
|
||||
oldValue: user.firstname,
|
||||
newValue: values.firstname,
|
||||
@@ -111,6 +116,7 @@ export const ProfileForm = ({
|
||||
}
|
||||
if (user.lastname !== values.lastname) {
|
||||
await logAction("PROFILE_CHANGE", {
|
||||
ip,
|
||||
field: "lastname",
|
||||
oldValue: user.lastname,
|
||||
newValue: values.lastname,
|
||||
@@ -118,6 +124,7 @@ export const ProfileForm = ({
|
||||
}
|
||||
if (user.email !== values.email) {
|
||||
await logAction("PROFILE_CHANGE", {
|
||||
ip,
|
||||
field: "email",
|
||||
oldValue: user.email,
|
||||
newValue: values.email,
|
||||
|
||||
@@ -48,9 +48,11 @@ export const Login = () => {
|
||||
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") || "/");
|
||||
} catch (error) {
|
||||
showBoundary(error);
|
||||
|
||||
@@ -26,6 +26,7 @@ export async function getOrSetDeviceId() {
|
||||
export const logAction = async (
|
||||
type: LOG_TYPE,
|
||||
otherValues?: {
|
||||
ip: string;
|
||||
field?: string;
|
||||
oldValue?: string;
|
||||
newValue?: string;
|
||||
@@ -35,13 +36,6 @@ export const logAction = async (
|
||||
const headersList = await headers();
|
||||
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();
|
||||
if (type == "LOGIN" || type == "REGISTER") {
|
||||
const existingLogs = await prisma.log.findMany({
|
||||
@@ -52,7 +46,7 @@ export const logAction = async (
|
||||
},
|
||||
OR: [
|
||||
{
|
||||
ip: ip,
|
||||
ip: otherValues?.ip,
|
||||
},
|
||||
{
|
||||
deviceId: deviceId,
|
||||
@@ -82,7 +76,7 @@ export const logAction = async (
|
||||
browser: headersList.get("user-agent") || "unknown",
|
||||
userId: user?.user.id || otherValues?.userId,
|
||||
deviceId: deviceId,
|
||||
ip,
|
||||
ip: otherValues?.ip,
|
||||
...otherValues,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -94,7 +94,12 @@ export const Register = () => {
|
||||
return;
|
||||
}
|
||||
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", {
|
||||
ip: ip,
|
||||
userId: user.id,
|
||||
});
|
||||
await signIn("credentials", {
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
RocketIcon,
|
||||
ReaderIcon,
|
||||
DownloadIcon,
|
||||
UpdateIcon,
|
||||
ActivityLogIcon,
|
||||
} from "@radix-ui/react-icons";
|
||||
import Link from "next/link";
|
||||
@@ -14,7 +13,7 @@ import { WarningAlert } from "./ui/PageAlert";
|
||||
import { getServerSession } from "api/auth/[...nextauth]/auth";
|
||||
import { Error } from "./Error";
|
||||
import Image from "next/image";
|
||||
import { Loader, Plane, Radar, Workflow } from "lucide-react";
|
||||
import { Plane, Radar, Workflow } from "lucide-react";
|
||||
import { BookingButton } from "./BookingButton";
|
||||
|
||||
export const VerticalNav = async () => {
|
||||
@@ -103,6 +102,11 @@ export const VerticalNav = async () => {
|
||||
<Link href="/admin/penalty">Audit-Log</Link>
|
||||
</li>
|
||||
)}
|
||||
{session.user.permissions.includes("ADMIN_USER_ADVANCED") && (
|
||||
<li>
|
||||
<Link href="/admin/account-log">Account Log</Link>
|
||||
</li>
|
||||
)}
|
||||
{session.user.permissions.includes("ADMIN_CHANGELOG") && (
|
||||
<li>
|
||||
<Link href="/admin/changelog">Changelog</Link>
|
||||
|
||||
Reference in New Issue
Block a user