feat(#9): Add sign up

This commit is contained in:
Josh Creek
2024-04-06 21:10:37 +01:00
parent b7996e50a6
commit 6cfa2bd6d7
10 changed files with 327 additions and 8 deletions
+3 -1
View File
@@ -1 +1,3 @@
MONGO_URL="" MONGO_URL=""
PUBLIC_SUPABASE_URL=""
PUBLIC_SUPABASE_ANON_KEY=""
+163
View File
@@ -8,6 +8,8 @@
"name": "LivingDexTracker", "name": "LivingDexTracker",
"version": "0.0.1", "version": "0.0.1",
"dependencies": { "dependencies": {
"@supabase/ssr": "^0.1.0",
"@supabase/supabase-js": "^2.42.0",
"mongoose": "^8.0.3", "mongoose": "^8.0.3",
"nanoid": "^5.0.4" "nanoid": "^5.0.4"
}, },
@@ -855,6 +857,112 @@
"win32" "win32"
] ]
}, },
"node_modules/@supabase/auth-js": {
"version": "2.63.0",
"resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.63.0.tgz",
"integrity": "sha512-yIgcHnlgv24GxHtVGUhwGqAFDyJkPIC/xjx7HostN08A8yCy8HIfl4JEkTKyBqD1v1L05jNEJOUke4Lf4O1+Qg==",
"dependencies": {
"@supabase/node-fetch": "^2.6.14"
}
},
"node_modules/@supabase/functions-js": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.2.2.tgz",
"integrity": "sha512-sJGq1nludmi7pY/fdtCpyY/pYonx7MfHdN408bqb736guWcVI1AChYVbI4kUM978EuOE4Ci6l7bUudfGg07QRw==",
"dependencies": {
"@supabase/node-fetch": "^2.6.14"
}
},
"node_modules/@supabase/node-fetch": {
"version": "2.6.15",
"resolved": "https://registry.npmjs.org/@supabase/node-fetch/-/node-fetch-2.6.15.tgz",
"integrity": "sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ==",
"dependencies": {
"whatwg-url": "^5.0.0"
},
"engines": {
"node": "4.x || >=6.0.0"
}
},
"node_modules/@supabase/node-fetch/node_modules/tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
},
"node_modules/@supabase/node-fetch/node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
},
"node_modules/@supabase/node-fetch/node_modules/whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
"dependencies": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
}
},
"node_modules/@supabase/postgrest-js": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-1.15.0.tgz",
"integrity": "sha512-U4bwBOrhsXWqDjZiYNbVqMBtRGgIIYE0kE5ZNSwsIbeBWfr/UxOMrnkIQUBGIZRhpYW/tw1WnTdRl1AGNyaxcw==",
"dependencies": {
"@supabase/node-fetch": "^2.6.14"
}
},
"node_modules/@supabase/realtime-js": {
"version": "2.9.3",
"resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.9.3.tgz",
"integrity": "sha512-lAp50s2n3FhGJFq+wTSXLNIDPw5Y0Wxrgt44eM5nLSA3jZNUUP3Oq2Ccd1CbZdVntPCWLZvJaU//pAd2NE+QnQ==",
"dependencies": {
"@supabase/node-fetch": "^2.6.14",
"@types/phoenix": "^1.5.4",
"@types/ws": "^8.5.10",
"ws": "^8.14.2"
}
},
"node_modules/@supabase/ssr": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/@supabase/ssr/-/ssr-0.1.0.tgz",
"integrity": "sha512-bIVrkqjAK5G3KjkIMKYKtAOlCgRRplEWjrlyRyXSOYtgDieiOhk2ZyNAPsEOa1By9OZVxuX5eAW1fitdnuxayw==",
"dependencies": {
"cookie": "^0.5.0",
"ramda": "^0.29.0"
},
"peerDependencies": {
"@supabase/supabase-js": "^2.33.1"
}
},
"node_modules/@supabase/ssr/node_modules/cookie": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
"integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/@supabase/storage-js": {
"version": "2.5.5",
"resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.5.5.tgz",
"integrity": "sha512-OpLoDRjFwClwc2cjTJZG8XviTiQH4Ik8sCiMK5v7et0MDu2QlXjCAW3ljxJB5+z/KazdMOTnySi+hysxWUPu3w==",
"dependencies": {
"@supabase/node-fetch": "^2.6.14"
}
},
"node_modules/@supabase/supabase-js": {
"version": "2.42.0",
"resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.42.0.tgz",
"integrity": "sha512-1PDqJiA4iG45w3AAu6xkccJ3wPqlGJUoz9CPhScRLLTStxhewYhz0mjryTpXz1kgtNHdUAsirALreezn8UZMjA==",
"dependencies": {
"@supabase/auth-js": "2.63.0",
"@supabase/functions-js": "2.2.2",
"@supabase/node-fetch": "2.6.15",
"@supabase/postgrest-js": "1.15.0",
"@supabase/realtime-js": "2.9.3",
"@supabase/storage-js": "2.5.5"
}
},
"node_modules/@sveltejs/adapter-auto": { "node_modules/@sveltejs/adapter-auto": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/@sveltejs/adapter-auto/-/adapter-auto-3.1.1.tgz", "resolved": "https://registry.npmjs.org/@sveltejs/adapter-auto/-/adapter-auto-3.1.1.tgz",
@@ -980,6 +1088,19 @@
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
"dev": true "dev": true
}, },
"node_modules/@types/node": {
"version": "20.12.5",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.5.tgz",
"integrity": "sha512-BD+BjQ9LS/D8ST9p5uqBxghlN+S42iuNxjsUGjeZobe/ciXzk2qb1B6IXc6AnRLS+yFJRpN2IPEHMzwspfDJNw==",
"dependencies": {
"undici-types": "~5.26.4"
}
},
"node_modules/@types/phoenix": {
"version": "1.6.4",
"resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.4.tgz",
"integrity": "sha512-B34A7uot1Cv0XtaHRYDATltAdKx0BvVKNgYNqE4WjtPUa4VQJM7kxeXcVKaH+KS+kCmZ+6w+QaUdcljiheiBJA=="
},
"node_modules/@types/pug": { "node_modules/@types/pug": {
"version": "2.0.10", "version": "2.0.10",
"resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.10.tgz", "resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.10.tgz",
@@ -1005,6 +1126,14 @@
"@types/webidl-conversions": "*" "@types/webidl-conversions": "*"
} }
}, },
"node_modules/@types/ws": {
"version": "8.5.10",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz",
"integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@typescript-eslint/eslint-plugin": { "node_modules/@typescript-eslint/eslint-plugin": {
"version": "7.1.1", "version": "7.1.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.1.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.1.1.tgz",
@@ -2971,6 +3100,15 @@
} }
] ]
}, },
"node_modules/ramda": {
"version": "0.29.1",
"resolved": "https://registry.npmjs.org/ramda/-/ramda-0.29.1.tgz",
"integrity": "sha512-OfxIeWzd4xdUNxlWhgFazxsA/nl3mS4/jGZI5n00uWOoSSFRhC1b6gl6xvmzUamgmqELraWp0J/qqVlXYPDPyA==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/ramda"
}
},
"node_modules/readdirp": { "node_modules/readdirp": {
"version": "3.6.0", "version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
@@ -3512,6 +3650,11 @@
"node": ">=14.17" "node": ">=14.17"
} }
}, },
"node_modules/undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
},
"node_modules/uri-js": { "node_modules/uri-js": {
"version": "4.4.1", "version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
@@ -4027,6 +4170,26 @@
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"dev": true "dev": true
}, },
"node_modules/ws": {
"version": "8.16.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz",
"integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/yallist": { "node_modules/yallist": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+2
View File
@@ -32,6 +32,8 @@
}, },
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"@supabase/ssr": "^0.1.0",
"@supabase/supabase-js": "^2.42.0",
"mongoose": "^8.0.3", "mongoose": "^8.0.3",
"nanoid": "^5.0.4" "nanoid": "^5.0.4"
}, },
+45 -5
View File
@@ -1,9 +1,49 @@
import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY } from '$env/static/public';
import { createServerClient } from '@supabase/ssr';
import type { Handle } from '@sveltejs/kit'; import type { Handle } from '@sveltejs/kit';
import { dbConnect } from '$lib/utils/db';
await dbConnect();
export const handle: Handle = async ({ event, resolve }) => { export const handle: Handle = async ({ event, resolve }) => {
const response = await resolve(event); event.locals.supabase = createServerClient(PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY, {
return response; cookies: {
get: (key) => event.cookies.get(key),
/**
* Note: You have to add the `path` variable to the
* set and remove method due to sveltekit's cookie API
* requiring this to be set, setting the path to an empty string
* will replicate previous/standard behaviour (https://kit.svelte.dev/docs/types#public-types-cookies)
*/
set: (key, value, options) => {
event.cookies.set(key, value, { ...options, path: '/' });
},
remove: (key, options) => {
event.cookies.delete(key, { ...options, path: '/' });
}
}
});
/**
* Unlike `supabase.auth.getSession`, which is unsafe on the server because it
* doesn't validate the JWT, this function validates the JWT by first calling
* `getUser` and aborts early if the JWT signature is invalid.
*/
event.locals.safeGetSession = async () => {
const {
data: { user },
error
} = await event.locals.supabase.auth.getUser();
if (error) {
return { session: null, user: null };
}
const {
data: { session }
} = await event.locals.supabase.auth.getSession();
return { session, user };
};
return resolve(event, {
filterSerializedResponseHeaders(name) {
return name === 'content-range';
}
});
}; };
+33
View File
@@ -0,0 +1,33 @@
<script lang="ts">
let email = '';
let password = '';
// Access the supabase client from the layout data
export let supabase: any;
async function signUpNewUser() {
try {
const { data, error } = await supabase.auth.signUp({
email: email,
password: password,
options: {
// Redirect URL after successful sign-up
redirectTo: '/welcome'
}
});
if (error) {
throw error;
}
// Handle success (optional)
} catch (error) {
console.error('Sign up error:', error.message);
// Handle error
}
}
</script>
<input type="email" bind:value={email} placeholder="Email" />
<input type="password" bind:value={password} placeholder="Password" />
<button on:click={signUpNewUser}>Sign Up</button>
+10
View File
@@ -0,0 +1,10 @@
import type { LayoutServerLoad } from './$types';
export const load: LayoutServerLoad = async ({ locals: { safeGetSession } }) => {
const { session, user } = await safeGetSession();
return {
session,
user
};
};
+6
View File
@@ -0,0 +1,6 @@
<script lang="ts">
</script>
<main>
<slot />
</main>
+34
View File
@@ -0,0 +1,34 @@
import { PUBLIC_SUPABASE_ANON_KEY, PUBLIC_SUPABASE_URL } from '$env/static/public';
import type { LayoutLoad } from './$types';
import { createBrowserClient, isBrowser, parse } from '@supabase/ssr';
export const load: LayoutLoad = async ({ fetch, data, depends }) => {
depends('supabase:auth');
const supabase = createBrowserClient(PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY, {
global: {
fetch
},
cookies: {
get(key) {
if (!isBrowser()) {
return JSON.stringify(data.session);
}
const cookie = parse(document.cookie);
return cookie[key];
}
}
});
/**
* It's fine to use `getSession` here, because on the client, `getSession` is
* safe, and on the server, it reads `session` from the `LayoutData`, which
* safely checked the session using `safeGetSession`.
*/
const {
data: { session }
} = await supabase.auth.getSession();
return { supabase, session };
};
+9 -2
View File
@@ -1,2 +1,9 @@
<h1>Welcome to SvelteKit</h1> <script lang="ts">
<p>Visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to read the documentation</p> import SignUp from '$lib/components/SignUp.svelte';
export let data;
let { supabase } = data;
$: ({ supabase } = data);
</script>
<SignUp {supabase} />
+22
View File
@@ -0,0 +1,22 @@
import { redirect } from '@sveltejs/kit';
import { type EmailOtpType } from '@supabase/supabase-js';
export const GET = async (event) => {
const {
url,
locals: { supabase }
} = event;
const token_hash = url.searchParams.get('token_hash') as string;
const type = url.searchParams.get('type') as EmailOtpType | null;
const next = url.searchParams.get('next') ?? '/';
if (token_hash && type) {
const { error } = await supabase.auth.verifyOtp({ token_hash, type });
if (!error) {
throw redirect(303, `/${next.slice(1)}`);
}
}
// return the user to an error page with some instructions
throw redirect(303, '/auth/auth-code-error');
};