feat : update
This commit is contained in:
@@ -32,8 +32,8 @@ formula fixtures. Their guide text and media are not imported or published.
|
||||
| `/` | Searchable public character directory |
|
||||
| `/[character]` | Redirect to the first visible page |
|
||||
| `/[character]/[page]` | Render a public guide page |
|
||||
| `/login` | Public account sign-in |
|
||||
| `/register` | Public account registration |
|
||||
| `/auth/login` | Public account sign-in |
|
||||
| `/auth/register` | Public account registration |
|
||||
| `/admin` | Character and page overview |
|
||||
| `/admin/[character]/[page]` | Visual page editor |
|
||||
| `/admin/create` | Create a structured guide from the synced character catalog |
|
||||
@@ -66,7 +66,7 @@ cp .env.example .env
|
||||
`.env.example` contains placeholders only. Configure `.env` yourself; it is
|
||||
ignored by Git and must never be committed. `BETTER_AUTH_URL` must exactly
|
||||
match the application origin. Set `BASE_URL` to the public site origin for SEO
|
||||
metadata, sitemap, and robots.txt. Visitors can create public accounts at `/register`.
|
||||
metadata, sitemap, and robots.txt. Visitors can create public accounts at `/auth/register`.
|
||||
|
||||
Prepare the database and start the application:
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ export default async function StructuredEditorPage({
|
||||
params: Promise<{ character: string; page: string }>;
|
||||
}) {
|
||||
await connection();
|
||||
if (!(await getAdminSession())) redirect("/login?next=%2Fadmin");
|
||||
if (!(await getAdminSession())) redirect("/auth/login?next=%2Fadmin");
|
||||
const { character, page } = await params;
|
||||
const guide = await getGuideEditorHeader(character);
|
||||
if (!guide) notFound();
|
||||
|
||||
@@ -27,7 +27,7 @@ export const instant = false;
|
||||
|
||||
export default async function GuideOverviewPage({ params }: { params: Promise<{ character: string }> }) {
|
||||
await connection();
|
||||
if (!(await getAdminSession())) redirect("/login?next=%2Fadmin");
|
||||
if (!(await getAdminSession())) redirect("/auth/login?next=%2Fadmin");
|
||||
const { character } = await params;
|
||||
const guide = await getGuideByCharacterKey(character);
|
||||
if (!guide) notFound();
|
||||
|
||||
@@ -14,7 +14,7 @@ export default async function GuidePreviewPage({
|
||||
params: Promise<{ character: string; page?: string[] }>;
|
||||
}) {
|
||||
await connection();
|
||||
if (!(await getAdminSession())) redirect("/login?next=%2Fadmin");
|
||||
if (!(await getAdminSession())) redirect("/auth/login?next=%2Fadmin");
|
||||
const { character, page } = await params;
|
||||
if (page && page.length > 1) notFound();
|
||||
const header = await getGuideEditorHeader(character);
|
||||
|
||||
@@ -21,7 +21,7 @@ export default async function AuditLogPage({
|
||||
searchParams: Promise<Record<string, string | string[] | undefined>>;
|
||||
}) {
|
||||
await connection();
|
||||
if (!(await getAdminSession())) redirect("/login?next=%2Fadmin");
|
||||
if (!(await getAdminSession())) redirect("/auth/login?next=%2Fadmin");
|
||||
|
||||
const query = await searchParams;
|
||||
const rawPage = Array.isArray(query.page) ? query.page[0] : query.page;
|
||||
|
||||
@@ -19,7 +19,7 @@ export const instant = false;
|
||||
export default async function AdminCommissionPage({ searchParams }: PageProps<"/admin/commission">) {
|
||||
await connection();
|
||||
const session = await getAdminSession();
|
||||
if (!session) redirect("/login?next=%2Fadmin");
|
||||
if (!session) redirect("/auth/login?next=%2Fadmin");
|
||||
const query = await searchParams;
|
||||
const rawPage = Number(query.page ?? 1);
|
||||
const page = Number.isInteger(rawPage) && rawPage > 0 ? Math.min(rawPage, 10000) : 1;
|
||||
|
||||
@@ -11,7 +11,7 @@ import { getActiveCatalog, listGuideCards } from "@/lib/guides/queries";
|
||||
|
||||
export default async function CreateGuidePage() {
|
||||
await connection();
|
||||
if (!(await getAdminSession())) redirect("/login?next=%2Fadmin");
|
||||
if (!(await getAdminSession())) redirect("/auth/login?next=%2Fadmin");
|
||||
const [catalog, guides] = await Promise.all([getActiveCatalog(), listGuideCards()]);
|
||||
if (!catalog) redirect("/admin?sync=required");
|
||||
return (
|
||||
|
||||
@@ -9,7 +9,7 @@ import { getCatalogOptions } from "@/lib/guides/queries";
|
||||
|
||||
export default async function GlossaryPage() {
|
||||
await connection();
|
||||
if (!(await getAdminSession())) redirect("/login?next=%2Fadmin");
|
||||
if (!(await getAdminSession())) redirect("/auth/login?next=%2Fadmin");
|
||||
const [catalog, aliases] = await Promise.all([
|
||||
getCatalogOptions(),
|
||||
listGlossaryAliases(),
|
||||
|
||||
@@ -9,7 +9,7 @@ export default async function ImageGeneratorLayout({
|
||||
children,
|
||||
}: LayoutProps<"/admin/image">) {
|
||||
await connection();
|
||||
if (!(await getAdminSession())) redirect("/login?next=%2Fadmin");
|
||||
if (!(await getAdminSession())) redirect("/auth/login?next=%2Fadmin");
|
||||
|
||||
return (
|
||||
<div className="min-h-svh">
|
||||
|
||||
@@ -5,7 +5,15 @@ import { TooltipProvider } from "@/components/ui/tooltip";
|
||||
export default function AdminLayout({ children }: LayoutProps<"/admin">) {
|
||||
return (
|
||||
<TooltipProvider>
|
||||
<Suspense fallback={null}>{children}</Suspense>
|
||||
<Suspense
|
||||
fallback={
|
||||
<main className="mx-auto flex min-h-svh w-full max-w-7xl items-start p-4 sm:p-6 lg:p-8">
|
||||
<p role="status" className="text-sm text-muted-foreground">กำลังโหลด…</p>
|
||||
</main>
|
||||
}
|
||||
>
|
||||
{children}
|
||||
</Suspense>
|
||||
</TooltipProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
export default function AdminLoading() {
|
||||
return (
|
||||
<main className="mx-auto flex min-h-svh w-full max-w-7xl items-start p-4 sm:p-6 lg:p-8">
|
||||
<p role="status" className="text-sm text-muted-foreground">กำลังโหลด…</p>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
+1
-1
@@ -12,7 +12,7 @@ export const instant = false;
|
||||
|
||||
export default async function AdminPage() {
|
||||
await connection();
|
||||
if (!(await getAdminSession())) redirect("/login?next=%2Fadmin");
|
||||
if (!(await getAdminSession())) redirect("/auth/login?next=%2Fadmin");
|
||||
const [guides, trash, catalogVersion] = await Promise.all([
|
||||
listGuideCards(),
|
||||
listGuideCards(true),
|
||||
|
||||
@@ -11,7 +11,7 @@ import { getStygianSchedule, listStygianSchedules } from "@/lib/stygian/reposito
|
||||
|
||||
export default async function AdminStygianPage({ searchParams }: { searchParams: Promise<Record<string, string | string[] | undefined>> }) {
|
||||
await connection();
|
||||
if (!(await getAdminSession())) redirect("/login?next=%2Fadmin");
|
||||
if (!(await getAdminSession())) redirect("/auth/login?next=%2Fadmin");
|
||||
const query = await searchParams;
|
||||
const schedules = await listStygianSchedules();
|
||||
const raw = Array.isArray(query.schedule) ? query.schedule[0] : query.schedule;
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
export default async function TemplatesPage() {
|
||||
redirect("/admin/create");
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import { safeAuthReturnPath } from "@/lib/auth/return-path";
|
||||
|
||||
export const instant = false;
|
||||
|
||||
export default async function CheckEmailPage({ searchParams }: PageProps<"/check-email">) {
|
||||
export default async function CheckEmailPage({ searchParams }: PageProps<"/auth/check-email">) {
|
||||
await connection();
|
||||
const { email, next, setup } = await searchParams;
|
||||
return <div className="min-h-svh"><SiteHeader /><main className="grid min-h-[calc(100svh-4rem)] place-items-center p-4">
|
||||
@@ -3,7 +3,7 @@ import { AccountPage } from "@/components/auth/account-page";
|
||||
|
||||
export const instant = false;
|
||||
|
||||
export default async function LoginPage({ searchParams }: PageProps<"/login">) {
|
||||
export default async function LoginPage({ searchParams }: PageProps<"/auth/login">) {
|
||||
await connection();
|
||||
const { next, verified, error } = await searchParams;
|
||||
return <AccountPage mode="login" next={next} verified={verified === "1" && !error}
|
||||
@@ -4,7 +4,7 @@ import { EmailActionForm } from "@/components/auth/email-action-form";
|
||||
|
||||
export const instant = false;
|
||||
|
||||
export default async function ResetPasswordPage({ searchParams }: PageProps<"/password-reset">) {
|
||||
export default async function ResetPasswordPage({ searchParams }: PageProps<"/auth/password-reset">) {
|
||||
await connection();
|
||||
const { token, error } = await searchParams;
|
||||
return <div className="min-h-svh"><SiteHeader /><main className="grid min-h-[calc(100svh-4rem)] place-items-center p-4">
|
||||
@@ -3,7 +3,7 @@ import { AccountPage } from "@/components/auth/account-page";
|
||||
|
||||
export const instant = false;
|
||||
|
||||
export default async function RegisterPage({ searchParams }: PageProps<"/register">) {
|
||||
export default async function RegisterPage({ searchParams }: PageProps<"/auth/register">) {
|
||||
await connection();
|
||||
const { next } = await searchParams;
|
||||
return <AccountPage mode="register" next={next} />;
|
||||
@@ -1,5 +0,0 @@
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
export default function LegacyCommissionLoginPage() {
|
||||
redirect("/login?next=%2Fcommission");
|
||||
}
|
||||
@@ -25,8 +25,8 @@ async function CommissionContent() {
|
||||
if (!session) return <div className="flex flex-col gap-4 rounded-xl border p-6">
|
||||
<p>เข้าสู่ระบบหรือสมัครสมาชิกก่อนเริ่มคำขอ</p>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Button nativeButton={false} render={<Link href="/login?next=%2Fcommission" />}>เข้าสู่ระบบ</Button>
|
||||
<Button variant="outline" nativeButton={false} render={<Link href="/register?next=%2Fcommission" />}>สมัครสมาชิก</Button>
|
||||
<Button nativeButton={false} render={<Link href="/auth/login?next=%2Fcommission" />}>เข้าสู่ระบบ</Button>
|
||||
<Button variant="outline" nativeButton={false} render={<Link href="/auth/register?next=%2Fcommission" />}>สมัครสมาชิก</Button>
|
||||
</div>
|
||||
</div>;
|
||||
const catalog = await getCatalogOptions();
|
||||
@@ -43,7 +43,7 @@ export default function CommissionPage() {
|
||||
<main className="mx-auto flex w-full max-w-5xl flex-col gap-6 p-4 py-10 sm:p-8">
|
||||
<header><h1 className="font-heading text-3xl font-semibold">Commission</h1>
|
||||
<p className="text-muted-foreground">ขอทีม เปรียบเทียบอาวุธหรือกลุ่มดาวตามที่ต้องการ</p></header>
|
||||
<Suspense fallback={<p>กำลังโหลด...</p>}><CommissionContent /></Suspense>
|
||||
<Suspense fallback={null}><CommissionContent /></Suspense>
|
||||
</main>
|
||||
</div>;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ export default async function CommissionPayPage({ params }: PageProps<"/commissi
|
||||
await connection();
|
||||
const { id } = await params;
|
||||
const session = await getCustomerSession();
|
||||
if (!session) redirect(`/login?next=${encodeURIComponent(`/commission/pay/${id}`)}`);
|
||||
if (!session) redirect(`/auth/login?next=${encodeURIComponent(`/commission/pay/${id}`)}`);
|
||||
const [checkout] = await getDb().select().from(commissionCheckouts)
|
||||
.where(and(eq(commissionCheckouts.id, id), eq(commissionCheckouts.userId, session.user.id))).limit(1);
|
||||
if (!checkout) notFound();
|
||||
|
||||
@@ -16,7 +16,7 @@ export const instant = false;
|
||||
export default async function CommissionTicketsPage() {
|
||||
await connection();
|
||||
const session = await getCustomerSession();
|
||||
if (!session) redirect("/login?next=%2Fcommission%2Ftickets");
|
||||
if (!session) redirect("/auth/login?next=%2Fcommission%2Ftickets");
|
||||
const [tickets, pending] = await Promise.all([
|
||||
getDb().select({ ticket: commissionTickets, amount: commissionCheckouts.amountBaht })
|
||||
.from(commissionTickets).innerJoin(commissionCheckouts, eq(commissionTickets.checkoutId, commissionCheckouts.id))
|
||||
|
||||
@@ -20,11 +20,11 @@ export default async function ProfilePage({ searchParams }: PageProps<"/profile"
|
||||
const nextPath = safeAuthReturnPath(next);
|
||||
const setupNextPath = nextPath === "/profile" || nextPath.startsWith("/profile?") ? "/" : nextPath;
|
||||
const session = await getCustomerSession();
|
||||
if (!session) redirect("/login?next=%2Fprofile");
|
||||
if (!session) redirect("/auth/login?next=%2Fprofile");
|
||||
const [user] = await getDb().select({ name: users.name, email: users.email,
|
||||
emailVerified: users.emailVerified, image: users.image })
|
||||
.from(users).where(eq(users.id, session.user.id)).limit(1);
|
||||
if (!user) redirect("/login?next=%2Fprofile");
|
||||
if (!user) redirect("/auth/login?next=%2Fprofile");
|
||||
return <div className="min-h-svh">{isAuthorizedAdmin(session.user) ? <AdminHeader /> : <SiteHeader />}
|
||||
<main className="mx-auto max-w-5xl p-4 py-10 sm:p-8">
|
||||
<h1 className="mb-6 font-heading text-3xl font-semibold">โปรไฟล์ของฉัน</h1>
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ export default function robots(): MetadataRoute.Robots {
|
||||
rules: {
|
||||
userAgent: "*",
|
||||
allow: "/",
|
||||
disallow: ["/admin", "/api", "/login", "/register"],
|
||||
disallow: ["/admin", "/api", "/auth", "/login", "/register"],
|
||||
},
|
||||
sitemap: `${siteUrl}/sitemap.xml`,
|
||||
};
|
||||
|
||||
@@ -2579,7 +2579,7 @@ function TeamEditor({
|
||||
const [importOpen, setImportOpen] = useState(false);
|
||||
const [importFiles, setImportFiles] = useState<File[]>([]);
|
||||
const [guideImportOpen, setGuideImportOpen] = useState(false);
|
||||
const [importGuides, setImportGuides] = useState<Array<{ id: string; name: string; characterKey: string; isPublic: boolean; coverUrl: string }> | null>(null);
|
||||
const [importGuides, setImportGuides] = useState<Array<{ id: string; name: string; characterKey: string; coverUrl: string }> | null>(null);
|
||||
const [activeImportGuide, setActiveImportGuide] = useState<TeamImportGuide | null>(null);
|
||||
const [selectedImports, setSelectedImports] = useState<Record<string, Omit<TeamToolValue, "clientId">>>({});
|
||||
const [guideImportLoading, setGuideImportLoading] = useState<"guides" | "teams" | null>(null);
|
||||
@@ -3998,11 +3998,6 @@ function TeamEditor({
|
||||
<CardHeader className="pt-4">
|
||||
<CardTitle>{sourceGuide.name}</CardTitle>
|
||||
<CardDescription>{sourceGuide.characterKey}</CardDescription>
|
||||
<CardAction>
|
||||
<Badge variant={sourceGuide.isPublic ? "default" : "secondary"}>
|
||||
{sourceGuide.isPublic ? "Published" : "Private"}
|
||||
</Badge>
|
||||
</CardAction>
|
||||
</CardHeader>
|
||||
<CardFooter className="relative pb-4">
|
||||
<Button type="button" size="xs" variant="outline" onClick={() => void enterImportGuide(sourceGuide.id)}>
|
||||
|
||||
@@ -16,7 +16,6 @@ import {
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
} from "@/components/ui/alert-dialog";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Spinner } from "@/components/ui/spinner";
|
||||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
||||
@@ -61,9 +60,6 @@ export function GuideVisibilityControl({
|
||||
|
||||
return (
|
||||
<div className={cn("flex flex-wrap items-center gap-2", className)}>
|
||||
<Badge variant={isPublic ? "default" : "secondary"}>
|
||||
{isPublic ? "Public" : "Private"}
|
||||
</Badge>
|
||||
<Button
|
||||
type="button"
|
||||
variant={isPublic ? "outline" : "default"}
|
||||
|
||||
@@ -157,7 +157,7 @@ export function AccountForm({
|
||||
fetchOptions: { headers: captcha ? { "x-captcha-response": token } : undefined },
|
||||
});
|
||||
if (result.error?.code === "EMAIL_NOT_VERIFIED") {
|
||||
router.push(`/check-email?email=${encodeURIComponent(email)}&next=${encodeURIComponent(nextPath)}`);
|
||||
router.push(`/auth/check-email?email=${encodeURIComponent(email)}&next=${encodeURIComponent(nextPath)}`);
|
||||
return;
|
||||
}
|
||||
if (result.error) throw new Error(result.error.message ?? "เข้าสู่ระบบไม่สำเร็จ");
|
||||
@@ -262,15 +262,15 @@ export function AccountForm({
|
||||
</FieldGroup>
|
||||
</form>
|
||||
{register ? <Button variant="link" nativeButton={false}
|
||||
render={<Link href={`/login${nextPath === "/" ? "" : `?next=${encodeURIComponent(nextPath)}`}`} />}>
|
||||
render={<Link href={`/auth/login${nextPath === "/" ? "" : `?next=${encodeURIComponent(nextPath)}`}`} />}>
|
||||
มีบัญชีแล้ว? เข้าสู่ระบบ
|
||||
</Button> : <div className="flex items-center justify-between gap-2">
|
||||
<Button variant="link" size="sm" className="px-0" nativeButton={false}
|
||||
render={<Link href={`/register${nextPath === "/" ? "" : `?next=${encodeURIComponent(nextPath)}`}`} />}>
|
||||
render={<Link href={`/auth/register${nextPath === "/" ? "" : `?next=${encodeURIComponent(nextPath)}`}`} />}>
|
||||
สมัครสมาชิก
|
||||
</Button>
|
||||
<Button variant="link" size="sm" className="px-0" nativeButton={false}
|
||||
render={<Link href="/forgot-password" />}>
|
||||
render={<Link href="/auth/forgot-password" />}>
|
||||
ลืมรหัสผ่าน?
|
||||
</Button>
|
||||
</div>}
|
||||
|
||||
@@ -17,7 +17,7 @@ export function AccountMenu({ admin = false }: { admin?: boolean }) {
|
||||
async function signOut() {
|
||||
await unsubscribeCommissionPush().catch(() => undefined);
|
||||
await authClient.signOut();
|
||||
router.push(admin ? "/login?next=%2Fadmin" : "/commission");
|
||||
router.push(admin ? "/auth/login?next=%2Fadmin" : "/commission");
|
||||
router.refresh();
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ export function EmailActionForm({ mode, token = "", invalidToken = false }: {
|
||||
setError("");
|
||||
try {
|
||||
if (mode === "forgot") {
|
||||
const result = await authClient.requestPasswordReset({ email: address, redirectTo: "/reset-password" });
|
||||
const result = await authClient.requestPasswordReset({ email: address, redirectTo: "/auth/password-reset" });
|
||||
if (result.error) throw new Error("ส่งลิงก์รีเซ็ตไม่สำเร็จ กรุณาลองอีกครั้ง");
|
||||
} else {
|
||||
const password = String(form.get("password") ?? "");
|
||||
@@ -74,10 +74,10 @@ export function EmailActionForm({ mode, token = "", invalidToken = false }: {
|
||||
mode === "forgot" ? "ส่งลิงก์รีเซ็ต" : "ตั้งรหัสผ่าน"}</Button>
|
||||
</FieldGroup>
|
||||
</form>}
|
||||
{mode === "reset" && !token && <Button variant="link" nativeButton={false} render={<Link href="/forgot-password" />}>
|
||||
{mode === "reset" && !token && <Button variant="link" nativeButton={false} render={<Link href="/auth/forgot-password" />}>
|
||||
ขอลิงก์รีเซ็ตใหม่
|
||||
</Button>}
|
||||
<Button variant="link" nativeButton={false} render={<Link href="/login" />}>กลับไปเข้าสู่ระบบ</Button>
|
||||
<Button variant="link" nativeButton={false} render={<Link href="/auth/login" />}>กลับไปเข้าสู่ระบบ</Button>
|
||||
</CardContent>
|
||||
</Card>;
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ export function RegistrationOtpForm({ email, nextPath, setup }: {
|
||||
<Button variant="link" onClick={resend} disabled={busy || sending}>
|
||||
{sending ? "กำลังส่ง..." : "ส่งรหัสใหม่"}
|
||||
</Button>
|
||||
<Button variant="link" nativeButton={false} render={<Link href="/login" />}>กลับไปเข้าสู่ระบบ</Button>
|
||||
<Button variant="link" nativeButton={false} render={<Link href="/auth/login" />}>กลับไปเข้าสู่ระบบ</Button>
|
||||
</CardContent>
|
||||
</Card>;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ describe("public auth return path", () => {
|
||||
});
|
||||
|
||||
it("rejects external destinations and auth loops", () => {
|
||||
for (const value of ["https://example.com", "//example.com", "/\\example.com", "/login", "/lo%67in", "/register", "/admin/login", 4])
|
||||
for (const value of ["https://example.com", "//example.com", "/\\example.com", "/login", "/lo%67in", "/register", "/auth/login", "/auth/register", "/auth/check-email", "/auth/password-reset", "/admin/login", 4])
|
||||
expect(safeAuthReturnPath(value)).toBe("/");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@ export function safeAuthReturnPath(value: unknown): string {
|
||||
try {
|
||||
const url = new URL(value, "https://auth.local");
|
||||
const path = decodeURIComponent(url.pathname);
|
||||
if (url.origin !== "https://auth.local" || ["/login", "/register", "/commission/login", "/admin/login"].includes(path))
|
||||
if (url.origin !== "https://auth.local" || path === "/auth" || path.startsWith("/auth/") || ["/login", "/register", "/commission/login", "/admin/login"].includes(path))
|
||||
return "/";
|
||||
return `${url.pathname}${url.search}${url.hash}`;
|
||||
} catch {
|
||||
|
||||
@@ -187,7 +187,6 @@ export async function listAdminTeamImportGuides(excludeGuideId: string) {
|
||||
id: guides.id,
|
||||
name: guides.name,
|
||||
characterKey: guides.characterKey,
|
||||
isPublic: guides.isPublic,
|
||||
coverObjectKey: media.objectKey,
|
||||
})
|
||||
.from(guides)
|
||||
|
||||
@@ -7,10 +7,6 @@ const nextConfig: NextConfig = {
|
||||
async headers() {
|
||||
return [{ source: "/:path*", headers: securityHeaders(process.env.NODE_ENV === "production", "https://cdn.jsdelivr.net") }];
|
||||
},
|
||||
async rewrites() {
|
||||
// The reset-password segment resolves to the dynamic guide route in this Next version.
|
||||
return { beforeFiles: [{ source: "/reset-password", destination: "/password-reset" }] };
|
||||
},
|
||||
cacheComponents: true,
|
||||
cacheHandlers: {
|
||||
remote: resolve(process.cwd(), "cache-handlers/redis-handler.cjs"),
|
||||
|
||||
@@ -53,7 +53,7 @@ describe("public rendering performance", () => {
|
||||
rules: {
|
||||
userAgent: "*",
|
||||
allow: "/",
|
||||
disallow: ["/admin", "/api", "/login", "/register"],
|
||||
disallow: ["/admin", "/api", "/auth", "/login", "/register"],
|
||||
},
|
||||
sitemap: "https://guide.example.test/sitemap.xml",
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user