大家好
如标题中所提到的,我试图使用nextauth 4.22.1来显示验证图像。我使用的是新的/app目录,它位于/src目录中。有时候它是有效的,但大多数时候它不是。
下面是我的代码:\src\app\fotos\page.js:
import Link from "next/link";
import { getFolders } from "./getFolders";
export default function Fotos() {
const folderNames = getFolders();
return (
<main>
<h2>Bestellung</h2>
<p>Hier kommen Fotos hin.</p>
<h3>Veranstaltungen:</h3>
<ul>
{folderNames.map((folderName) => (
<li key={folderName}>
<Link href={`/fotos/${folderName}`}>{folderName}</Link>
</li>
))}
</ul>
</main>
);
}
\src\app\fotos\getFolders.js:
import fs from "fs";
import path from "path";
export function getFolders() {
const directoryPath = path.join(process.cwd(), "/src/app/fotos/[course]");
const fileNames = fs.readdirSync(directoryPath);
return fileNames.filter((e) => {
return e !== ".gitignore" && e !== "page.js" && e !== "FotoPage.js";
});
}
\src\app\fotos[course]\page.js:
"use client";
import { SessionProvider } from "next-auth/react";
import FotoPage from "./FotoPage";
export default function Show(props) {
return (
<SessionProvider>
<FotoPage props={props} />
</SessionProvider>
);
}
\src\app\fotos[course]\FotoPage.js:
"use client";
import { getSession } from "next-auth/react";
import { redirect } from "next/navigation";
import Link from "next/link";
import UWImage from "@/components/UWImage";
export default async function FotoPage({ props }) {
const session = await getSession();
const folderListPromise = await fetch("/api/getfotofoldercontent");
const folderList = await folderListPromise.json();
const course = props.params.course;
if (folderList.folderNames.includes(course)) {
if (session?.user.name === course) {
const fileNamesPromise = await fetch(`/api/getfotofoldercontent/${course}`);
const fileNamesList = await fileNamesPromise.json();
return (
<main>
<h2>Fotos aus Kurs {course}</h2>
<div style={{ display: "flex", flexWrap: "wrap", gap: "10px", justifyContent: "center" }}>
{fileNamesList.fileNames.map((fileName) => (
<div key={fileName} style={{ textAlign: "center" }}>
<strong>{fileName}</strong>
<UWImage course={course} fileName={fileName} />
</div>
))}
</div>
</main>
);
} else { // not logged in as correct user
redirect(`/api/auth/signin?callbackUrl=/fotos/${course}`);
}
} else {
return (
<main>
<h1>
Den Kurs "{course}" gibt es nicht.{" "}
<Link href="/fotos" style={{ textDecoration: "underline" }}>
Hier
</Link>{" "}
geht es zu den Kursen.
</h1>
</main>
);
}
}
\src\components\UWImage.js:
"use client";
import Image from "next/image";
export default function UWImage({ course, fileName }) {
return (
<div>
<Image
src={`/api/getimage?folderName=${course}&fileName=${fileName}`}
width={400}
height={400}
style={{ width: "auto", height: "30vh", borderRadius: "3%" }}
alt={fileName}
/>
</div>
);
}
\src\app\API\getimage\route.js:
import { getServerSession } from "next-auth";
import { authOptions } from "@/lib/auth";
import { NextResponse } from "next/server";
import { readFileSync } from "fs";
import { join } from "path";
export async function GET(request) {
const url = new URL(request.url);
const folderName = url.searchParams.get("folderName");
const session = await getServerSession(authOptions);
if (session && session.user.name === folderName) {
const fileName = url.searchParams.get("fileName");
const imagePath = join(process.cwd(), "src/app/fotos/[course]/", folderName, fileName);
const image = readFileSync(imagePath);
return new NextResponse(image, { "Cache-Control": "private" });
} else {
return new NextResponse(null, { status: 401 });
}
}
\src\app\API\getfotofoldercontent[[...folder]]\route.js:
import { getServerSession } from "next-auth";
import { authOptions } from "@/lib/auth";
import { NextResponse } from "next/server";
import { getFolders } from "@/app/fotos/getFolders";
import { getFileNames } from "@/app/fotos/getFileNames";
export async function GET(params) {
const folderName = new URL(params.url).pathname.split("/")[3];
if (!folderName) {
return NextResponse.json({ folderNames: getFolders()});
}
const session = await getServerSession(authOptions);
if (session && session.user.name === folderName){
return NextResponse.json({ fileNames: getFileNames(folderName)});
}
return new NextResponse(null, { status: 401 });
}
\src\app\fotos\getFileNames.js:
import fs from "fs";
import path from "path";
export function getFileNames(folderName) {
const directoryPath = path.join(process.cwd(), "/src/app/fotos/[course]", folderName);
const fileNames = fs.readdirSync(directoryPath);
return fileNames;
}
\src\app\API\auth\signin\page.js:
import LoginForm from "@/components/LoginForm";
export default function LoginPage() {
return (
<main style={{ display: "flex", justifyContent: "center" }}>
<div className="LoginContainer">
<br />
<div className="LoginBackgroundImage">
<h1 className="LoginHeading">Login</h1>
</div>
<br />
<LoginForm />
</div>
</main>
);
}
\src\components\LoginForm.js:
"use client";
import { signIn } from "next-auth/react";
import { useSearchParams, useRouter } from "next/navigation";
import { useState } from "react";
export default function LoginForm() {
const router = useRouter();
const callbackUrl = useSearchParams().get("callbackUrl") || "/";
let [loading, setLoading] = useState(false);
let [course, setCourse] = useState(callbackUrl.split("/").pop());
let [password, setPassword] = useState("");
const [error, setError] = useState("");
async function onSubmit(e) {
e.preventDefault();
try {
setLoading(true);
const res = await signIn("credentials", {
redirect: false,
courseName: course,
password: password,
callbackUrl
});
setLoading(false);
if (!res?.error) {
setError("");
router.push(callbackUrl);
} else {
setError("falsches Passwort");
}
} catch (error) {
setLoading(false);
setError(error);
}
}
const handleChange = (event) => {
event.preventDefault();
const { name, value } = event.target;
if (name === "course") {
setCourse(value);
} else if (name === "password") {
setPassword(value);
}
};
return (
<form onSubmit={onSubmit}>
{error ? <strong className="w3-text-red">{error}</strong> : ""}
<div className="mb-6">
<input
style={{ margin: 5 }}
required
type="text"
className="input-box"
name="course"
value={course}
onChange={handleChange}
placeholder="KURS32XYZ"
/>
</div>
<div className="mb-6">
<input
style={{ margin: 5 }}
required
type="password"
className="input-box"
name="password"
value={password}
onChange={handleChange}
placeholder="Passwort"
/>
</div>
<button style={{ margin: 5, padding: 2 }} type="submit" disabled={loading}>
{loading ? "lädt..." : "Anmelden"}
</button>
</form>
);
}
\src\app\API\auth[...nextauth]\route.js:
import { authOptions } from "@/lib/auth";
import NextAuth from "next-auth";
const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };
\src\lib\auth.js:
import CredentialsProvider from "next-auth/providers/credentials";
// TODO: replace with real passwords
const passwordList = {
"5CR33N5H0T5": "12345",
W4LLP4P3R5: "54321",
FR4NC3: "france",
QW3RTZ: "qwertz",
V1T450L: "vitasol",
"3RINN3RUNG": "erinnerung",
H4LL0W: "halloween",
MILRI4: "milria"
};
function isCorrectCredentials(credentials) {
return passwordList[credentials.courseName] === credentials.password;
}
export const authOptions = {
session: {
strategy: "jwt"
},
providers: [
CredentialsProvider({
name: "credentials",
credentials: {
courseName: {
label: "Kursname",
type: "text",
placeholder: "KURS32XYZ"
},
password: { label: "Passwort", type: "password" }
},
async authorize(credentials) {
return isCorrectCredentials(credentials) ? { id: "1", name: credentials.courseName } : null;
}
})
],
pages: {
signIn: "api/auth/signin"
}
};
\src\app\layout.js:
import "./globals.css";
import "./w3.css";
import { Inter } from "next/font/google";
import Header from "@/components/Header";
import Footer from "@/components/Footer";
const inter = Inter({ subsets: ["latin"] });
export const metadata = {
title: "AquaFotos - Unterwasser-Bilder",
description: "Unterwasser-Fotos in bester Qualität"
};
export default function RootLayout({ children }) {
return (
<html lang="de">
<body className={inter.className}>
<Header />
{children}
<Footer />
</body>
</html>
);
}
\package.json:
{
"name": "aquafotos.com",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"eslint": "8.43.0",
"eslint-config-next": "^12.0.4",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-jsx-a11y": "^6.5.1",
"eslint-plugin-react": "^7.25.3",
"eslint-plugin-react-hooks": "^4.6.0",
"next": "^13.4.8-canary.2",
"next-auth": "^4.22.1",
"react": "18.2.0",
"react-dom": "18.2.0"
}
}
希望我没忘了什么重要的事。我认为,页眉和页脚的内容与问题无关,但如果我错了,我也可以分享内容。错误,我经常看到的是,当我试图看到一个文件夹的图像(例如W 4LLP 4P 3R 5,我试图访问http://localhost:3000/fotos/W 4LLP 4P 3R 5),浏览器试图一遍又一遍地获取会话。我可以在开发人员控制台中看到,cookie“next-auth.session-token”的值一直在变化。在vs代码的本地控制台中,我一次又一次地收到消息(node:28917) ExperimentalWarning: stream/web is an experimental feature. This feature could change at any time
(“(node:28917)”中的数字正在变化)。在浏览器控制台中,我有时会收到以下错误:
Uncaught (in promise) TypeError: param is undefined
FotoPage FotoPage.js:8
describeNativeComponentFrame react-dom.development.js:2534
describeFunctionComponentFrame react-dom.development.js:2629
describeFiber react-dom.development.js:2708
getStackByFiberInDevAndProd react-dom.development.js:2727
createCapturedValueAtFiber react-dom.development.js:14060
throwException react-dom.development.js:14550
throwAndUnwindWorkLoop react-dom.development.js:24676
renderRootConcurrent react-dom.development.js:24291
performConcurrentWorkOnRoot react-dom.development.js:23343
workLoop scheduler.development.js:261
flushWork scheduler.development.js:230
performWorkUntilDeadline scheduler.development.js:537
EventHandlerNonNull* scheduler.development.js:575
<anonymous> scheduler.development.js:638
NextJS 4
<anonymous> index.js:6
NextJS 4
<anonymous> react-dom.development.js:27
<anonymous> react-dom.development.js:36657
NextJS 4
<anonymous> index.js:37
NextJS 4
<anonymous> client.js:3
NextJS 4
<anonymous> app-index.js:14
NextJS 4
<anonymous> app-next-dev.js:8
appBootstrap app-bootstrap.js:58
loadScriptsInSequence app-bootstrap.js:23
appBootstrap app-bootstrap.js:57
<anonymous> app-next-dev.js:7
NextJS 7
FotoPage.js:8:39
但在某些情况下,它只是工作正常。上面的错误表明,当调用http://localhost:3000/fotos/W 4LLP 4P 3R 5(例如)时,参数未定义,但我不知道这是怎么回事。预期行为:尝试获得会话。如果我以正确名称(文件夹名称,在本例中为“W 4LLP 4P 3R 5”)的用户身份登录,则显示文件夹包含的图像。如果我没有登录,或者登录的用户不正确,请重定向到登录页面。登录后,重定向回callbackUrl。您可以尝试在每个图像文件夹中执行此操作。只需将文件夹放在\src\app\fotos[course]中,并将密码输入放在\src\lib\auth.js中。也许我想得太复杂了,我想达到的目标要容易得多。如果有人有主意,我很感激。
1条答案
按热度按时间lmyy7pcs1#
如果其他人也遇到同样的问题:我通过以下更改使其可靠地工作:
在\src\app\fotos[course]\FotoPage.js中:
在\src\app\fotos[course]\page.js中:
在\src\app\fotos\page.js中: