diff --git a/src/app.d.ts b/src/app.d.ts index beaae02..d04fe0b 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -5,8 +5,9 @@ declare global { // interface Error {} interface Locals { session?: { - accessToken: string; - refreshToken: string; + accessToken: string | null; + refreshToken: string | null; + set(accessToken: string, refreshToken: string): Promise; }; } // interface PageData {} diff --git a/src/lib/authUri.ts b/src/lib/authUri.ts deleted file mode 100644 index c4018d1..0000000 --- a/src/lib/authUri.ts +++ /dev/null @@ -1,27 +0,0 @@ -import axios, { AxiosInstance } from "axios"; - -export async function fetchAuthRedirectUri(api_url: string): Promise<{ - uri: string; - callbackKey: string; -}> { - const client: AxiosInstance = axios.create({ - baseURL: api_url || "", - timeout: 5000, - }); - try { - const res = await client.get("/v1/auth/uri"); - const d = res.data ?? {}; - const uri = d.data.uri; - const callbackKey = d.data.callbackKey; - if (typeof uri !== "string" || !uri) - throw new Error("redirect uri missing from response"); - return { - uri, - callbackKey, - }; - } catch (e) { - throw new Error( - `Failed to fetch auth redirect uri: ${(e as Error).message}`, - ); - } -} diff --git a/src/lib/index.ts b/src/lib/index.ts index 9af6e32..caece70 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -1,10 +1,15 @@ // place files you want to import through the `$lib` alias in this folder. -export * from "./axios"; -export * from "./user"; -export * from "./companies"; -export * from "./credentialTypes"; +import { user } from "./optima-api/modules/user"; +export const optima = { + auth: (await import("./optima-api/modules/auth")).auth, + company: (await import("./optima-api/modules/companies")).company, + credential: (await import("./optima-api/modules/credentials")).credential, + credentialType: (await import("./optima-api/modules/credentialTypes")) + .credentialType, + user, +}; /** * @TODO * diff --git a/src/lib/axios.ts b/src/lib/optima-api/axios.ts similarity index 100% rename from src/lib/axios.ts rename to src/lib/optima-api/axios.ts diff --git a/src/lib/optima-api/modules/auth.ts b/src/lib/optima-api/modules/auth.ts new file mode 100644 index 0000000..a6da93e --- /dev/null +++ b/src/lib/optima-api/modules/auth.ts @@ -0,0 +1,29 @@ +import axios, { AxiosInstance } from "axios"; + +export const auth = { + async fetchAuthRedirectUri(api_url: string): Promise<{ + uri: string; + callbackKey: string; + }> { + const client: AxiosInstance = axios.create({ + baseURL: api_url || "", + timeout: 5000, + }); + try { + const res = await client.get("/v1/auth/uri"); + const d = res.data ?? {}; + const uri = d.data.uri; + const callbackKey = d.data.callbackKey; + if (typeof uri !== "string" || !uri) + throw new Error("redirect uri missing from response"); + return { + uri, + callbackKey, + }; + } catch (e) { + throw new Error( + `Failed to fetch auth redirect uri: ${(e as Error).message}`, + ); + } + }, +}; diff --git a/src/lib/companies.ts b/src/lib/optima-api/modules/companies.ts similarity index 97% rename from src/lib/companies.ts rename to src/lib/optima-api/modules/companies.ts index 6f0e156..fee0e3f 100644 --- a/src/lib/companies.ts +++ b/src/lib/optima-api/modules/companies.ts @@ -1,4 +1,4 @@ -import api from "./axios"; +import api from "../axios"; export const company = { async fetch(accessToken: string, id: string) { diff --git a/src/lib/credentialTypes.ts b/src/lib/optima-api/modules/credentialTypes.ts similarity index 98% rename from src/lib/credentialTypes.ts rename to src/lib/optima-api/modules/credentialTypes.ts index 7d92e12..b43b0cd 100644 --- a/src/lib/credentialTypes.ts +++ b/src/lib/optima-api/modules/credentialTypes.ts @@ -1,4 +1,4 @@ -import api from "./axios"; +import api from "../axios"; export interface CredentialTypeField { id: string; diff --git a/src/lib/credentials.ts b/src/lib/optima-api/modules/credentials.ts similarity index 98% rename from src/lib/credentials.ts rename to src/lib/optima-api/modules/credentials.ts index f43571d..e2a610a 100644 --- a/src/lib/credentials.ts +++ b/src/lib/optima-api/modules/credentials.ts @@ -1,4 +1,4 @@ -import api from "./axios"; +import api from "../axios"; export interface CredentialField { id: string; diff --git a/src/lib/user.ts b/src/lib/optima-api/modules/user.ts similarity index 100% rename from src/lib/user.ts rename to src/lib/optima-api/modules/user.ts diff --git a/src/routes/login/+page.server.ts b/src/routes/(auth)/login/+page.server.ts similarity index 85% rename from src/routes/login/+page.server.ts rename to src/routes/(auth)/login/+page.server.ts index 3eb0752..e452bea 100644 --- a/src/routes/login/+page.server.ts +++ b/src/routes/(auth)/login/+page.server.ts @@ -1,11 +1,11 @@ -import { user } from "$lib"; import { Actions, redirect } from "@sveltejs/kit"; +import { optima } from "$lib"; export const actions: Actions = { login: async (event) => { const data = await event.request.formData(); - const tokens = await user.awaitAuthCallback( + const tokens = await optima.user.awaitAuthCallback( data.get("callbackKey") as string, ); diff --git a/src/routes/login/+page.svelte b/src/routes/(auth)/login/+page.svelte similarity index 94% rename from src/routes/login/+page.svelte rename to src/routes/(auth)/login/+page.svelte index d66b134..2b5db56 100644 --- a/src/routes/login/+page.svelte +++ b/src/routes/(auth)/login/+page.svelte @@ -1,9 +1,9 @@ Home — App -
-

App Home

- -
- -
-
-

Welcome back

-

- This is your protected home page. Quick links and recent activity appear - below. -

-
- -
- - -
-

Recent activity

-

No recent activity.

-
-
+
+

Welcome

+

Your new landing page. Ready to build.

- -
- © {new Date().getFullYear()} Your App -
- - diff --git a/src/routes/admin/+layout.server.ts b/src/routes/admin/+layout.server.ts deleted file mode 100644 index 105b8ea..0000000 --- a/src/routes/admin/+layout.server.ts +++ /dev/null @@ -1,8 +0,0 @@ -import type { LayoutServerLoad } from "./$types"; - -export const load: LayoutServerLoad = async ({ params, parent }) => { - const { session } = await parent(); - return { - accessToken: session.accessToken, - }; -}; diff --git a/src/routes/admin/+page.svelte b/src/routes/admin/+page.svelte deleted file mode 100644 index 8439adf..0000000 --- a/src/routes/admin/+page.svelte +++ /dev/null @@ -1,49 +0,0 @@ - - - - Admin Dashboard — App - - -
-

Admin Dashboard

- -
- -
-
-

Administration

-

Manage system settings and configurations.

-
- -
-
-

Credential Types

-

Create and manage credential type definitions.

- -
-
-
- - diff --git a/src/routes/admin/credential-types/+page.server.ts b/src/routes/admin/credential-types/+page.server.ts deleted file mode 100644 index 10e1d5f..0000000 --- a/src/routes/admin/credential-types/+page.server.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { PageServerLoad } from "./$types"; -import { credentialType } from "$lib/credentialTypes"; - -export const load: PageServerLoad = async ({ params, parent }) => { - const { session } = await parent(); - const response = await credentialType.fetchMany(session.accessToken ?? ""); - return { - credentialTypes: response.data || [], - accessToken: session.accessToken, - }; -}; diff --git a/src/routes/admin/credential-types/+page.svelte b/src/routes/admin/credential-types/+page.svelte deleted file mode 100644 index d897377..0000000 --- a/src/routes/admin/credential-types/+page.svelte +++ /dev/null @@ -1,617 +0,0 @@ - - - - Credential Types — Admin - - -
-

Credential Types

- -
- -
- {#if isLoading && !showForm} - - {:else} - {#if error} - - {/if} - - {#if showForm} -
-

{editingId ? "Edit" : "Create"} Credential Type

- -
- - -
- -
- - -
- -
- - -
- -
-

Credential Fields *

- {#each formFields as field, index (field.id)} -
-
- - handleFieldNameBlur(index)} - /> -
- -
-
- - -
- -
- - -
-
- -
- - -
- - -
- {/each} - - -
- -
- - -
-
- {/if} - -
-

Credential Types ({credentialTypes.length})

- - {#if credentialTypes.length === 0} -

- No credential types yet. Create one to get started. -

- {:else} -
- {#each credentialTypes as ct (ct.id)} -
- {#if ct.icon} - {ct.name} - {/if} - -

{ct.name}

-

{ct.permissionScope}

- -
- Fields ({ct.fields.length}): -
    - {#each ct.fields as field} -
  • - {field.name} - {#if field.secure} - Secure - {/if} - {#if field.required} - Required - {/if} -
  • - {/each} -
-
- -
- Used by {ct.credentialCount} credential(s) -
- -
- Created: {new Date(ct.createdAt).toLocaleDateString()} - Updated: {new Date(ct.updatedAt).toLocaleDateString()} -
- -
- - -
-
- {/each} -
- {/if} -
- {/if} -
- - diff --git a/src/routes/companies/+page.svelte b/src/routes/companies/+page.svelte deleted file mode 100644 index 9722e0d..0000000 --- a/src/routes/companies/+page.svelte +++ /dev/null @@ -1,559 +0,0 @@ - - - - Companies — App - - -
-

Companies

- -
- -
-
-

Company Directory

- {#if isLoading} - - {:else if error} - - {:else} -

Browse all companies. Total: {totalRecords} companies

- {/if} -
- - {#if !isLoading && !error} -
- - {#if searchQuery} -

- Found {displayedCompanies.length} company/companies matching "{searchQuery}" -

- {/if} -
- - {#if displayedCompanies.length > 0} -
- {#if isResultsLoading} -
- -
- {:else} - {#each displayedCompanies as comp (comp.id)} -
goto(`/companies/${comp.id}`)} - on:keydown={(e) => { - if (e.key === "Enter" || e.key === " ") { - e.preventDefault(); - goto(`/companies/${comp.id}`); - } - }} - > -

{comp.name}

-
-
CW Company ID
-
{comp.cw_CompanyId}
-
CW Identifier
-
{comp.cw_Identifier}
-
Created
-
{new Date(comp.createdAt).toLocaleDateString()}
-
- View Details -
- {/each} - {/if} -
- {:else} -
-

No companies found

-
- {/if} - - {#if totalPages > 1 && !isResultsLoading} -
- - - - -
- {#each Array.from({ length: Math.min(totalPages, 5) }, (_, i) => { - const startPage = Math.max(1, currentPage - 2); - return startPage + i; - }) as page} - {#if page <= totalPages} - - {/if} - {/each} -
- - - - - - - Page {currentPage} of {totalPages} - -
- {/if} - {/if} -
- -
- © {new Date().getFullYear()} Your App -
- - diff --git a/src/routes/companies/[id]/+page.server.ts b/src/routes/companies/[id]/+page.server.ts deleted file mode 100644 index 01e23c6..0000000 --- a/src/routes/companies/[id]/+page.server.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { company } from "$lib/companies"; -import { credential } from "$lib/credentials"; -import type { PageServerLoad } from "./$types"; -import { error } from "@sveltejs/kit"; - -export const load: PageServerLoad = async ({ params, parent }) => { - const { session } = await parent(); - - if (!session.accessToken) { - throw error(401, "Unauthorized: Access token required"); - } - - try { - const companyData = await company.fetch(session.accessToken, params.id); - - if (!companyData) { - throw error(404, `Company with ID ${params.id} not found`); - } - - // attempt to load configurations but don't fail the whole page if it errors - let configurations = null; - let configurationsError = null; - try { - configurations = await company.fetchConfigurations( - session.accessToken, - params.id, - ); - } catch (cfgErr) { - console.error("Failed to fetch configurations:", cfgErr); - configurationsError = String( - cfgErr instanceof Error ? cfgErr.message : cfgErr, - ); - } - - // attempt to load credentials but don't fail the whole page if it errors - let credentials = null; - let credentialsError = null; - try { - credentials = await credential.fetchByCompany( - session.accessToken, - params.id, - ); - } catch (credErr) { - console.error("Failed to fetch credentials:", credErr); - credentialsError = String( - credErr instanceof Error ? credErr.message : credErr, - ); - } - - return { - company: companyData, - configurations, - configurationsError, - credentials, - credentialsError, - session, - companyId: params.id, - }; - } catch (err) { - console.error("Failed to fetch company:", err); - - if (err instanceof Error && err.message.includes("404")) { - throw error(404, `Company with ID ${params.id} not found`); - } - - if (err instanceof Error && err.message.includes("401")) { - throw error(401, "Your session has expired. Please log in again."); - } - - throw error(500, "Failed to load company details. Please try again."); - } -}; diff --git a/src/routes/companies/[id]/+page.svelte b/src/routes/companies/[id]/+page.svelte deleted file mode 100644 index 5444962..0000000 --- a/src/routes/companies/[id]/+page.svelte +++ /dev/null @@ -1,215 +0,0 @@ - - - - Company Detail — App - - -
-

Company Detail

- -
- -
- {#if error} - - {:else} -
-

API Response

-
- Show company JSON -
{JSON.stringify(data.company, null, 2)}
-
-
-
-

Configurations

- {#if data.configurationsError} - - {:else if data.configurations} -
- Show configurations JSON -
{JSON.stringify(data.configurations, null, 2)}
-
- {:else} -

No configurations available for this company.

- {/if} -
-
-
-

Credentials

- -
- {#if data.credentialsError} - - {:else if data.credentials && data.credentials.data && data.credentials.data.length > 0} -
- Show credentials JSON -
{JSON.stringify(data.credentials, null, 2)}
-
- {:else} -

No credentials available for this company.

- {/if} -
- {/if} -
- - - - diff --git a/src/routes/company/+page.svelte b/src/routes/company/+page.svelte deleted file mode 100644 index 153558f..0000000 --- a/src/routes/company/+page.svelte +++ /dev/null @@ -1,12 +0,0 @@ - - - - - Redirecting... - - -

Redirecting to /companies

diff --git a/src/routes/company/[id]/+page.server.ts b/src/routes/company/[id]/+page.server.ts deleted file mode 100644 index 0f63ed9..0000000 --- a/src/routes/company/[id]/+page.server.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { company } from "$lib/companies"; -import type { PageServerLoad } from "./$types"; -import { error } from "@sveltejs/kit"; - -export const load: PageServerLoad = async ({ params, parent }) => { - const { session } = await parent(); - - if (!session.accessToken) { - throw error(401, "Unauthorized: Access token required"); - } - - try { - const companyData = await company.fetch(session.accessToken, params.id); - - if (!companyData) { - throw error(404, `Company with ID ${params.id} not found`); - } - - // attempt to load configurations but don't fail the whole page if it errors - let configurations = null; - let configurationsError = null; - try { - configurations = await company.fetchConfigurations( - session.accessToken, - params.id, - ); - } catch (cfgErr) { - console.error("Failed to fetch configurations:", cfgErr); - configurationsError = String( - cfgErr instanceof Error ? cfgErr.message : cfgErr, - ); - } - - return { - company: companyData, - configurations, - configurationsError, - }; - } catch (err) { - console.error("Failed to fetch company:", err); - - if (err instanceof Error && err.message.includes("404")) { - throw error(404, `Company with ID ${params.id} not found`); - } - - if (err instanceof Error && err.message.includes("401")) { - throw error(401, "Your session has expired. Please log in again."); - } - - throw error(500, "Failed to load company details. Please try again."); - } -}; diff --git a/src/routes/company/[id]/+page.svelte b/src/routes/company/[id]/+page.svelte deleted file mode 100644 index 14fc7ba..0000000 --- a/src/routes/company/[id]/+page.svelte +++ /dev/null @@ -1,25 +0,0 @@ - - - - - Redirecting... - - -

Redirecting to /companies

diff --git a/src/routes/hooks.server.ts b/src/routes/hooks.server.ts new file mode 100644 index 0000000..a607558 --- /dev/null +++ b/src/routes/hooks.server.ts @@ -0,0 +1,21 @@ +// src/hooks.server.ts +import type { Handle } from "@sveltejs/kit"; + +export const handle: Handle = async ({ event, resolve }) => { + const accessToken = event.cookies.get("acceessToken") || null; + const refreshToken = event.cookies.get("refreshToken") || null; + + const setTokens = async (accessToken: string, refreshToken: string) => { + event.cookies.set("accessToken", accessToken, {} as any); + event.cookies.set("refreshToken", refreshToken, {} as any); + + event.locals.session = { accessToken, refreshToken, set: setTokens }; + + return; + }; + + event.locals.session = { accessToken, refreshToken, set: setTokens }; + + const response = await resolve(event); + return response; +}; diff --git a/src/routes/page.svelte.test.ts b/src/routes/page.svelte.test.ts deleted file mode 100644 index a110662..0000000 --- a/src/routes/page.svelte.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { describe, test, expect } from 'vitest'; -import '@testing-library/jest-dom/vitest'; -import { render, screen } from '@testing-library/svelte'; -import Page from './+page.svelte'; - -describe('/+page.svelte', () => { - test('should render h1', () => { - render(Page); - expect(screen.getByRole('heading', { level: 1 })).toBeInTheDocument(); - }); -}); diff --git a/src/app.css b/src/styles/app.css similarity index 100% rename from src/app.css rename to src/styles/app.css diff --git a/src/styles/home.css b/src/styles/home.css new file mode 100644 index 0000000..e69de29