mirror of
https://github.com/rajnandan1/kener.git
synced 2026-05-12 14:18:35 -05:00
Implement URL resolver functions for client and server, update navigation links to use resolved URLs, and remove deprecated nav component
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
type ResolveFn = (...args: any[]) => string;
|
||||
|
||||
/**
|
||||
* Wrapper for SvelteKit's resolve function
|
||||
* @param resolve - The resolve function from $app/paths
|
||||
* @param path - The route path or route ID (e.g., "/blog/[slug]") or absolute URL
|
||||
* @param params - Optional parameters for dynamic route segments
|
||||
* @returns The resolved URL with base path, or the original URL if it's absolute
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // Using a static path
|
||||
* urlResolve(resolve, "/dashboard-apis/monitor-bar")
|
||||
*
|
||||
* // Using a dynamic route with params
|
||||
* urlResolve(resolve, "/blog/[slug]", { slug: "hello-world" })
|
||||
*
|
||||
* // Using an absolute URL (returns as-is)
|
||||
* urlResolve(resolve, "https://example.com/api")
|
||||
* ```
|
||||
*/
|
||||
export default function urlResolve(resolve: ResolveFn, path: string, params?: Record<string, string>): string {
|
||||
// If path is an absolute URL, return it as-is
|
||||
if (path.startsWith("http://") || path.startsWith("https://")) {
|
||||
return path;
|
||||
}
|
||||
|
||||
if (params) {
|
||||
return resolve(path, params);
|
||||
}
|
||||
return resolve(path);
|
||||
}
|
||||
@@ -2,6 +2,8 @@
|
||||
import { page } from "$app/state";
|
||||
import * as NavigationMenu from "$lib/components/ui/navigation-menu/index.js";
|
||||
import { navigationMenuTriggerStyle } from "$lib/components/ui/navigation-menu/navigation-menu-trigger.svelte";
|
||||
import { resolve } from "$app/paths";
|
||||
import urlResolve from "$lib/client/resolver.js";
|
||||
|
||||
let { data } = page;
|
||||
const navItems: { name: string; url: string; iconURL: string }[] = data.navItems || [];
|
||||
@@ -13,12 +15,12 @@
|
||||
<div class="bg-background flex items-center justify-between rounded-3xl border p-1">
|
||||
<!-- Brand -->
|
||||
<a
|
||||
href={siteUrl}
|
||||
href={urlResolve(resolve, siteUrl)}
|
||||
class="{navigationMenuTriggerStyle()} hover:border-border border border-transparent text-xs hover:bg-transparent"
|
||||
style="border-radius: var(--radius-3xl)"
|
||||
>
|
||||
{#if logo}
|
||||
<img src={logo} alt={siteName} class="mr-2 h-6 w-6 rounded-full object-cover" />
|
||||
<img src={urlResolve(resolve, logo)} alt={siteName} class="mr-2 h-6 w-6 rounded-full object-cover" />
|
||||
{/if}
|
||||
{siteName}
|
||||
</a>
|
||||
@@ -31,14 +33,14 @@
|
||||
<NavigationMenu.Link>
|
||||
{#snippet child()}
|
||||
<a
|
||||
href={item.url}
|
||||
href={urlResolve(resolve, item.url)}
|
||||
class="{navigationMenuTriggerStyle()} hover:border-border border border-transparent text-xs hover:bg-transparent"
|
||||
target={item.url.startsWith("http") ? "_blank" : undefined}
|
||||
rel={item.url.startsWith("http") ? "noopener noreferrer" : undefined}
|
||||
style="border-radius: var(--radius-3xl)"
|
||||
>
|
||||
{#if item.iconURL}
|
||||
<img src={item.iconURL} alt={item.name} class="mr-2 h-4 w-4" />
|
||||
<img src={urlResolve(resolve, item.iconURL)} alt={item.name} class="mr-2 h-4 w-4" />
|
||||
{/if}
|
||||
{item.name}
|
||||
</a>
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
import StatusBarCalendar from "$lib/components/StatusBarCalendar.svelte";
|
||||
import { selectedTimezone } from "$lib/stores/timezone";
|
||||
import type { MonitorBarResponse, BarData } from "$lib/server/api-server/monitor-bar/get.js";
|
||||
import { resolve } from "$app/paths";
|
||||
|
||||
interface Props {
|
||||
tag: string;
|
||||
@@ -52,7 +53,7 @@
|
||||
try {
|
||||
const endOfDayTodayAtTz = getEndOfDayAtTz($selectedTimezone);
|
||||
const response = await fetch(
|
||||
`/dashboard-apis/monitor-bar?tag=${encodeURIComponent(tag)}&endOfDayTodayAtTz=${endOfDayTodayAtTz}`
|
||||
`${resolve("/dashboard-apis/monitor-bar")}?tag=${encodeURIComponent(tag)}&endOfDayTodayAtTz=${endOfDayTodayAtTz}`
|
||||
);
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to fetch monitor data");
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
<script>
|
||||
import { Button } from "$lib/components/ui/button";
|
||||
import * as DropdownMenu from "$lib/components/ui/dropdown-menu";
|
||||
import Languages from "lucide-svelte/icons/languages";
|
||||
import Menu from "lucide-svelte/icons/menu";
|
||||
import { base } from "$app/paths";
|
||||
import { analyticsEvent } from "$lib/boringOne";
|
||||
import GMI from "$lib/components/gmi.svelte";
|
||||
export let data;
|
||||
let defaultPattern = data.site?.pattern || "squares";
|
||||
let allPets = [
|
||||
{
|
||||
url: base + "/chicken.gif",
|
||||
bottom: "-5"
|
||||
},
|
||||
{
|
||||
url: base + "/dog.gif",
|
||||
bottom: "-17"
|
||||
},
|
||||
{
|
||||
url: base + "/cockatiel.gif",
|
||||
bottom: "-10"
|
||||
},
|
||||
{
|
||||
url: base + "/crab.gif",
|
||||
bottom: "-20"
|
||||
},
|
||||
{
|
||||
url: base + "/fox.gif",
|
||||
bottom: "-9"
|
||||
},
|
||||
{
|
||||
url: base + "/horse.gif",
|
||||
bottom: "-11"
|
||||
},
|
||||
{
|
||||
url: base + "/panda.gif",
|
||||
bottom: "0"
|
||||
},
|
||||
{
|
||||
url: base + "/totoro.gif",
|
||||
bottom: "-27"
|
||||
},
|
||||
{
|
||||
url: base + "/rabbit.gif",
|
||||
bottom: "0"
|
||||
},
|
||||
{
|
||||
url: base + "/duck.gif",
|
||||
bottom: "-5"
|
||||
},
|
||||
{
|
||||
url: base + "/snake.gif",
|
||||
bottom: "0"
|
||||
}
|
||||
];
|
||||
let randomPet = allPets[Math.floor(Math.random() * allPets.length)];
|
||||
</script>
|
||||
|
||||
{#if defaultPattern == "pets" && !!randomPet}
|
||||
<div class="pets-pattern" style="background-image: url({randomPet.url});bottom: {randomPet.bottom}px"></div>
|
||||
{:else}
|
||||
<div class="{defaultPattern}-pattern"></div>
|
||||
{/if}
|
||||
|
||||
<header class="sticky top-0 z-50 mx-auto md:mt-2">
|
||||
<div class="bg-card container flex h-14 max-w-[820px] items-center border px-3 md:rounded-md">
|
||||
<a rel="external" href={data.site.home ? data.site.home : base} class="mr-6 flex items-center space-x-2">
|
||||
{#if data.site.logo}
|
||||
<GMI src={data.site.logo} classList="w-8" alt={data.site.title} srcset="" />
|
||||
{/if}
|
||||
{#if data.site.siteName}
|
||||
<span class=" inline-block text-[15px] font-bold lg:text-base">
|
||||
{data.site.siteName}
|
||||
</span>
|
||||
{/if}
|
||||
</a>
|
||||
<div class="flex w-full justify-end">
|
||||
{#if data.site.nav}
|
||||
<nav class=" hidden flex-wrap items-center text-sm font-medium md:flex">
|
||||
{#each data.site.nav as navItem}
|
||||
<a
|
||||
rel="external"
|
||||
href={navItem.url}
|
||||
class="text-card-foreground hover:bg-background flex rounded-md px-3 py-2 transition-all ease-linear"
|
||||
on:click={() =>
|
||||
analyticsEvent("navigation", {
|
||||
name: navItem.name
|
||||
})}
|
||||
>
|
||||
{#if navItem.iconURL}
|
||||
<GMI src={navItem.iconURL} classList="mr-1.5 mt-0.5 inline h-4" alt={navItem.name} />
|
||||
{/if}
|
||||
<span>{navItem.name}</span>
|
||||
</a>
|
||||
{/each}
|
||||
</nav>
|
||||
<div class="flex md:hidden">
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger>
|
||||
<Button variant="outline" size="sm">
|
||||
<Menu size={14} />
|
||||
</Button>
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content>
|
||||
{#each data.site.nav as navItem}
|
||||
<DropdownMenu.Group>
|
||||
<DropdownMenu.Item>
|
||||
<a rel="external" href={navItem.url}> {navItem.name} </a>
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Group>
|
||||
{/each}
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* Server-side URL resolver that uses KENER_BASE_PATH environment variable
|
||||
* Works in both SvelteKit and Node scheduler contexts
|
||||
*
|
||||
* @param path - The route path (e.g., "/api/monitor") or absolute URL
|
||||
* @param params - Optional parameters for dynamic route segments (e.g., { slug: "hello" })
|
||||
* @returns The resolved URL with base path, or the original URL if it's absolute
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // Using a static path
|
||||
* serverResolve("/dashboard-apis/monitor-bar")
|
||||
* // Returns: "/status/dashboard-apis/monitor-bar" (if KENER_BASE_PATH=/status)
|
||||
*
|
||||
* // Using dynamic route with params
|
||||
* serverResolve("/blog/[slug]", { slug: "hello-world" })
|
||||
* // Returns: "/status/blog/hello-world"
|
||||
*
|
||||
* // Using an absolute URL (returns as-is)
|
||||
* serverResolve("https://example.com/api")
|
||||
* // Returns: "https://example.com/api"
|
||||
* ```
|
||||
*/
|
||||
export function serverResolve(path: string, params?: Record<string, string>): string {
|
||||
// If path is an absolute URL, return it as-is
|
||||
if (path.startsWith("http://") || path.startsWith("https://")) {
|
||||
return path;
|
||||
}
|
||||
|
||||
// Get base path from environment variable
|
||||
const basePath = process.env.KENER_BASE_PATH || "";
|
||||
|
||||
// Replace route parameters if provided
|
||||
let resolvedPath = path;
|
||||
if (params) {
|
||||
for (const [key, value] of Object.entries(params)) {
|
||||
resolvedPath = resolvedPath.replace(`[${key}]`, value);
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure path starts with /
|
||||
if (!resolvedPath.startsWith("/")) {
|
||||
resolvedPath = "/" + resolvedPath;
|
||||
}
|
||||
|
||||
// Combine base path with resolved path
|
||||
// Ensure no double slashes
|
||||
const fullPath = basePath + resolvedPath;
|
||||
return fullPath.replace(/\/+/g, "/");
|
||||
}
|
||||
|
||||
export default serverResolve;
|
||||
@@ -1,21 +1,19 @@
|
||||
import { redirect } from "@sveltejs/kit";
|
||||
import type { PageServerLoad } from "./$types";
|
||||
import dotenv from "dotenv";
|
||||
import { VerifyToken } from "$lib/server/controllers/controller.js";
|
||||
import db from "$lib/server/db/db.js";
|
||||
|
||||
dotenv.config();
|
||||
import serverResolve from "$lib/server/resolver.js";
|
||||
|
||||
export const load: PageServerLoad = async ({ cookies }) => {
|
||||
const tokenData = cookies.get("kener-user");
|
||||
if (tokenData) {
|
||||
const tokenUser = await VerifyToken(tokenData);
|
||||
if (!tokenUser) {
|
||||
throw redirect(302, "/account/logout");
|
||||
throw redirect(302, serverResolve("/account/logout"));
|
||||
}
|
||||
const userDB = await db.getUserByEmail(tokenUser.email);
|
||||
if (userDB) {
|
||||
throw redirect(302, "/manage/app/site-configurations");
|
||||
throw redirect(302, serverResolve("/manage/app/site-configurations"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
import { error } from "@sveltejs/kit";
|
||||
import type { PageServerLoad } from "./$types";
|
||||
import { GetPageDashboardData } from "$lib/server/controllers/dashboardController.js";
|
||||
import { resolve } from "$app/paths";
|
||||
import { env } from "$env/dynamic/private";
|
||||
|
||||
export const load: PageServerLoad = async ({ url }) => {
|
||||
const pagePath = url.pathname.substring(1); // Remove leading slash
|
||||
|
||||
const dashboardData = await GetPageDashboardData(pagePath);
|
||||
export const load: PageServerLoad = async ({ url, params }) => {
|
||||
const pagePath = url.pathname.replace(/\//g, ""); // Remove all slash if it exists
|
||||
const base = !!env.KENER_BASE_PATH ? env.KENER_BASE_PATH.substring(1) : ""; // Remove leading slash from base path if it exists
|
||||
const normalizedPagePath = base && pagePath.startsWith(base) ? pagePath.substring(base.length) : pagePath;
|
||||
const dashboardData = await GetPageDashboardData(normalizedPagePath);
|
||||
if (!dashboardData) {
|
||||
throw error(404, "Page Not Found");
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
<script lang="ts">
|
||||
import Bell from "@lucide/svelte/icons/bell";
|
||||
import { t } from "$lib/stores/i18n";
|
||||
|
||||
import * as Item from "$lib/components/ui/item/index.js";
|
||||
|
||||
import { resolve } from "$app/paths";
|
||||
import EventsCard from "$lib/components/EventsCard.svelte";
|
||||
import MonitorBar from "$lib/components/MonitorBar.svelte";
|
||||
import ThemePlus from "$lib/components/ThemePlus.svelte";
|
||||
|
||||
@@ -18,8 +18,6 @@ function getAllowedHost(origin: string): string | undefined {
|
||||
|
||||
export default defineConfig(({ mode }) => {
|
||||
const port = Number(process.env.PORT) || 3000;
|
||||
const basePath = (process.env.KENER_BASE_PATH ?? "").trim();
|
||||
const viteBase = basePath === "" ? undefined : basePath.endsWith("/") ? basePath : `${basePath}/`;
|
||||
|
||||
const buildEnv = process.env.VITE_BUILD_ENV || mode || "development";
|
||||
const isProduction = buildEnv === "production";
|
||||
@@ -28,7 +26,6 @@ export default defineConfig(({ mode }) => {
|
||||
const allowedHost = getAllowedHost(origin);
|
||||
|
||||
return {
|
||||
base: viteBase,
|
||||
optimizeDeps: {
|
||||
include: ["rrule"],
|
||||
exclude: [
|
||||
|
||||
Reference in New Issue
Block a user