-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
9dff45b
commit e7f14f4
Showing
11 changed files
with
209 additions
and
101 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import type { LayoutServerLoad } from "./$types"; | ||
|
||
|
||
export const load: LayoutServerLoad = async ({ locals }) => { | ||
return { session: locals.session } | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
<script lang="ts"> | ||
import { SessionProvider } from '@animelist/auth-sveltekit/client'; | ||
// import type { LayoutServerData } from './$types'; | ||
</script> | ||
|
||
|
||
<SessionProvider> | ||
<slot /> | ||
</SessionProvider> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
11 changes: 11 additions & 0 deletions
11
packages/animelist-auth-sveltekit/src/client/SessionProvider.svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<script lang="ts"> | ||
import { Session } from "@animelist/auth/client"; | ||
import { session as sessionStore, INITIALIZE_SESSION } from "./session"; | ||
export let session: Session | null | undefined = undefined; | ||
// Initialize the session store | ||
sessionStore[INITIALIZE_SESSION](session).catch(console.error); | ||
</script> | ||
|
||
<slot {session} /> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
export * from "./session"; | ||
export * from "./session"; | ||
export { default as SessionProvider } from "./SessionProvider.svelte"; |
172 changes: 87 additions & 85 deletions
172
packages/animelist-auth-sveltekit/src/client/session.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,107 +1,109 @@ | ||
// import { dev } from "$app/environment"; | ||
import { type User } from "@animelist/core"; | ||
import { getSession } from "@animelist/auth/client"; | ||
import { get, writable } from "svelte/store"; | ||
import { type Session, getSession } from "@animelist/auth/client"; | ||
import { get, writable, derived } from "svelte/store"; | ||
|
||
let initialized = false; | ||
/** | ||
* @internal | ||
*/ | ||
export const INITIALIZE_SESSION = Symbol("INITIALIZE_SESSION"); | ||
|
||
const DAY_MILLIS = 1000 * 60 * 60 * 24; | ||
|
||
export type SessionState = { | ||
user: User | null; | ||
accessToken: string | null; | ||
session: Session | null, | ||
loading: boolean; | ||
} | ||
|
||
const sessionStore = writable<SessionState>({ | ||
user: null, | ||
accessToken: null, | ||
loading: false | ||
}); | ||
|
||
type InitializeSession = Omit<SessionState, 'loading'>; | ||
|
||
function setUserSession(session: InitializeSession | null) { | ||
if (session) { | ||
initialized = true; | ||
sessionStore.set({ | ||
loading: false, | ||
accessToken: session.accessToken, | ||
user: session.user | ||
}) | ||
} else { | ||
initialized = false; | ||
sessionStore.set({ | ||
loading: false, | ||
accessToken: null, | ||
user: null | ||
}); | ||
} | ||
} | ||
function createSession() { | ||
const baseSessionStore = writable<SessionState>({ | ||
session: null, | ||
loading: false | ||
}); | ||
|
||
async function fetchUserSession() { | ||
if (typeof window === 'undefined') { | ||
return; | ||
} | ||
async function fetchUserSession() { | ||
if (typeof window === 'undefined') { | ||
return null; | ||
} | ||
|
||
initialized = true; | ||
try { | ||
// fetch the current user session | ||
const session = await getSession(); | ||
|
||
try { | ||
// Set state to loading | ||
const currentSession = get(sessionStore); | ||
sessionStore.set({ | ||
loading: true, | ||
accessToken: currentSession.accessToken, | ||
user: currentSession.user | ||
}); | ||
if (session == null) { | ||
baseSessionStore.set({ loading: false, session }); | ||
return null; | ||
} | ||
|
||
// fetch the current user session | ||
const session = await getSession(); | ||
baseSessionStore.set({ session, loading: false }); | ||
|
||
if (session == null) { | ||
return sessionStore.set({ loading: false, accessToken: null, user: null }); | ||
} | ||
// We use 1 day as a threshold because we don't expect an user to stay 24 hours | ||
// without any interaction. in most cases this is not reached because the default session is 7 days | ||
const expiresAt = new Date(session.expiresAt); | ||
|
||
// if (dev) { | ||
// console.log("🍥 User session loaded: ", JSON.stringify(session, null, 2)); | ||
// } | ||
if (expiresAt.getTime() < DAY_MILLIS) { | ||
window.setTimeout( | ||
fetchUserSession, | ||
expiresAt.getTime() | ||
); | ||
} | ||
|
||
// Currently the expiration of the access token is 31 days, which is really long, | ||
// so we don't have reason to refresh it, each time the user log in a new token will be created. | ||
const { accessToken, user } = session; | ||
sessionStore.set({ user, accessToken, loading: false }) | ||
} | ||
catch (err) { | ||
console.error(err); | ||
initialized = false; | ||
sessionStore.set({ user: null, accessToken: null, loading: false }) | ||
} | ||
} | ||
return session; | ||
} | ||
catch (err) { | ||
console.error(err); | ||
baseSessionStore.set({ session: null, loading: false }); | ||
} | ||
|
||
async function initialize(session?: InitializeSession | null) { | ||
if (session && initialized) { | ||
return; | ||
return null; | ||
} | ||
|
||
if (session !== undefined) { | ||
setUserSession(session); | ||
} | ||
else { | ||
await fetchUserSession(); | ||
async function initialize(session?: Session | null) { | ||
if (session === undefined) { | ||
// Set state to loading | ||
baseSessionStore.update(s => ({ ...s, loading: true })); | ||
|
||
// Fetch the current session | ||
await fetchUserSession(); | ||
} else { | ||
baseSessionStore.set({ | ||
session, | ||
loading: false | ||
}) | ||
} | ||
} | ||
} | ||
|
||
function destroy() { | ||
sessionStore.set({ | ||
accessToken: null, | ||
loading: false, | ||
user: null, | ||
const sessionStore = derived(baseSessionStore, ($store) => { | ||
return { | ||
/** | ||
* Returns the current user. | ||
*/ | ||
get user() { | ||
return $store?.session?.user || null; | ||
}, | ||
|
||
/** | ||
* Returns the current user access token. | ||
*/ | ||
get accessToken() { | ||
return $store?.session?.accessToken || null | ||
}, | ||
|
||
...$store | ||
} | ||
}) | ||
} | ||
|
||
export const session = { | ||
initialize, | ||
destroy, | ||
subscribe: sessionStore.subscribe, | ||
get current() { | ||
return get(sessionStore); | ||
return { | ||
subscribe: sessionStore.subscribe, | ||
|
||
/** | ||
* Returns `true` if the user is authenticated. | ||
*/ | ||
get isAuthenticated() { | ||
return get(baseSessionStore).session != null | ||
}, | ||
|
||
// @internal | ||
[INITIALIZE_SESSION]: initialize | ||
} | ||
} | ||
|
||
export const session = createSession(); |
Oops, something went wrong.