Refactor login page to support user registration. Enhanced form handling by adding name and password confirmation fields, improved error messaging, and updated button labels for better user experience. Adjusted internal server options to reflect changes in form data structure.
CI / build (push) Has been skipped
CI / deploy (push) Successful in 1m52s

This commit is contained in:
eewing
2026-02-26 15:11:24 -06:00
parent 790357cc13
commit 60ac4a8f79
4 changed files with 204 additions and 41 deletions
+1 -1
View File
@@ -24,7 +24,7 @@ export const options = {
app: ({ head, body, assets, nonce, env }) => "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\" />\n <link rel=\"icon\" href=\"" + assets + "/favicon.png\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=5, viewport-fit=cover\" />\n <meta name=\"theme-color\" content=\"#0f172a\" />\n " + head + "\n </head>\n <body data-sveltekit-preload-data=\"hover\">\n <div style=\"display: contents\">" + body + "</div>\n </body>\n</html>\n",
error: ({ status, message }) => "<!doctype html>\n<html lang=\"en\">\n\t<head>\n\t\t<meta charset=\"utf-8\" />\n\t\t<title>" + message + "</title>\n\n\t\t<style>\n\t\t\tbody {\n\t\t\t\t--bg: white;\n\t\t\t\t--fg: #222;\n\t\t\t\t--divider: #ccc;\n\t\t\t\tbackground: var(--bg);\n\t\t\t\tcolor: var(--fg);\n\t\t\t\tfont-family:\n\t\t\t\t\tsystem-ui,\n\t\t\t\t\t-apple-system,\n\t\t\t\t\tBlinkMacSystemFont,\n\t\t\t\t\t'Segoe UI',\n\t\t\t\t\tRoboto,\n\t\t\t\t\tOxygen,\n\t\t\t\t\tUbuntu,\n\t\t\t\t\tCantarell,\n\t\t\t\t\t'Open Sans',\n\t\t\t\t\t'Helvetica Neue',\n\t\t\t\t\tsans-serif;\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t\tjustify-content: center;\n\t\t\t\theight: 100vh;\n\t\t\t\tmargin: 0;\n\t\t\t}\n\n\t\t\t.error {\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t\tmax-width: 32rem;\n\t\t\t\tmargin: 0 1rem;\n\t\t\t}\n\n\t\t\t.status {\n\t\t\t\tfont-weight: 200;\n\t\t\t\tfont-size: 3rem;\n\t\t\t\tline-height: 1;\n\t\t\t\tposition: relative;\n\t\t\t\ttop: -0.05rem;\n\t\t\t}\n\n\t\t\t.message {\n\t\t\t\tborder-left: 1px solid var(--divider);\n\t\t\t\tpadding: 0 0 0 1rem;\n\t\t\t\tmargin: 0 0 0 1rem;\n\t\t\t\tmin-height: 2.5rem;\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t}\n\n\t\t\t.message h1 {\n\t\t\t\tfont-weight: 400;\n\t\t\t\tfont-size: 1em;\n\t\t\t\tmargin: 0;\n\t\t\t}\n\n\t\t\t@media (prefers-color-scheme: dark) {\n\t\t\t\tbody {\n\t\t\t\t\t--bg: #222;\n\t\t\t\t\t--fg: #ddd;\n\t\t\t\t\t--divider: #666;\n\t\t\t\t}\n\t\t\t}\n\t\t</style>\n\t</head>\n\t<body>\n\t\t<div class=\"error\">\n\t\t\t<span class=\"status\">" + status + "</span>\n\t\t\t<div class=\"message\">\n\t\t\t\t<h1>" + message + "</h1>\n\t\t\t</div>\n\t\t</div>\n\t</body>\n</html>\n"
},
version_hash: "1pywl2x"
version_hash: "3lzvrw"
};
export async function get_hooks() {
@@ -8,37 +8,97 @@ export const load = async ({ url, locals }: Parameters<PageServerLoad>[0]) => {
}
return {
redirectTo: url.searchParams.get('redirectTo') ?? '',
error: null
error: null,
form: null as { error?: string; redirectTo?: string } | null
};
};
export const actions = {
default: async ({ request, locals, url }: import('./$types').RequestEvent) => {
const formData = await request.formData();
const email = String(formData.get('email') ?? '').trim();
const password = String(formData.get('password') ?? '');
const redirectTo = String(formData.get('redirectTo') ?? '').trim() || '/';
const loginAction = async ({
request,
locals
}: {
request: Request;
locals: App.Locals;
}) => {
const formData = await request.formData();
const intent = String(formData.get('intent') ?? 'login').trim();
const name = String(formData.get('name') ?? '').trim();
const email = String(formData.get('email') ?? '').trim();
const password = String(formData.get('password') ?? '');
const passwordConfirm = String(formData.get('passwordConfirm') ?? '');
const redirectTo = String(formData.get('redirectTo') ?? '').trim() || '/';
if (intent === 'register') {
if (!name) {
return {
redirectTo,
form: { error: 'Name is required.', redirectTo }
};
}
if (!email || !password) {
return {
redirectTo,
error: 'Email and password are required.'
form: { error: 'Email and password are required.', redirectTo }
};
}
try {
await locals.pb.collection('users').authWithPassword(email, password);
} catch (err) {
const message = err && typeof err === 'object' && 'message' in err
? String((err as { message: string }).message)
: 'Invalid email or password.';
if (password.length < 8) {
return {
redirectTo,
error: message
form: { error: 'Password must be at least 8 characters.', redirectTo }
};
}
if (password !== passwordConfirm) {
return {
redirectTo,
form: { error: 'Passwords do not match.', redirectTo }
};
}
try {
await locals.pb.collection('users').create({
name,
email,
password,
passwordConfirm: password,
Stackq: true
});
await locals.pb.collection('users').authWithPassword(email, password);
} catch (err) {
const message =
err && typeof err === 'object' && 'message' in err
? String((err as { message: string }).message)
: 'Could not create account. Email may already be in use.';
return {
redirectTo,
form: { error: message, redirectTo }
};
}
throw redirect(303, redirectTo);
}
if (!email || !password) {
return {
redirectTo,
form: { error: 'Email and password are required.', redirectTo }
};
}
try {
await locals.pb.collection('users').authWithPassword(email, password);
} catch (err) {
const message =
err && typeof err === 'object' && 'message' in err
? String((err as { message: string }).message)
: 'Invalid email or password.';
return {
redirectTo,
form: { error: message, redirectTo }
};
}
throw redirect(303, redirectTo);
};
export const actions = {
default: loginAction
};
;null as any as Actions;