Resources Seite für Desktop-client

This commit is contained in:
PxlLoewe
2025-06-27 23:39:28 -07:00
parent ec22cdb987
commit 1a1fab3f58
7 changed files with 131 additions and 49 deletions

View File

@@ -0,0 +1,43 @@
import { Download } from "lucide-react";
import Image, { StaticImageData } from "next/image";
import { ReactNode } from "react";
type ResourceCardProps = {
title?: string;
description?: string;
btnLabel?: string;
imageAlt?: string;
image?: StaticImageData;
btnHref?: string;
BtnIcon: ReactNode;
};
export default function ResourceCard({
title,
BtnIcon,
description,
btnLabel: downloadLabel,
imageAlt,
image,
btnHref: downloadHref,
}: ResourceCardProps) {
return (
<div className="hero bg-base-200 rounded-xl shadow-lg p-8">
<div className="hero-content flex-col lg:flex-row">
{image && (
<div className="max-w-sm w-full">
<Image src={image} alt={imageAlt ?? ""} width={600} className="rounded-lg shadow-2xl" />
</div>
)}
<div className="lg:ml-10 mt-6 lg:mt-0">
<h1 className="text-3xl font-bold">{title}</h1>
<p className="py-2 max-w-2xl text-base">{description}</p>
<a href={downloadHref} className="btn btn-primary mt-4">
{BtnIcon}
{downloadLabel}
</a>
</div>
</div>
</div>
);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

View File

@@ -0,0 +1,18 @@
import ResourceCard from "(app)/resources/_components/Card";
import { Download } from "lucide-react";
import Desktop from "./_components/desktop-client.png";
export default function () {
return (
<div>
<ResourceCard
image={Desktop}
title="Desktop client"
BtnIcon={<Download />}
btnHref="https://cdn.virtualairrescue.com/desktop/setup.exe"
btnLabel="Herunterladen"
description="Verwende diesen Client, um dich mit dem VAR-Netzwerk zu verbinden. Wenn du verbindest kannst du einen Push-To-Talk key setzen um den Funk zu bedienen. Wenn du als Pilot fliegen möchtest wird deine Position aus dem Simulator an unser Tracker-System übertragen."
/>
</div>
);
}

View File

@@ -2,25 +2,32 @@
import { prisma } from "@repo/db"; import { prisma } from "@repo/db";
import { sendMailByTemplate } from "../../../helper/mail"; import { sendMailByTemplate } from "../../../helper/mail";
import OLD_USER from "../../api/auth/[...nextauth]/var.User.json";
import bcrypt from "bcryptjs"; import bcrypt from "bcryptjs";
import { createNewUserFromOld, OldUser } from "../../../types/oldUser";
export const resetPassword = async (email: string) => { export const resetPassword = async (email: string) => {
try { try {
const user = await prisma.user.findFirst({ let user = await prisma.user.findFirst({
where: { where: {
email, email,
}, },
}); });
const oldUser = (OLD_USER as OldUser[]).find((u) => u.email === email);
if (!user) { if (!user) {
return { error: "Nutzer nicht gefunden" }; if (oldUser) {
user = await createNewUserFromOld(oldUser);
// If the user is not found in the new database, check the old user data
} else {
return { error: "Nutzer nicht gefunden" };
}
} }
const array = new Uint8Array(8); const array = new Uint8Array(8);
crypto.getRandomValues(array); crypto.getRandomValues(array);
const password = Array.from(array, (byte) => const password = Array.from(array, (byte) => ("0" + (byte % 36).toString(36)).slice(-1)).join(
("0" + (byte % 36).toString(36)).slice(-1), "",
).join(""); );
const hashedPassword = await bcrypt.hash(password, 12); const hashedPassword = await bcrypt.hash(password, 12);
await prisma.user.update({ await prisma.user.update({
where: { where: {

View File

@@ -5,6 +5,7 @@ import {
LockClosedIcon, LockClosedIcon,
RocketIcon, RocketIcon,
ReaderIcon, ReaderIcon,
DownloadIcon,
} from "@radix-ui/react-icons"; } from "@radix-ui/react-icons";
import Link from "next/link"; import Link from "next/link";
import { WarningAlert } from "./ui/PageAlert"; import { WarningAlert } from "./ui/PageAlert";
@@ -42,6 +43,12 @@ export const VerticalNav = async () => {
Einstellungen Einstellungen
</Link> </Link>
</li> </li>
<li>
<Link href="/resources">
<DownloadIcon />
Downloads
</Link>
</li>
{viewAdminMenu && ( {viewAdminMenu && (
<li> <li>
<details open> <details open>

View File

@@ -4,7 +4,7 @@ import Credentials from "next-auth/providers/credentials";
import { DiscordAccount, prisma, User } from "@repo/db"; import { DiscordAccount, prisma, User } from "@repo/db";
import bcrypt from "bcryptjs"; import bcrypt from "bcryptjs";
import oldUser from "./var.User.json"; import oldUser from "./var.User.json";
import { OldUser } from "../../../../types/oldUser"; import { createNewUserFromOld, OldUser } from "../../../../types/oldUser";
import { sendVerificationLink } from "(app)/admin/user/action"; import { sendVerificationLink } from "(app)/admin/user/action";
export const options: AuthOptions = { export const options: AuthOptions = {
@@ -28,48 +28,7 @@ export const options: AuthOptions = {
"v1 User Passwords match:", "v1 User Passwords match:",
bcrypt.compareSync(credentials.password, v1User.password), bcrypt.compareSync(credentials.password, v1User.password),
); );
const newUser = await prisma.user.create({ const newUser = await createNewUserFromOld(v1User);
data: {
email: v1User.email,
password: v1User.password,
migratedFromV1: true,
firstname: v1User.firstname,
lastname: v1User.lastname,
publicId: v1User.publicId,
badges: [
...v1User.badges
.map((badge) => {
switch (badge) {
case "day-1-member":
return "DAY1";
case "d-1":
return "D1";
case "p-1":
return "P1";
default:
return null;
}
})
.filter((badge) => badge !== null),
"V1Veteran",
],
},
});
if (v1User.discord) {
await prisma.discordAccount.create({
data: {
tokenType: "Bearer",
refreshToken: v1User.discord.tokens.refresh_token,
discordId: v1User.discord.profile.id,
userId: newUser.id,
username: v1User.discord.profile.username,
globalName: v1User.discord.profile.global_name,
avatar: v1User.discord.profile.avatar,
email: v1User.discord.profile.email,
verified: v1User.discord.profile.verified,
},
});
}
await sendVerificationLink(newUser.id); await sendVerificationLink(newUser.id);
return newUser; return newUser;
} }

View File

@@ -1,3 +1,5 @@
import { prisma } from "@repo/db";
export interface OldUser { export interface OldUser {
firstname: string; firstname: string;
publicId: string; publicId: string;
@@ -24,3 +26,49 @@ export interface OldUser {
}; };
}; };
} }
export const createNewUserFromOld = async (oldUser: OldUser) => {
const newUser = await prisma.user.create({
data: {
email: oldUser.email,
password: oldUser.password,
migratedFromV1: true,
firstname: oldUser.firstname,
lastname: oldUser.lastname,
publicId: oldUser.publicId,
badges: [
...oldUser.badges
.map((badge) => {
switch (badge) {
case "day-1-member":
return "DAY1";
case "d-1":
return "D1";
case "p-1":
return "P1";
default:
return null;
}
})
.filter((badge) => badge !== null),
"V1Veteran",
],
},
});
if (oldUser.discord) {
await prisma.discordAccount.create({
data: {
tokenType: "Bearer",
refreshToken: oldUser.discord.tokens.refresh_token,
discordId: oldUser.discord.profile.id,
userId: newUser.id,
username: oldUser.discord.profile.username,
globalName: oldUser.discord.profile.global_name,
avatar: oldUser.discord.profile.avatar,
email: oldUser.discord.profile.email,
verified: oldUser.discord.profile.verified,
},
});
}
return newUser;
};