aboutsummaryrefslogtreecommitdiffstats
path: root/code/app/src/routes/(main)
diff options
context:
space:
mode:
Diffstat (limited to 'code/app/src/routes/(main)')
-rw-r--r--code/app/src/routes/(main)/(app)/+layout.svelte27
-rw-r--r--code/app/src/routes/(main)/(app)/projects/+page.svelte171
-rw-r--r--code/app/src/routes/(main)/(public)/portal/+page.svelte26
-rw-r--r--code/app/src/routes/(main)/(public)/portal/+page.ts10
-rw-r--r--code/app/src/routes/(main)/(public)/reset-password/+page.svelte11
-rw-r--r--code/app/src/routes/(main)/(public)/reset-password/[id]/+page.svelte41
-rw-r--r--code/app/src/routes/(main)/(public)/sign-in/+page.svelte106
-rw-r--r--code/app/src/routes/(main)/+layout.server.ts74
8 files changed, 239 insertions, 227 deletions
diff --git a/code/app/src/routes/(main)/(app)/+layout.svelte b/code/app/src/routes/(main)/(app)/+layout.svelte
index e57bc3b..936e0a7 100644
--- a/code/app/src/routes/(main)/(app)/+layout.svelte
+++ b/code/app/src/routes/(main)/(app)/+layout.svelte
@@ -22,12 +22,13 @@
TransitionRoot,
} from "@rgossiaux/svelte-headlessui";
import { DialogPanel } from "@developermuch/dev-svelte-headlessui";
- import { Input } from "$components";
+ import { Input, Notification } from "$components";
import { goto } from "$app/navigation";
import { page } from "$app/stores";
+ import { onMount } from "svelte";
+ import { fgs, sgs } from "$help/global-state";
- const accountService = new AccountService();
-
+ const accountService = AccountService.resolve();
const session = {
profile: {
username: "Brukernavn",
@@ -37,6 +38,12 @@
let sidebarOpen = false;
let sidebarSearchValue: string | undefined;
+ let showEmailValidatedNotif = false;
+
+ onMount(() => {
+ showEmailValidatedNotif = fgs("showEmailValidatedAlertWhenLoggedIn") === "true";
+ if (showEmailValidatedNotif) sgs("showEmailValidatedAlertWhenLoggedIn", false);
+ });
function sign_out() {
accountService.end_session(() => goto("/sign-in"));
@@ -71,6 +78,20 @@
];
</script>
+{#if showEmailValidatedNotif}
+ <Notification
+ title="Email successfully validated"
+ subtitle="Because of this, you now have gained access to more functionality"
+ show={true}
+ />
+
+ <Notification
+ title="Email successfully validated"
+ subtitle="Because of this, you now have gained access to more functionality"
+ show={true}
+ />
+{/if}
+
<div class="min-h-full">
<!-- Mobile sidebar -->
<TransitionRoot show={sidebarOpen}>
diff --git a/code/app/src/routes/(main)/(app)/projects/+page.svelte b/code/app/src/routes/(main)/(app)/projects/+page.svelte
index 1508118..2585331 100644
--- a/code/app/src/routes/(main)/(app)/projects/+page.svelte
+++ b/code/app/src/routes/(main)/(app)/projects/+page.svelte
@@ -1,41 +1,14 @@
<script lang="ts">
- import {Button, ProjectStatusBadge, Input} from "$components";
- import type {Project} from "$models/projects/Project";
- import {onMount} from "svelte";
- import {faker} from "@faker-js/faker";
- import {Temporal} from "temporal-polyfill";
- import {createTable, Subscribe, Render} from "svelte-headless-table";
- import {addSortBy, addTableFilter} from "svelte-headless-table/plugins";
- import {ProjectStatus} from "$models/projects/ProjectStatus";
- import {writable, type Writable} from "svelte/store";
- import {
- ChevronDownIcon,
- ChevronUpIcon,
- ChevronUpDownIcon,
- MagnifyingGlassIcon,
- FunnelIcon,
- } from "$components/icons";
+ import { Button, ProjectStatusBadge, Input } from "$components";
+ import type { Project } from "$models/projects/Project";
+ import { createTable, Subscribe, Render } from "svelte-headless-table";
+ import { addSortBy, addTableFilter } from "svelte-headless-table/plugins";
+ import { writable, type Writable } from "svelte/store";
+ import { ChevronDownIcon, ChevronUpIcon, ChevronUpDownIcon, MagnifyingGlassIcon, FunnelIcon } from "$components/icons";
import LL from "$i18n/i18n-svelte";
- import {goto} from "$app/navigation";
+ import { goto } from "$app/navigation";
- let projects: Writable<Array<Project>> = writable([]);
-
- onMount(() => {
- let i = 0;
- const tempProjects = [];
- while (i < 101) {
- tempProjects.push({
- id: crypto.randomUUID(),
- name: faker.lorem.word(),
- start: Temporal.Now.plainDateISO().toLocaleString(),
- description: faker.lorem.words(3),
- members: [],
- status: ProjectStatus.IDLE,
- });
- i++;
- }
- projects.set(tempProjects);
- });
+ const projects: Writable<Array<Project>> = writable([]);
function on_open_project(event) {
if (event.code && (event.code !== "Enter" || event.code !== "Space")) return;
@@ -50,14 +23,14 @@
});
const columns = table.createColumns([
- table.column({header: $LL.name(), accessor: "name"}),
- table.column({header: "Status", accessor: "status"}),
- table.column({header: "Start", accessor: "start"}),
- table.column({header: "Description", accessor: "description", plugins: {sort: {disable: true}}}),
+ table.column({ header: $LL.name(), accessor: "name" }),
+ table.column({ header: "Status", accessor: "status" }),
+ table.column({ header: "Start", accessor: "start" }),
+ table.column({ header: "Description", accessor: "description", plugins: { sort: { disable: true } } }),
]);
- const {headerRows, rows, tableAttrs, tableBodyAttrs, pluginStates} = table.createViewModel(columns);
- const {filterValue} = pluginStates.filter;
+ const { headerRows, rows, tableAttrs, tableBodyAttrs, pluginStates } = table.createViewModel(columns);
+ const { filterValue } = pluginStates.filter;
</script>
<div class="sm:flex sm:items-center">
@@ -66,78 +39,80 @@
<p class="mt-2 text-sm text-gray-700">A list of all the projects in your organsation.</p>
</div>
<div class="mt-4 sm:mt-0 sm:ml-16 inline-flex gap-1 sm:flex-none">
- <Input icon={MagnifyingGlassIcon} placeholder="Search" bind:value={$filterValue}/>
- <Button text="Create project" href="/projects/create"/>
+ <Input icon={MagnifyingGlassIcon} placeholder="Search" bind:value={$filterValue} />
+ <Button text="Create project" href="/projects/create" />
</div>
</div>
<div class="-mx-2 mt-6 rounded-md shadow overflow-auto max-h-[80vh] sm:-mx-6 md:mx-0">
<table {...$tableAttrs} class="min-w-full divide-y divide-gray-300">
<thead class="bg-gray-50">
- {#each $headerRows as headerRow (headerRow.id)}
- <Subscribe rowAttrs={headerRow.attrs()} let:rowAttrs>
- <tr {...rowAttrs} class="shadow-sm">
- {#each headerRow.cells as cell (cell.id)}
- <Subscribe attrs={cell.attrs()} let:attrs props={cell.props()} let:props>
- <th
+ {#each $headerRows as headerRow (headerRow.id)}
+ <Subscribe rowAttrs={headerRow.attrs()} let:rowAttrs>
+ <tr {...rowAttrs} class="shadow-sm">
+ {#each headerRow.cells as cell (cell.id)}
+ <Subscribe attrs={cell.attrs()} let:attrs props={cell.props()} let:props>
+ <th
{...attrs}
scope="col"
class="sticky top-0 bg-gray-50 bg-opacity-100 whitespace-nowrap px-2 py-3.5 text-left text-sm font-semibold text-gray-900"
- >
- <div class="group inline-flex">
- <Render of={cell.render()}/>
- <span
+ >
+ <div class="group inline-flex">
+ <Render of={cell.render()} />
+ <span
on:click={props.sort.toggle}
on:keypress={props.sort.toggle}
class="{props.sort.disabled
- ? 'bg-gray-200 text-gray-900 group-hover:bg-gray-300'
- : 'invisible text-gray-400 group-hover:visible group-focus:visible'}
+ ? 'bg-gray-200 text-gray-900 group-hover:bg-gray-300'
+ : 'invisible text-gray-400 group-hover:visible group-focus:visible'}
{props.sort.disabled ? '' : 'cursor-pointer'}
ml-2 flex-none rounded"
- >
- {#if props.sort.order === "asc"}
- <ChevronUpIcon/>
- {:else if props.sort.order === "desc"}
- <ChevronDownIcon/>
- {:else if !props.sort.disabled}
- <ChevronUpDownIcon/>
- {/if}
- </span>
- {#if cell.id === "status"}
- <span class="invisible text-gray-400 cursor-pointer group-hover:visible group-focus:visible ml-2 flex-none rounded">
- <FunnelIcon/>
- </span>
- {/if}
- </div>
- </th>
- </Subscribe>
- {/each}
- </tr>
- </Subscribe>
- {/each}
+ >
+ {#if props.sort.order === "asc"}
+ <ChevronUpIcon />
+ {:else if props.sort.order === "desc"}
+ <ChevronDownIcon />
+ {:else if !props.sort.disabled}
+ <ChevronUpDownIcon />
+ {/if}
+ </span>
+ {#if cell.id === "status"}
+ <span
+ class="invisible text-gray-400 cursor-pointer group-hover:visible group-focus:visible ml-2 flex-none rounded"
+ >
+ <FunnelIcon />
+ </span>
+ {/if}
+ </div>
+ </th>
+ </Subscribe>
+ {/each}
+ </tr>
+ </Subscribe>
+ {/each}
</thead>
<tbody {...$tableBodyAttrs} class="divide-y divide-gray-200 bg-white">
- {#each $rows as row (row.id)}
- <Subscribe rowAttrs={row.attrs()} let:rowAttrs>
- <tr {...rowAttrs}>
- {#each row.cells as cell (cell.id)}
- {@const materialisedCell = cell.render()}
- <Subscribe attrs={cell.attrs()} let:attrs>
- <td {...attrs} class="whitespace-nowrap px-2 py-2 text-sm">
- {#if cell.id === "name"}
- <span class="link" title="Open project" on:click={on_open_project} on:keypress={on_open_project}>
- <Render of={materialisedCell}/>
- </span>
- {:else if cell.id === "status"}
- <ProjectStatusBadge status={materialisedCell.toString()}/>
- {:else}
- <Render of={materialisedCell}/>
- {/if}
- </td>
- </Subscribe>
- {/each}
- </tr>
- </Subscribe>
- {/each}
+ {#each $rows as row (row.id)}
+ <Subscribe rowAttrs={row.attrs()} let:rowAttrs>
+ <tr {...rowAttrs}>
+ {#each row.cells as cell (cell.id)}
+ {@const materialisedCell = cell.render()}
+ <Subscribe attrs={cell.attrs()} let:attrs>
+ <td {...attrs} class="whitespace-nowrap px-2 py-2 text-sm">
+ {#if cell.id === "name"}
+ <span class="link" title="Open project" on:click={on_open_project} on:keypress={on_open_project}>
+ <Render of={materialisedCell} />
+ </span>
+ {:else if cell.id === "status"}
+ <ProjectStatusBadge status={materialisedCell.toString()} />
+ {:else}
+ <Render of={materialisedCell} />
+ {/if}
+ </td>
+ </Subscribe>
+ {/each}
+ </tr>
+ </Subscribe>
+ {/each}
</tbody>
</table>
</div>
diff --git a/code/app/src/routes/(main)/(public)/portal/+page.svelte b/code/app/src/routes/(main)/(public)/portal/+page.svelte
new file mode 100644
index 0000000..bd6aa15
--- /dev/null
+++ b/code/app/src/routes/(main)/(public)/portal/+page.svelte
@@ -0,0 +1,26 @@
+<script lang="ts">
+ import { onMount } from "svelte";
+ import type { PageData } from "./$types";
+ import type { PortalMessage } from "$configuration";
+ import { goto } from "$app/navigation";
+ import { sgs } from "$help/global-state";
+
+ export let data: PageData;
+
+ onMount(async () => {
+ switch (data.message as PortalMessage) {
+ case "emailValidated": {
+ sgs("showEmailValidatedAlertWhenLoggedIn", true);
+ await goto("/home");
+ break;
+ }
+ default: {
+ await goto("/home");
+ }
+ }
+ });
+</script>
+
+<div class="p-3">
+ <h1>Warping...</h1>
+</div>
diff --git a/code/app/src/routes/(main)/(public)/portal/+page.ts b/code/app/src/routes/(main)/(public)/portal/+page.ts
new file mode 100644
index 0000000..49bf3db
--- /dev/null
+++ b/code/app/src/routes/(main)/(public)/portal/+page.ts
@@ -0,0 +1,10 @@
+import type { PortalMessage } from '$configuration';
+import { redirect } from '@sveltejs/kit';
+import type { PageLoad } from './$types';
+
+export const load: PageLoad = async ({ url }) => {
+ const queryParams = new URLSearchParams(url.search);
+ const message = queryParams.get("msg") as PortalMessage;
+ if (!message) throw redirect(302, "/");
+ return { message };
+}; \ No newline at end of file
diff --git a/code/app/src/routes/(main)/(public)/reset-password/+page.svelte b/code/app/src/routes/(main)/(public)/reset-password/+page.svelte
index 55859f6..a45ccdd 100644
--- a/code/app/src/routes/(main)/(public)/reset-password/+page.svelte
+++ b/code/app/src/routes/(main)/(public)/reset-password/+page.svelte
@@ -12,7 +12,7 @@
};
const formError = new FormError();
- const resetRequests = new PasswordResetService();
+ const passwordResetService = PasswordResetService.resolve();
let loading = false;
let showSuccessAlert = false;
@@ -23,7 +23,7 @@
showSuccessAlert = false;
showErrorAlert = false;
loading = true;
- const response = await resetRequests.create_request_async(formData.email.value);
+ const response = await passwordResetService.create_request_async(formData.email.value);
loading = false;
if (response.isCreated) {
showSuccessAlert = true;
@@ -37,17 +37,12 @@
}
}
} else {
- formError.title = $LL.unexpectedError();
- formError.subtitle = $LL.tryAgainSoon();
+ formError.set($LL.unexpectedError(), $LL.tryAgainSoon());
}
showErrorAlert = formError.has_error() && !showSuccessAlert;
}
</script>
-<svlete:head>
- <title>Reset password - Greatoffice</title>
-</svlete:head>
-
<div class="min-h-full flex flex-col justify-center py-12 sm:px-6 lg:px-8">
<div class="sm:mx-auto sm:w-full p-2 sm:p-0 sm:max-w-md">
<h2 class="mt-6 text-3xl tracking-tight font-bold text-gray-900">
diff --git a/code/app/src/routes/(main)/(public)/reset-password/[id]/+page.svelte b/code/app/src/routes/(main)/(public)/reset-password/[id]/+page.svelte
index 8f817bf..27a1af5 100644
--- a/code/app/src/routes/(main)/(public)/reset-password/[id]/+page.svelte
+++ b/code/app/src/routes/(main)/(public)/reset-password/[id]/+page.svelte
@@ -1,14 +1,15 @@
<script lang="ts">
- import {onMount} from "svelte";
+ import { onMount } from "svelte";
import LL from "$i18n/i18n-svelte";
- import {Alert, Input, Button} from "$components";
- import type {PageServerData} from "./$types";
- import {goto} from "$app/navigation";
- import {SignInPageMessage, signInPageMessageQueryKey} from "$routes/(main)/(public)/sign-in";
- import {PasswordResetService} from "$services/password-reset-service";
+ import { Alert, Input, Button } from "$components";
+ import type { PageServerData } from "./$types";
+ import { goto } from "$app/navigation";
+ import { SignInPageMessage, signInPageMessageQueryKey } from "$routes/(main)/(public)/sign-in";
+ import { PasswordResetService } from "$services/password-reset-service";
export let data: PageServerData;
- const passwordResets = new PasswordResetService();
+ const passwordResetService = PasswordResetService.resolve();
+
const formData = {
newPassword: {
value: "",
@@ -24,7 +25,7 @@
async function submitFormAsync() {
if (!canSubmit) return;
loading = true;
- const request = await passwordResets.fulfill_request_async(data.resetRequestId, formData.newPassword.value);
+ const request = await passwordResetService.fulfill_request_async(data.resetRequestId, formData.newPassword.value);
if (request.isFulfilled) {
goto("/sign-in?" + signInPageMessageQueryKey + "=" + SignInPageMessage.AFTER_PASSWORD_RESET);
} else if (request.knownProblem) {
@@ -33,7 +34,7 @@
}
onMount(async () => {
- const response = await passwordResets.request_is_valid_async(data.resetRequestId);
+ const response = await passwordResetService.request_is_valid_async(data.resetRequestId);
requestIsInvalid = !response.isValid;
finishedPreliminaryLoading = true;
});
@@ -57,19 +58,21 @@
<div class="bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10">
<form class="space-y-6" on:submit|preventDefault={submitFormAsync}>
{#if requestIsInvalid}
- <Alert title={$LL.resetPasswordPage.invalidRequestTitle()}
- message={$LL.resetPasswordPage.invalidRequestMessage()}/>
+ <Alert
+ title={$LL.resetPasswordPage.invalidRequestTitle()}
+ message={$LL.resetPasswordPage.invalidRequestMessage()}
+ />
{/if}
<Input
- id="password"
- name="password"
- type="password"
- autocomplete="new-password"
- required
- bind:value={formData.newPassword.value}
- label={$LL.resetPasswordPage.newPassword()}
+ id="password"
+ name="password"
+ type="password"
+ autocomplete="new-password"
+ required
+ bind:value={formData.newPassword.value}
+ label={$LL.resetPasswordPage.newPassword()}
/>
- <Button text={$LL.submit()} type="submit" {loading} fullWidth/>
+ <Button text={$LL.submit()} type="submit" {loading} fullWidth />
</form>
</div>
</div>
diff --git a/code/app/src/routes/(main)/(public)/sign-in/+page.svelte b/code/app/src/routes/(main)/(public)/sign-in/+page.svelte
index c4ecb1a..66d4575 100644
--- a/code/app/src/routes/(main)/(public)/sign-in/+page.svelte
+++ b/code/app/src/routes/(main)/(public)/sign-in/+page.svelte
@@ -8,62 +8,62 @@
import { AccountService } from "$services/account-service";
import type { LoginPayload } from "$services/abstractions/IAccountService";
import { FormError } from "$models/internal/FormError";
+ import type { IForm } from "$models/internal/IForm";
- let loading = false;
- let showErrorAlert = false;
let messageType: SignInPageMessage | undefined = undefined;
- const accountService = new AccountService();
-
- const formData = {
- username: {
- value: "",
- errors: [],
- },
- password: {
- value: "",
- errors: [],
+ const accountService = AccountService.resolve();
+ const form = {
+ fields: {
+ username: {
+ value: "",
+ errors: [],
+ },
+ password: {
+ value: "",
+ errors: [],
+ },
+ persist: {
+ value: false,
+ errors: [],
+ },
},
- persist: {
- value: false,
- errors: [],
- },
- as_payload(): LoginPayload {
+ error: new FormError(),
+ isLoading: false,
+ showError: false,
+ get_payload(): LoginPayload {
return {
- password: formData.password.value,
- username: formData.username.value,
- persist: !formData.persist.value,
+ password: form.fields.password.value,
+ username: form.fields.username.value,
+ persist: !form.fields.persist.value,
};
},
- };
-
- const formError = new FormError();
+ async submit_async() {
+ console.log("sadf");
+ form.error.set();
+ form.showError = form.error.has_error();
+ form.isLoading = true;
+ const loginResponse = await accountService.login_async(form.get_payload());
+ if (loginResponse.isLoggedIn) {
+ await goto("/home");
+ } else if (loginResponse.knownProblem) {
+ form.error.set_from_known_problem(loginResponse.knownProblem);
+ } else {
+ form.error.set($LL.unexpectedError(), $LL.tryAgainSoon());
+ }
+ form.isLoading = false;
+ form.showError = form.error.has_error();
+ },
+ } as IForm;
onMount(() => {
- const searcher = new URLSearchParams(window.location.search);
- if (searcher.get(signInPageMessageQueryKey)) {
- messageType = searcher.get(signInPageMessageQueryKey) as SignInPageMessage;
- searcher.delete(signInPageMessageQueryKey);
- history.replaceState(null, "", window.location.origin + window.location.pathname);
+ const queryParams = new URLSearchParams(window.location.search);
+ if (queryParams.get(signInPageMessageQueryKey)) {
+ messageType = queryParams.get(signInPageMessageQueryKey) as SignInPageMessage;
+ queryParams.delete(signInPageMessageQueryKey);
+ window.history.replaceState(null, "", window.location.origin + window.location.pathname);
}
});
-
- async function submit_form_async() {
- formError.set();
- showErrorAlert = formError.has_error();
- loading = true;
- const loginResponse = await accountService.login_async(formData.as_payload());
- if (loginResponse.isLoggedIn) {
- await goto("/home");
- } else if (loginResponse.knownProblem) {
- formError.set_from_known_problem(loginResponse.knownProblem);
- } else {
- formError.title = $LL.unexpectedError();
- formError.subtitle = $LL.tryAgainSoon();
- }
- loading = false;
- showErrorAlert = formError.has_error();
- }
</script>
<div class="min-h-full flex flex-col justify-center py-12 sm:px-6 lg:px-8">
@@ -106,10 +106,10 @@
</div>
<div class="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
<div class="bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10">
- {#if showErrorAlert}
- <Alert title={formError.title} message={formError.subtitle} type="error" _pwKey={signInPageTestKeys.formErrorAlert} />
+ {#if form.showError}
+ <Alert title={form.error.title} message={form.error.subtitle} type="error" _pwKey={signInPageTestKeys.formErrorAlert} />
{/if}
- <form class="space-y-6 mt-2" use:pwKey={signInPageTestKeys.signInForm} on:submit|preventDefault={submit_form_async}>
+ <form class="space-y-6 mt-2" use:pwKey={signInPageTestKeys.signInForm} on:submit|preventDefault={() => form.submit_async()}>
<Input
id="username"
_pwKey={signInPageTestKeys.usernameInput}
@@ -117,7 +117,8 @@
type="email"
label={$LL.emailAddress()}
required
- bind:value={formData.username.value}
+ errors={form.fields.username.errors}
+ bind:value={form.fields.username.value}
/>
<Input
@@ -128,7 +129,8 @@
_pwKey={signInPageTestKeys.passwordInput}
autocomplete="current-password"
required
- bind:value={formData.password.value}
+ errors={form.fields.password.errors}
+ bind:value={form.fields.password.value}
/>
<div class="flex items-center justify-between">
@@ -136,7 +138,7 @@
id="remember-me"
_pwKey={signInPageTestKeys.rememberMeCheckbox}
name="remember-me"
- bind:checked={formData.persist.value}
+ bind:checked={form.fields.persist.value}
label={$LL.signInPage.notMyComputer()}
/>
<div class="text-sm">
@@ -146,7 +148,7 @@
</div>
</div>
- <Button text={$LL.submit()} fullWidth type="submit" {loading} />
+ <Button text={$LL.submit()} fullWidth type="submit" loading={form.isLoading} />
</form>
</div>
</div>
diff --git a/code/app/src/routes/(main)/+layout.server.ts b/code/app/src/routes/(main)/+layout.server.ts
index 4199d7f..b040b8f 100644
--- a/code/app/src/routes/(main)/+layout.server.ts
+++ b/code/app/src/routes/(main)/+layout.server.ts
@@ -1,35 +1,41 @@
-import {api_base, CookieNames} from "$configuration";
-import {log_debug, log_error} from "$help/logger";
-import {error, redirect} from "@sveltejs/kit";
-import {Temporal} from "temporal-polyfill";
-import type {LayoutServerLoad} from "./$types";
+import { api_base, CookieNames } from "$configuration";
+import { cached_result_async, CacheKeys } from "$help/cache";
+import { log_debug, log_error } from "$help/logger";
+import { md5 } from "$help/md5";
+import { error, redirect } from "@sveltejs/kit";
+import type { LayoutServerLoad } from "./$types";
-export const load: LayoutServerLoad = async ({url, request, route, cookies, locals, fetch}) => {
- console.log(url.toString());
+export const load: LayoutServerLoad = async ({ route, cookies, locals, fetch }) => {
const isBaseRoute = route.id === "/(main)";
- const isPublicRoute = (route.id?.startsWith("/(main)/(public)") || isBaseRoute) ?? true;
+ const isPortalRoute = route.id === "/(main)/(public)/portal";
+ const isPublicRoute = (isBaseRoute || (route.id?.startsWith("/(main)/(public)") ?? false)) ?? true;
const sessionCookieValue = cookies.get(CookieNames.session);
- const hasSessionCookie = (sessionCookieValue?.length > 0 ?? false);
- const sessionIsValid = hasSessionCookie && (await cached_result_async<Response>("sessionCheck", 120, () => fetch(api_base("_/is-authenticated"), {
- headers: {
- Cookie: CookieNames.session + "=" + sessionCookieValue,
- },
- }).catch((e) => {
- log_error(e);
- throw error(503, {
- message: "We are experiencing a service disruption! Have patience while we resolve the issue.",
- });
- }))).ok;
+ let sessionIsValid = false;
+ if ((sessionCookieValue?.length > 0 ?? false)) {
+ const sessionHash = md5(sessionCookieValue);
+ sessionIsValid = (await cached_result_async<Response>(sessionHash + "_" + CacheKeys.isAuthenticated, 120, () => fetch(api_base("_/is-authenticated"), {
+ headers: {
+ Cookie: CookieNames.session + "=" + sessionCookieValue,
+ },
+ }).catch((e) => {
+ log_error(e);
+ throw error(503, {
+ message: "We are experiencing a service disruption! Have patience while we resolve the issue.",
+ });
+ }))).ok;
+ }
log_debug("Base Layout loaded", {
sessionIsValid,
isPublicRoute,
+ isBaseRoute,
+ isPortalRoute,
routeId: route.id,
});
- if (sessionIsValid && isPublicRoute) {
+ if (sessionIsValid && isPublicRoute && !isPortalRoute) {
throw redirect(302, "/home");
- } else if (isBaseRoute || !sessionIsValid && !isPublicRoute) {
+ } else if (!isPortalRoute && (isBaseRoute || !sessionIsValid && !isPublicRoute)) {
throw redirect(302, "/sign-in");
}
@@ -37,29 +43,3 @@ export const load: LayoutServerLoad = async ({url, request, route, cookies, loca
locale: locals.locale,
};
};
-
-let resultCache = {};
-
-async function cached_result_async<T>(key: string, staleAfterSeconds: number, get_result: any, forceRefresh: boolean = false) {
- if (!resultCache[key]) {
- resultCache[key] = {
- l: 0,
- c: undefined as T,
- };
- }
- const staleEpoch = ((resultCache[key]?.l ?? 0) + staleAfterSeconds);
- const isStale = forceRefresh || (staleEpoch < Temporal.Now.instant().epochSeconds);
- if (isStale || !resultCache[key]?.c) {
- resultCache[key].c = await get_result();
- resultCache[key].l = Temporal.Now.instant().epochSeconds;
- }
-
- log_debug("Ran cached_result_async", {
- cacheKey: key,
- isStale,
- cache: resultCache[key],
- staleEpoch,
- });
-
- return resultCache[key].c as T;
-} \ No newline at end of file