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 { 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: {
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user