diff --git a/.svelte-kit/generated/server/internal.js b/.svelte-kit/generated/server/internal.js
index 8e26641d..a6d60d75 100644
--- a/.svelte-kit/generated/server/internal.js
+++ b/.svelte-kit/generated/server/internal.js
@@ -24,7 +24,7 @@ export const options = {
app: ({ head, body, assets, nonce, env }) => "\n\n
\n \n \n \n \n " + head + "\n \n \n " + body + "
\n \n\n",
error: ({ status, message }) => "\n\n\t\n\t\t \n\t\t" + message + " \n\n\t\t\n\t\n\t\n\t\t\n\t\t\t
" + status + " \n\t\t\t
\n\t\t\t\t
" + message + " \n\t\t\t\n\t\t
\n\t\n\n"
},
- version_hash: "1pywl2x"
+ version_hash: "3lzvrw"
};
export async function get_hooks() {
diff --git a/.svelte-kit/types/src/routes/login/proxy+page.server.ts b/.svelte-kit/types/src/routes/login/proxy+page.server.ts
index eb1aff83..e95082d4 100644
--- a/.svelte-kit/types/src/routes/login/proxy+page.server.ts
+++ b/.svelte-kit/types/src/routes/login/proxy+page.server.ts
@@ -8,37 +8,97 @@ export const load = async ({ url, locals }: Parameters[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;
\ No newline at end of file
diff --git a/src/routes/login/+page.server.ts b/src/routes/login/+page.server.ts
index 6fa3f5ec..61cabab5 100644
--- a/src/routes/login/+page.server.ts
+++ b/src/routes/login/+page.server.ts
@@ -7,36 +7,96 @@ export const load: PageServerLoad = async ({ url, locals }) => {
}
return {
redirectTo: url.searchParams.get('redirectTo') ?? '',
- error: null
+ error: null,
+ form: null as { error?: string; redirectTo?: string } | null
};
};
-export const actions: Actions = {
- default: async ({ request, locals, url }) => {
- 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: Actions = {
+ default: loginAction
};
diff --git a/src/routes/login/+page.svelte b/src/routes/login/+page.svelte
index 3b5d7149..300bb690 100644
--- a/src/routes/login/+page.svelte
+++ b/src/routes/login/+page.svelte
@@ -10,6 +10,8 @@
formAction: string;
} = $props();
+ let isRegister = $state(false);
+
const redirectTo = $derived(data.form?.redirectTo ?? data.redirectTo ?? '');
const error = $derived(data.form?.error);
@@ -17,17 +19,35 @@
- Sign in
- Enter your email and password.
+ {isRegister ? 'Create account' : 'Sign in'}
+
+ {isRegister
+ ? 'Enter your email and a password (min 8 characters).'
+ : 'Enter your email and password.'}
+
-
+
+ (isRegister = !isRegister)}
+ >
+ {isRegister ? 'Already have an account? Sign in' : 'Create an account'}
+