⬆️ Upgraded SvelteKit

This commit is contained in:
Mawoka
2022-08-18 15:35:01 +02:00
parent 12ec2ebf94
commit 9b79358db3
31 changed files with 198 additions and 324 deletions
@@ -0,0 +1,302 @@
<!--
- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at https://mozilla.org/MPL/2.0/.
-->
<script lang="ts">
import { getLocalization } from '$lib/i18n';
import { DateTime } from 'luxon';
import { UAParser } from 'ua-parser-js';
import Spinner from '$lib/Spinner.svelte';
const { t } = getLocalization();
let showMap = false;
interface UserAccount {
id: string;
email: string;
username: string;
verified: boolean;
created_at: string;
}
interface ChangePasswordData {
oldPassword: string;
newPassword: string;
newPasswordConfirm: string;
}
let changePasswordData: ChangePasswordData = {
oldPassword: '',
newPassword: '',
newPasswordConfirm: ''
};
let locationData;
let this_session;
let passwordChangeDataValid = false;
const checkPasswords = (data: ChangePasswordData): void => {
passwordChangeDataValid =
data.newPassword === data.newPasswordConfirm &&
data.newPassword.length >= 8 &&
data.oldPassword !== data.newPassword &&
data.oldPassword !== '';
};
$: checkPasswords(changePasswordData);
const changePassword = async () => {
if (!passwordChangeDataValid) {
return;
}
const res = await fetch('/api/v1/users/password/update', {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
old_password: changePasswordData.oldPassword,
new_password: changePasswordData.newPassword
})
});
if (res.status === 200) {
alert('Password changed');
window.location.assign('/account/login');
} else {
alert('Password change failed');
}
};
const getUser = async (): Promise<UserAccount> => {
const response = await fetch('/api/v1/users/me', {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
});
if (response.status === 200) {
return await response.json();
} else {
window.location.assign('/account/login');
}
};
const formatDate = (date: string): string => {
const dt = DateTime.fromISO(date);
return dt.toLocaleString(DateTime.DATETIME_MED);
};
const getSessions = async () => {
const res = await fetch('/api/v1/users/sessions/list');
if (res.status === 200) {
const res2 = await fetch('/api/v1/users/session');
if (res2.status === 200) {
this_session = await res2.json();
}
return await res.json();
} else {
window.location.assign('/account/login?returnTo=/account/settings');
}
return await res.json();
};
const getFormattedUserAgent = (userAgent: string): string => {
const parser = new UAParser(userAgent);
const result = parser.getResult();
return `${result.browser.name} ${result.browser.version} (${result.os.name})`;
};
const checkLocation = async (session_ip: string) => {
const res = await fetch(`/api/v1/utils/ip-lookup/${session_ip}`);
const json = await res.json();
console.log(json.status, json.status === 'fail');
if (json.status === 'fail') {
alert('This feature is kinda broken...');
return;
} else {
locationData = await res.json();
showMap = true;
}
};
const deleteSession = async (session_id: string) => {
const res = await fetch(`/api/v1/users/sessions/${session_id}`, {
method: 'DELETE'
});
if (res.status === 200) {
window.location.reload();
}
};
</script>
<svelte:head>
<title>ClassQuiz - Settings</title>
</svelte:head>
{#await getUser()}
<Spinner />
{:then user}
<div class="w-full">
<div class="sm:flex space-x-7 md:items-start items-center">
<div class="mb-4">
<img
class="rounded-md md:w-80"
src="/api/v1/users/avatar"
alt="Profile image of {user.username}"
/>
</div>
<div>
<h1 class="text-slate-100 text-4xl font-bold my-2">{user.username}</h1>
<p class="text-slate-100 text-lg mb-6 md:max-w-lg">
{$t('words.email')}: {user.email}
</p>
<form class="flex flex-col md:flex-row" on:submit|preventDefault={changePassword}>
<label
>{$t('settings_page.old_password')}:<input
type="password"
class="m-2 text-black"
bind:value={changePasswordData.oldPassword}
/></label
>
<label
>{$t('settings_page.new_password')}:<input
type="password"
class="m-2 text-black"
bind:value={changePasswordData.newPassword}
/></label
>
<label
>{$t('settings_page.repeat_password')}:<input
type="password"
class="m-2 text-black"
bind:value={changePasswordData.newPasswordConfirm}
/></label
>
<button
class="border-2 px-2 py-1 rounded-md border-black text-slate-100 hover:bg-amber-700 hover:text-indigo-100 transition duration-75 disabled:cursor-not-allowed disabled:opacity-50"
disabled={!passwordChangeDataValid}
type="submit"
>
{$t('settings_page.change_password_submit')}
</button>
</form>
</div>
</div>
</div>
{/await}
{#await getSessions()}
<Spinner />
{:then sessions}
<table class="min-w-full">
<thead class="bg-gray-50 dark:bg-gray-700">
<tr>
<th
scope="col"
class="py-3 px-6 text-xs font-medium tracking-wider text-left text-gray-700 uppercase dark:text-gray-400"
>
{$t('overview_page.created_at')}
</th>
<th
scope="col"
class="py-3 px-6 text-xs font-medium tracking-wider text-left text-gray-700 uppercase dark:text-gray-400"
>
{$t('settings_page.last_seen')}
</th>
<th
scope="col"
class="py-3 px-6 text-xs font-medium tracking-wider text-left text-gray-700 uppercase dark:text-gray-400"
>
{$t('words.browser')}
</th>
<th
scope="col"
class="py-3 px-6 text-xs font-medium tracking-wider text-left text-gray-700 uppercase dark:text-gray-400"
>
{$t('settings_page.check_location')}
</th>
<th
scope="col"
class="py-3 px-6 text-xs font-medium tracking-wider text-left text-gray-700 uppercase dark:text-gray-400"
>
{$t('settings_page.delete_this_session')}
</th>
<th
scope="col"
class="py-3 px-6 text-xs font-medium tracking-wider text-left text-gray-700 uppercase dark:text-gray-400"
>
{$t('settings_page.this_session?')}
</th>
</tr>
</thead>
<tbody>
{#each sessions as session}
<tr class="bg-white border-b dark:bg-gray-800 dark:border-gray-700">
<td
class="py-4 px-6 text-sm text-gray-500 whitespace-nowrap dark:text-gray-400"
>
{formatDate(session.created_at)}
</td>
<td
class="py-4 px-6 text-sm text-gray-500 whitespace-nowrap dark:text-gray-400"
>
{formatDate(session.last_seen)}
</td>
<td
class="py-4 px-6 text-sm text-gray-500 whitespace-nowrap dark:text-gray-400"
>
{getFormattedUserAgent(session.user_agent)}
</td>
<td
class="py-4 px-6 text-sm text-gray-500 whitespace-nowrap dark:text-gray-400"
>
<button
on:click={() => {
checkLocation(session.ip_address);
}}>{$t('words.view')}</button
>
</td>
<td
class="py-4 px-6 text-sm text-gray-500 whitespace-nowrap dark:text-gray-400"
>
<button
on:click={() => {
deleteSession(session.id);
}}>{$t('words.delete')}</button
>
</td>
<td
class="py-4 px-6 text-sm text-gray-500 whitespace-nowrap dark:text-gray-400"
>
{#if session.id === this_session.id}
{:else}
{/if}
</td>
</tr>
{/each}
</tbody>
</table>
{/await}
<div class="w-5/6 h-5/6 z-20 absolute top-10 pt-16 left-28" class:hidden={!showMap}>
{#if showMap}
{#await import('$lib/Map.svelte')}
<Spinner />
{:then c}
<button
on:click={() => {
showMap = false;
}}
class="bg-black text-white rounded-t-lg px-1">{$t('words.close')}</button
>
<div class="w-full h-full">
<svelte:component
this={c.default}
lat={locationData.lat}
lng={locationData.lon}
markerText={'Somewhere here was this session registered.'}
/>
</div>
{/await}
{/if}
</div>