Resources Seite für Desktop-client
This commit is contained in:
43
apps/hub/app/(app)/resources/_components/Card.tsx
Normal file
43
apps/hub/app/(app)/resources/_components/Card.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
BIN
apps/hub/app/(app)/resources/_components/desktop-client.png
Normal file
BIN
apps/hub/app/(app)/resources/_components/desktop-client.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 50 KiB |
18
apps/hub/app/(app)/resources/page.tsx
Normal file
18
apps/hub/app/(app)/resources/page.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -2,25 +2,32 @@
|
||||
|
||||
import { prisma } from "@repo/db";
|
||||
import { sendMailByTemplate } from "../../../helper/mail";
|
||||
import OLD_USER from "../../api/auth/[...nextauth]/var.User.json";
|
||||
import bcrypt from "bcryptjs";
|
||||
import { createNewUserFromOld, OldUser } from "../../../types/oldUser";
|
||||
|
||||
export const resetPassword = async (email: string) => {
|
||||
try {
|
||||
const user = await prisma.user.findFirst({
|
||||
let user = await prisma.user.findFirst({
|
||||
where: {
|
||||
email,
|
||||
},
|
||||
});
|
||||
|
||||
const oldUser = (OLD_USER as OldUser[]).find((u) => u.email === email);
|
||||
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);
|
||||
crypto.getRandomValues(array);
|
||||
const password = Array.from(array, (byte) =>
|
||||
("0" + (byte % 36).toString(36)).slice(-1),
|
||||
).join("");
|
||||
const password = Array.from(array, (byte) => ("0" + (byte % 36).toString(36)).slice(-1)).join(
|
||||
"",
|
||||
);
|
||||
const hashedPassword = await bcrypt.hash(password, 12);
|
||||
await prisma.user.update({
|
||||
where: {
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
LockClosedIcon,
|
||||
RocketIcon,
|
||||
ReaderIcon,
|
||||
DownloadIcon,
|
||||
} from "@radix-ui/react-icons";
|
||||
import Link from "next/link";
|
||||
import { WarningAlert } from "./ui/PageAlert";
|
||||
@@ -42,6 +43,12 @@ export const VerticalNav = async () => {
|
||||
Einstellungen
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/resources">
|
||||
<DownloadIcon />
|
||||
Downloads
|
||||
</Link>
|
||||
</li>
|
||||
{viewAdminMenu && (
|
||||
<li>
|
||||
<details open>
|
||||
|
||||
@@ -4,7 +4,7 @@ import Credentials from "next-auth/providers/credentials";
|
||||
import { DiscordAccount, prisma, User } from "@repo/db";
|
||||
import bcrypt from "bcryptjs";
|
||||
import oldUser from "./var.User.json";
|
||||
import { OldUser } from "../../../../types/oldUser";
|
||||
import { createNewUserFromOld, OldUser } from "../../../../types/oldUser";
|
||||
import { sendVerificationLink } from "(app)/admin/user/action";
|
||||
|
||||
export const options: AuthOptions = {
|
||||
@@ -28,48 +28,7 @@ export const options: AuthOptions = {
|
||||
"v1 User Passwords match:",
|
||||
bcrypt.compareSync(credentials.password, v1User.password),
|
||||
);
|
||||
const newUser = await prisma.user.create({
|
||||
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,
|
||||
},
|
||||
});
|
||||
}
|
||||
const newUser = await createNewUserFromOld(v1User);
|
||||
await sendVerificationLink(newUser.id);
|
||||
return newUser;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { prisma } from "@repo/db";
|
||||
|
||||
export interface OldUser {
|
||||
firstname: 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;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user