Compare commits
No commits in common. "1e325913875642916d526bde52cea0728c0515bb" and "d7ff30dca0da6e1ea88d05060752e89e3fc9032e" have entirely different histories.
1e32591387
...
d7ff30dca0
@ -5,16 +5,15 @@ import { users } from './db';
|
|||||||
type SessionData = {
|
type SessionData = {
|
||||||
userId: string;
|
userId: string;
|
||||||
userAgent: string;
|
userAgent: string;
|
||||||
ip: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const sessions: Map<string, SessionData & { publicId: string }> = new Map();
|
const sessions: Map<string, SessionData> = new Map();
|
||||||
|
|
||||||
export function createSession(data: SessionData) {
|
export function createSession(data: SessionData) {
|
||||||
const sessionId = nanoid();
|
const token = nanoid();
|
||||||
sessions.set(sessionId, { ...data, publicId: nanoid() });
|
sessions.set(token, data);
|
||||||
setTimeout(() => sessions.delete(sessionId), parseInt(env.SESSION_LIFETIME) * 1000 || 86_400_000);
|
setTimeout(() => sessions.delete(token), parseInt(env.SESSION_LIFETIME) * 1000 || 86_400_000);
|
||||||
return sessionId;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getUserFromSession(sessionId?: string) {
|
export async function getUserFromSession(sessionId?: string) {
|
||||||
@ -26,26 +25,7 @@ export async function getUserFromSession(sessionId?: string) {
|
|||||||
return await users.getById(data.userId);
|
return await users.getById(data.userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deleteSession(criteria: { sessionId?: string; publicId?: string }) {
|
export function deleteSession(sessionId?: string) {
|
||||||
let deleted = false;
|
if (!sessionId) return;
|
||||||
|
sessions.delete(sessionId);
|
||||||
if (criteria.sessionId) {
|
|
||||||
deleted = sessions.delete(criteria.sessionId);
|
|
||||||
} else {
|
|
||||||
for (let [k, v] of sessions) {
|
|
||||||
if (v.publicId == criteria.publicId) {
|
|
||||||
// surprisingly, deleting while iterating is fine with ES6 iterables
|
|
||||||
deleted = sessions.delete(k);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return deleted;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getUserSessions(userId: string) {
|
|
||||||
return sessions
|
|
||||||
.values()
|
|
||||||
.filter((s) => s.userId == userId)
|
|
||||||
.toArray();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
import { Button } from 'bits-ui';
|
import { Button } from 'bits-ui';
|
||||||
import IconPlus from '~icons/tabler/plus';
|
import IconPlus from '~icons/tabler/plus';
|
||||||
|
|
||||||
let { createHref = null, msgAdd = '', children } = $props();
|
let { createHref = null, msgAdd, children } = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if createHref}
|
{#if createHref}
|
||||||
|
|||||||
@ -1,21 +0,0 @@
|
|||||||
import { FORBIDDEN, SUCCESS } from '$lib/server/commonResponses';
|
|
||||||
import { deleteSession, getUserSessions } from '$lib/server/sessions';
|
|
||||||
import { fail, type Actions, type ServerLoad } from '@sveltejs/kit';
|
|
||||||
|
|
||||||
export const load: ServerLoad = async ({ locals: { guard } }) => {
|
|
||||||
const user = guard.requiresAuth().orRedirects().getUser();
|
|
||||||
return {
|
|
||||||
sessions: getUserSessions(user.id),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const actions = {
|
|
||||||
delete: async ({ locals: { guard }, request }) => {
|
|
||||||
if (guard.requiresAuth().isFailed()) return FORBIDDEN;
|
|
||||||
|
|
||||||
const id = (await request.formData()).get('id');
|
|
||||||
|
|
||||||
const deleted = deleteSession({ publicId: id?.toString() });
|
|
||||||
return deleted ? SUCCESS : fail(404, { error: 'Could not find session.' });
|
|
||||||
},
|
|
||||||
} satisfies Actions;
|
|
||||||
@ -1,47 +0,0 @@
|
|||||||
<script>
|
|
||||||
import { browser } from '$app/environment';
|
|
||||||
import { pageTitle } from '$lib/v2/globalStores.js';
|
|
||||||
import ListPage from '$lib/v2/snippets/ListPage.svelte';
|
|
||||||
import { Button, Separator } from 'bits-ui';
|
|
||||||
import IconPlugConnectedX from '~icons/tabler/plug-connected-x';
|
|
||||||
|
|
||||||
let { data, form } = $props();
|
|
||||||
|
|
||||||
$pageTitle = 'My active sessions';
|
|
||||||
|
|
||||||
$effect(() => {
|
|
||||||
if (form?.success && browser) {
|
|
||||||
window.location.reload();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<ListPage>
|
|
||||||
<div class="flex flex-col gap-10 sm:mx-auto sm:w-4/5">
|
|
||||||
{#each data.sessions as session}
|
|
||||||
<div
|
|
||||||
class="flex flex-col gap-5 w-full rounded-xl sm:rounded-2xl border border-neutral-600
|
|
||||||
px-6 py-5 sm:px-10 sm:py-8 bg-neutral-950 shadow-xl text-neutral-500 text-sm text-justify"
|
|
||||||
>
|
|
||||||
<p>User-Agent: <span class="text-neutral-200 font-semibold">{session.userAgent}</span></p>
|
|
||||||
<p>IP Address: <span class="text-neutral-200 font-semibold">{session.ip}</span></p>
|
|
||||||
<Separator.Root class="bg-neutral-600 h-px w-full"></Separator.Root>
|
|
||||||
<div class="flex items-center justify-end gap-5 text-xs sm:text-sm">
|
|
||||||
<p>Don't recognize this?</p>
|
|
||||||
<form method="POST" action="?/delete">
|
|
||||||
<input type="hidden" name="id" value={session.publicId} />
|
|
||||||
<Button.Root
|
|
||||||
class="flex items-center justify-center gap-2 bg-neutral-100 text-neutral-950 rounded-full px-4 p-2
|
|
||||||
border border-white cursor-pointer transition-all ease-in-out duration-300
|
|
||||||
hover:scale-95 hover:bg-neutral-400"
|
|
||||||
>
|
|
||||||
End session
|
|
||||||
<IconPlugConnectedX />
|
|
||||||
</Button.Root>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
<div class="h-10"></div>
|
|
||||||
</div>
|
|
||||||
</ListPage>
|
|
||||||
@ -91,7 +91,6 @@ export const actions = {
|
|||||||
createSession({
|
createSession({
|
||||||
userAgent: request.headers.get('user-agent') ?? 'UNKNOWN',
|
userAgent: request.headers.get('user-agent') ?? 'UNKNOWN',
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
ip,
|
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { deleteSession } from '$lib/server/sessions';
|
|||||||
import { redirect, type ServerLoad } from '@sveltejs/kit';
|
import { redirect, type ServerLoad } from '@sveltejs/kit';
|
||||||
|
|
||||||
export const load: ServerLoad = async ({ cookies }) => {
|
export const load: ServerLoad = async ({ cookies }) => {
|
||||||
deleteSession({ sessionId: cookies.get('session') });
|
deleteSession(cookies.get('session'));
|
||||||
cookies.delete('session', { path: '/' });
|
cookies.delete('session', { path: '/' });
|
||||||
redirect(302, '/login');
|
redirect(302, '/login');
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user