Fix first setup server error handling
This commit is contained in:
@@ -8,6 +8,8 @@ import { requireInitialSetup } from "@/lib/setup";
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
|
|
||||||
|
export const dynamic = "force-dynamic";
|
||||||
|
|
||||||
export default async function AdminPage() {
|
export default async function AdminPage() {
|
||||||
await requireInitialSetup();
|
await requireInitialSetup();
|
||||||
const user = await requireCurrentUser();
|
const user = await requireCurrentUser();
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import { requireCurrentUser, userIsAdmin } from "@/lib/session";
|
|||||||
import { requireInitialSetup } from "@/lib/setup";
|
import { requireInitialSetup } from "@/lib/setup";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
|
||||||
|
export const dynamic = "force-dynamic";
|
||||||
|
|
||||||
export default async function DashboardPage() {
|
export default async function DashboardPage() {
|
||||||
await requireInitialSetup();
|
await requireInitialSetup();
|
||||||
const user = await requireCurrentUser();
|
const user = await requireCurrentUser();
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import { requireCurrentUser, userIsAdmin } from "@/lib/session";
|
|||||||
import { requireInitialSetup } from "@/lib/setup";
|
import { requireInitialSetup } from "@/lib/setup";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
|
||||||
|
export const dynamic = "force-dynamic";
|
||||||
|
|
||||||
export default async function FriendsPage() {
|
export default async function FriendsPage() {
|
||||||
await requireInitialSetup();
|
await requireInitialSetup();
|
||||||
const user = await requireCurrentUser();
|
const user = await requireCurrentUser();
|
||||||
|
|||||||
@@ -416,6 +416,17 @@ select {
|
|||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form-error {
|
||||||
|
border: 1px solid color-mix(in srgb, var(--danger) 45%, var(--border));
|
||||||
|
border-radius: 8px;
|
||||||
|
background: color-mix(in srgb, var(--danger) 10%, var(--panel));
|
||||||
|
color: var(--danger);
|
||||||
|
margin: 0 0 14px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 1100px) {
|
@media (max-width: 1100px) {
|
||||||
.room-layout {
|
.room-layout {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import { loginUser } from "@/lib/user-actions";
|
|||||||
import { hasAdminUser } from "@/lib/setup";
|
import { hasAdminUser } from "@/lib/setup";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
|
|
||||||
|
export const dynamic = "force-dynamic";
|
||||||
|
|
||||||
export default async function LoginPage() {
|
export default async function LoginPage() {
|
||||||
if (!(await hasAdminUser())) {
|
if (!(await hasAdminUser())) {
|
||||||
redirect("/setup");
|
redirect("/setup");
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
import { hasAdminUser } from "@/lib/setup";
|
import { hasAdminUser } from "@/lib/setup";
|
||||||
|
|
||||||
|
export const dynamic = "force-dynamic";
|
||||||
|
|
||||||
export default async function HomePage() {
|
export default async function HomePage() {
|
||||||
if (!(await hasAdminUser())) {
|
if (!(await hasAdminUser())) {
|
||||||
redirect("/setup");
|
redirect("/setup");
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import { registerUser } from "@/lib/user-actions";
|
|||||||
import { hasAdminUser } from "@/lib/setup";
|
import { hasAdminUser } from "@/lib/setup";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
|
|
||||||
|
export const dynamic = "force-dynamic";
|
||||||
|
|
||||||
export default async function RegisterPage() {
|
export default async function RegisterPage() {
|
||||||
if (!(await hasAdminUser())) {
|
if (!(await hasAdminUser())) {
|
||||||
redirect("/setup");
|
redirect("/setup");
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import { requireCurrentUser, userIsAdmin } from "@/lib/session";
|
|||||||
import { requireInitialSetup } from "@/lib/setup";
|
import { requireInitialSetup } from "@/lib/setup";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
|
|
||||||
|
export const dynamic = "force-dynamic";
|
||||||
|
|
||||||
export default async function RoomPage({ params }: { params: Promise<{ slug: string }> }) {
|
export default async function RoomPage({ params }: { params: Promise<{ slug: string }> }) {
|
||||||
await requireInitialSetup();
|
await requireInitialSetup();
|
||||||
const user = await requireCurrentUser();
|
const user = await requireCurrentUser();
|
||||||
|
|||||||
@@ -7,14 +7,21 @@ import { hasAdminUser } from "@/lib/setup";
|
|||||||
|
|
||||||
export const dynamic = "force-dynamic";
|
export const dynamic = "force-dynamic";
|
||||||
|
|
||||||
|
function normalizeUsername(value: FormDataEntryValue | null) {
|
||||||
|
return String(value || "")
|
||||||
|
.trim()
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/[^a-z0-9_-]/g, "");
|
||||||
|
}
|
||||||
|
|
||||||
async function createFirstAdmin(formData: FormData) {
|
async function createFirstAdmin(formData: FormData) {
|
||||||
"use server";
|
"use server";
|
||||||
|
|
||||||
const username = String(formData.get("username") || "").trim().toLowerCase();
|
const username = normalizeUsername(formData.get("username"));
|
||||||
const password = String(formData.get("password") || "");
|
const password = String(formData.get("password") || "");
|
||||||
|
|
||||||
if (!username || password.length < 10) {
|
if (!username || password.length < 10) {
|
||||||
throw new Error("Username is required and password must be at least 10 characters.");
|
redirect("/setup?error=invalid");
|
||||||
}
|
}
|
||||||
|
|
||||||
const existingAdmin = await prisma.userRole.findFirst({
|
const existingAdmin = await prisma.userRole.findFirst({
|
||||||
@@ -26,6 +33,15 @@ async function createFirstAdmin(formData: FormData) {
|
|||||||
redirect("/login");
|
redirect("/login");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const existingUser = await prisma.user.findUnique({
|
||||||
|
where: { username },
|
||||||
|
select: { id: true }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (existingUser) {
|
||||||
|
redirect("/setup?error=username");
|
||||||
|
}
|
||||||
|
|
||||||
const passwordHash = await hash(password, 12);
|
const passwordHash = await hash(password, 12);
|
||||||
|
|
||||||
const user = await prisma.$transaction(async (tx) => {
|
const user = await prisma.$transaction(async (tx) => {
|
||||||
@@ -83,10 +99,11 @@ async function createFirstAdmin(formData: FormData) {
|
|||||||
redirect("/dashboard");
|
redirect("/dashboard");
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function SetupPage() {
|
export default async function SetupPage({ searchParams }: { searchParams: Promise<{ error?: string }> }) {
|
||||||
if (await hasAdminUser()) {
|
if (await hasAdminUser()) {
|
||||||
redirect("/login");
|
redirect("/login");
|
||||||
}
|
}
|
||||||
|
const { error } = await searchParams;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="auth-page">
|
<main className="auth-page">
|
||||||
@@ -95,6 +112,7 @@ export default async function SetupPage() {
|
|||||||
<h1>WatchLink first setup</h1>
|
<h1>WatchLink first setup</h1>
|
||||||
<p>Create the first admin account. This screen locks after setup.</p>
|
<p>Create the first admin account. This screen locks after setup.</p>
|
||||||
</div>
|
</div>
|
||||||
|
{error ? <p className="form-error">{setupErrorMessage(error)}</p> : null}
|
||||||
<form className="form" action={createFirstAdmin}>
|
<form className="form" action={createFirstAdmin}>
|
||||||
<label>
|
<label>
|
||||||
Username
|
Username
|
||||||
@@ -112,3 +130,8 @@ export default async function SetupPage() {
|
|||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setupErrorMessage(error: string) {
|
||||||
|
if (error === "username") return "This username already exists. Choose another username.";
|
||||||
|
return "Use a username with letters, numbers, dashes or underscores and a password with at least 10 characters.";
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
|
import { unstable_noStore as noStore } from "next/cache";
|
||||||
import { prisma } from "./prisma";
|
import { prisma } from "./prisma";
|
||||||
|
|
||||||
export async function hasAdminUser() {
|
export async function hasAdminUser() {
|
||||||
|
noStore();
|
||||||
try {
|
try {
|
||||||
const admin = await prisma.userRole.findFirst({
|
const admin = await prisma.userRole.findFirst({
|
||||||
where: { role: { name: "admin" } },
|
where: { role: { name: "admin" } },
|
||||||
|
|||||||
Reference in New Issue
Block a user