diff --git a/README.md b/README.md index a164a29..ee726b9 100644 --- a/README.md +++ b/README.md @@ -1,65 +1,5 @@ -# create-svelte - -Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/main/packages/create-svelte). - -## Creating a project - -If you're seeing this, you've probably already done this step. Congrats! - -```bash -# create a new project in the current directory -npm create svelte@latest - -# create a new project in my-app -npm create svelte@latest my-app -``` - -## Developing - -Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: - -```bash -npm run dev - -# or start the server and open the app in a new browser tab -npm run dev -- --open -``` - -## Building - -To create a production version of your app: - -```bash -npm run build -``` - -You can preview the production build with `npm run preview`. - -> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment. - - - - - - - - - -SERVER (WEB SERVER ) - - - client -> GET -> server (html/css/js) - - dist (DATA) 300kb - - CACHE -> html/css/js - - COOKIES -> EVENTS ( login/password, last session) - - LocalStorage --> ( key-object database ) (MORE DATA > 60 days) - - - +# Svelte template With supabase +! required https://supabase.com/docs/guides/auth/server-side/sveltekit diff --git a/package-lock.json b/package-lock.json index 15a0552..e4834ab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,8 @@ "version": "0.0.1", "dependencies": { "@apollo/client": "^3.11.8", + "@supabase/ssr": "^0.5.1", + "@supabase/supabase-js": "^2.45.4", "@types/uuid": "^10.0.0", "axios": "^1.7.7", "graphql": "^16.9.0", @@ -2966,6 +2968,84 @@ "win32" ] }, + "node_modules/@supabase/auth-js": { + "version": "2.65.0", + "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.65.0.tgz", + "integrity": "sha512-+wboHfZufAE2Y612OsKeVP4rVOeGZzzMLD/Ac3HrTQkkY4qXNjI6Af9gtmxwccE5nFvTiF114FEbIQ1hRq5uUw==", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/functions-js": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.4.1.tgz", + "integrity": "sha512-8sZ2ibwHlf+WkHDUZJUXqqmPvWQ3UHN0W30behOJngVh/qHHekhJLCFbh0AjkE9/FqqXtf9eoVvmYgfCLk5tNA==", + "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/postgrest-js": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-1.16.1.tgz", + "integrity": "sha512-EOSEZFm5pPuCPGCmLF1VOCS78DfkSz600PBuvBND/IZmMciJ1pmsS3ss6TkB6UkuvTybYiBh7gKOYyxoEO3USA==", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/realtime-js": { + "version": "2.10.2", + "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.10.2.tgz", + "integrity": "sha512-qyCQaNg90HmJstsvr2aJNxK2zgoKh9ZZA8oqb7UT2LCh3mj9zpa3Iwu167AuyNxsxrUE8eEJ2yH6wLCij4EApA==", + "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.5.1", + "resolved": "https://registry.npmjs.org/@supabase/ssr/-/ssr-0.5.1.tgz", + "integrity": "sha512-+G94H/GZG0nErZ3FQV9yJmsC5Rj7dmcfCAwOt37hxeR1La+QTl8cE9whzYwPUrTJjMLGNXoO+1BMvVxwBAbz4g==", + "dependencies": { + "cookie": "^0.6.0" + }, + "peerDependencies": { + "@supabase/supabase-js": "^2.43.4" + } + }, + "node_modules/@supabase/storage-js": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.7.0.tgz", + "integrity": "sha512-iZenEdO6Mx9iTR6T7wC7sk6KKsoDPLq8rdu5VRy7+JiT1i8fnqfcOr6mfF2Eaqky9VQzhP8zZKQYjzozB65Rig==", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/supabase-js": { + "version": "2.45.4", + "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.45.4.tgz", + "integrity": "sha512-E5p8/zOLaQ3a462MZnmnz03CrduA5ySH9hZyL03Y+QZLIOO4/Gs8Rdy4ZCKDHsN7x0xdanVEWWFN3pJFQr9/hg==", + "dependencies": { + "@supabase/auth-js": "2.65.0", + "@supabase/functions-js": "2.4.1", + "@supabase/node-fetch": "2.6.15", + "@supabase/postgrest-js": "1.16.1", + "@supabase/realtime-js": "2.10.2", + "@supabase/storage-js": "2.7.0" + } + }, "node_modules/@sveltejs/adapter-auto": { "version": "3.2.5", "resolved": "https://registry.npmjs.org/@sveltejs/adapter-auto/-/adapter-auto-3.2.5.tgz", @@ -3085,11 +3165,15 @@ "version": "22.7.5", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz", "integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==", - "dev": true, "dependencies": { "undici-types": "~6.19.2" } }, + "node_modules/@types/phoenix": { + "version": "1.6.5", + "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.5.tgz", + "integrity": "sha512-xegpDuR+z0UqG9fwHqNoy3rI7JDlvaPh2TY47Fl80oq6g+hXT+c/LEuE43X48clZ6lOfANl5WrPur9fYO1RJ/w==" + }, "node_modules/@types/uuid": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", @@ -3099,7 +3183,6 @@ "version": "8.5.12", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz", "integrity": "sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==", - "dev": true, "dependencies": { "@types/node": "*" } @@ -4201,7 +4284,6 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", - "dev": true, "engines": { "node": ">= 0.6" } @@ -8281,8 +8363,7 @@ "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "dev": true + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, "node_modules/ts-interface-checker": { "version": "0.1.13", @@ -8391,8 +8472,7 @@ "node_modules/undici-types": { "version": "6.19.8", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", - "dev": true + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" }, "node_modules/unixify": { "version": "1.0.0", @@ -8680,14 +8760,12 @@ "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==", - "dev": true + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, "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==", - "dev": true, "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" @@ -8837,7 +8915,6 @@ "version": "8.18.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", - "dev": true, "engines": { "node": ">=10.0.0" }, diff --git a/package.json b/package.json index b03400d..400e93f 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,8 @@ "type": "module", "dependencies": { "@apollo/client": "^3.11.8", + "@supabase/ssr": "^0.5.1", + "@supabase/supabase-js": "^2.45.4", "@types/uuid": "^10.0.0", "axios": "^1.7.7", "graphql": "^16.9.0", diff --git a/src/app.d.ts b/src/app.d.ts index 743f07b..1a4d5fa 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -1,13 +1,20 @@ -// See https://kit.svelte.dev/docs/types#app -// for information about these interfaces +import type { Session, SupabaseClient, User } from '@supabase/supabase-js' + declare global { - namespace App { - // interface Error {} - // interface Locals {} - // interface PageData {} - // interface PageState {} - // interface Platform {} - } + namespace App { + // interface Error {} + interface Locals { + supabase: SupabaseClient + safeGetSession: () => Promise<{ session: Session | null; user: User | null }> + session: Session | null + user: User | null + } + interface PageData { + session: Session | null + } + // interface PageState {} + // interface Platform {} + } } -export {}; +export {} \ No newline at end of file diff --git a/src/hooks.server.ts b/src/hooks.server.ts new file mode 100644 index 0000000..6f5bb83 --- /dev/null +++ b/src/hooks.server.ts @@ -0,0 +1,81 @@ +import { createServerClient } from '@supabase/ssr' +import { type Handle, redirect } from '@sveltejs/kit' +import { sequence } from '@sveltejs/kit/hooks' + +import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY } from '$env/static/public' + +const supabase: Handle = async ({ event, resolve }) => { + /** + * Creates a Supabase client specific to this server request. + * + * The Supabase client gets the Auth token from the request cookies. + */ + event.locals.supabase = createServerClient(PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY, { + cookies: { + getAll: () => event.cookies.getAll(), + /** + * SvelteKit's cookies API requires `path` to be explicitly set in + * the cookie options. Setting `path` to `/` replicates previous/ + * standard behavior. + */ + setAll: (cookiesToSet) => { + cookiesToSet.forEach(({ name, value, options }) => { + event.cookies.set(name, value, { ...options, path: '/' }) + }) + }, + }, + }) + + /** + * Unlike `supabase.auth.getSession()`, which returns the session _without_ + * validating the JWT, this function also calls `getUser()` to validate the + * JWT before returning the session. + */ + event.locals.safeGetSession = async () => { + const { + data: { session }, + } = await event.locals.supabase.auth.getSession() + if (!session) { + return { session: null, user: null } + } + + const { + data: { user }, + error, + } = await event.locals.supabase.auth.getUser() + if (error) { + // JWT validation has failed + return { session: null, user: null } + } + + return { session, user } + } + + return resolve(event, { + filterSerializedResponseHeaders(name) { + /** + * Supabase libraries use the `content-range` and `x-supabase-api-version` + * headers, so we need to tell SvelteKit to pass it through. + */ + return name === 'content-range' || name === 'x-supabase-api-version' + }, + }) +} + +const authGuard: Handle = async ({ event, resolve }) => { + const { session, user } = await event.locals.safeGetSession() + event.locals.session = session + event.locals.user = user + + if (!event.locals.session && event.url.pathname.startsWith('/private')) { + redirect(303, '/auth') + } + + if (event.locals.session && event.url.pathname === '/auth') { + redirect(303, '/private') + } + + return resolve(event) +} + +export const handle: Handle = sequence(supabase, authGuard) \ No newline at end of file diff --git a/src/lib/supabase.ts b/src/lib/supabase.ts new file mode 100644 index 0000000..1781975 --- /dev/null +++ b/src/lib/supabase.ts @@ -0,0 +1,5 @@ +import { createClient } from '@supabase/supabase-js' + + + +export const supabase = createClient('https://nrzfuoabxjwpeawergsm.supabase.co', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im5yemZ1b2FieGp3cGVhd2VyZ3NtIiwicm9sZSI6ImFub24iLCJpYXQiOjE3Mjg4OTA1MjcsImV4cCI6MjA0NDQ2NjUyN30.VCOhPigGyRe40B23yTpCtACRrbjFNbsL8RMe52JGZhQ') diff --git a/src/routes/+page.server.ts b/src/routes/+page.server.ts index 959911d..509932d 100644 --- a/src/routes/+page.server.ts +++ b/src/routes/+page.server.ts @@ -1,6 +1,10 @@ import type { PageServerLoad } from "./$types"; -export const load = (({ }) => { - +export const load = (async({ locals: { safeGetSession }, cookies }) => { + const { session } = await safeGetSession() + return { + session, + cookies: cookies.getAll(), + } }) satisfies PageServerLoad; \ No newline at end of file