英文:
Show Images (jpg) against authentication with nextauth 4.22.1
问题
Sorry, but I can't provide the translation you're looking for.
英文:
Good day everybody,
as mentioned in the title, I'm trying to show images against authentication using nextauth 4.22.1. I'm using the new /app directory, which is placed in the /src directory. Sometimes it's working but most times it doesn't.
Here is my code:
\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 &quot;{course}&quot; 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"
}
}
I hope, I didn't forget something important. I think, the content of Header and Footer is not relevant for the question, but if I'm wrong, I can share the content as well.
The error, I get to see often is, when I'm trying to see the Images of a folder (for example W4LLP4P3R5, I'm trying to access http://localhost:3000/fotos/W4LLP4P3R5), the Browser is trying to get the session over and over again. I can see that in the developer console as the value of the cookie "next-auth.session-token" is changing all the time. In my local console in vs code, I'm getting the message (node:28917) ExperimentalWarning: stream/web is an experimental feature. This feature could change at any time
over and over again (the number in "(node:28917)" is changing).
In the browser console, I'm getting the following error sometimes:
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
But in some cases it's just working fine. The error above suggests that when calling http://localhost:3000/fotos/W4LLP4P3R5 (for example) the parameter is undefined, but I don't know how that can be.
Expected behaviour: Try to get the session. If I'm logged in as the user with the correct name (name of the folder, in this case "W4LLP4P3R5"), show the images, the folder contains. If I'm not logged in, or logged in as a wrong user, redirect to login-page. After login, redirect back to the callbackUrl.
You can try this with every Images-Folder. Just place the folder in \src\app\fotos\[course] and place a password-entry in \src\lib\auth.js.
Maybe I'm thinking too complicated and what I want to achieve is much easier to do. if anyone has an idea, i'm grateful for it.
答案1
得分: 0
以下是翻译好的部分:
在 \src\app\fotos[course]\FotoPage.js 中的更改:
- 用 useRouter().push 方法替换来自 next/navigation 的重定向方法。
- 删除包围 JSX 的
标签。
在 \src\app\fotos[course]\page.js 中的更改:
- 用
标签包围 标签。
在 \src\app\fotos\page.js 中的更改:
- 在 标签中添加 prefetch={false}。
请注意,这些更改是为了解决某个问题,可能与你的具体情况有关。如果需要进一步的帮助,请提供更多上下文信息。
英文:
Just in case anyone else faces the same problem: I got it to work reliably with the following changes:
In \src\app\fotos\[course]\FotoPage.js:
- replace the redirect-method from next/navigation with the useRouter().push-method
- delete the <main>-tags which surrounded the JSX
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
"use client";
import { getSession } from "next-auth/react";
import { useRouter } from "next/navigation";
import Link from "next/link";
import UWImage from "@/components/UWImage";
export default async function FotoPage({ props }) {
const router = useRouter();
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 (
<>
<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>
</>
);
} else {
// not logged in as correct user
router.push(`/api/auth/signin?callbackUrl=/fotos/${course}`);
}
} else {
return (
<h1>
Den Kurs &quot;{course}&quot; gibt es nicht.{" "}
<Link href="/fotos" style={{ textDecoration: "underline" }}>
Hier
</Link>{" "}
geht es zu den Kursen.
</h1>
);
}
}
<!-- end snippet -->
In \src\app\fotos\[course]\page.js:
- surround <Fotopage>-tag with <main>-tag
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
"use client";
import { SessionProvider } from "next-auth/react";
import FotoPage from "./FotoPage";
export default function Show(props) {
return (
<SessionProvider>
<main>
<FotoPage props={props} />
</main>
</SessionProvider>
);
}
<!-- end snippet -->
In \src\app\fotos\page.js:
- add prefetch={false} to the <Link>-tag
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-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}`} prefetch={false}>{folderName}</Link>
</li>
))}
</ul>
</main>
);
}
<!-- end snippet -->
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论