mirror of
https://github.com/rajnandan1/kener.git
synced 2026-06-23 04:10:22 +00: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 { page } from "$app/state";
|
||||||
import * as NavigationMenu from "$lib/components/ui/navigation-menu/index.js";
|
import * as NavigationMenu from "$lib/components/ui/navigation-menu/index.js";
|
||||||
import { navigationMenuTriggerStyle } from "$lib/components/ui/navigation-menu/navigation-menu-trigger.svelte";
|
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;
|
let { data } = page;
|
||||||
const navItems: { name: string; url: string; iconURL: string }[] = data.navItems || [];
|
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">
|
<div class="bg-background flex items-center justify-between rounded-3xl border p-1">
|
||||||
<!-- Brand -->
|
<!-- Brand -->
|
||||||
<a
|
<a
|
||||||
href={siteUrl}
|
href={urlResolve(resolve, siteUrl)}
|
||||||
class="{navigationMenuTriggerStyle()} hover:border-border border border-transparent text-xs hover:bg-transparent"
|
class="{navigationMenuTriggerStyle()} hover:border-border border border-transparent text-xs hover:bg-transparent"
|
||||||
style="border-radius: var(--radius-3xl)"
|
style="border-radius: var(--radius-3xl)"
|
||||||
>
|
>
|
||||||
{#if logo}
|
{#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}
|
{/if}
|
||||||
{siteName}
|
{siteName}
|
||||||
</a>
|
</a>
|
||||||
@@ -31,14 +33,14 @@
|
|||||||
<NavigationMenu.Link>
|
<NavigationMenu.Link>
|
||||||
{#snippet child()}
|
{#snippet child()}
|
||||||
<a
|
<a
|
||||||
href={item.url}
|
href={urlResolve(resolve, item.url)}
|
||||||
class="{navigationMenuTriggerStyle()} hover:border-border border border-transparent text-xs hover:bg-transparent"
|
class="{navigationMenuTriggerStyle()} hover:border-border border border-transparent text-xs hover:bg-transparent"
|
||||||
target={item.url.startsWith("http") ? "_blank" : undefined}
|
target={item.url.startsWith("http") ? "_blank" : undefined}
|
||||||
rel={item.url.startsWith("http") ? "noopener noreferrer" : undefined}
|
rel={item.url.startsWith("http") ? "noopener noreferrer" : undefined}
|
||||||
style="border-radius: var(--radius-3xl)"
|
style="border-radius: var(--radius-3xl)"
|
||||||
>
|
>
|
||||||
{#if item.iconURL}
|
{#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}
|
{/if}
|
||||||
{item.name}
|
{item.name}
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
import StatusBarCalendar from "$lib/components/StatusBarCalendar.svelte";
|
import StatusBarCalendar from "$lib/components/StatusBarCalendar.svelte";
|
||||||
import { selectedTimezone } from "$lib/stores/timezone";
|
import { selectedTimezone } from "$lib/stores/timezone";
|
||||||
import type { MonitorBarResponse, BarData } from "$lib/server/api-server/monitor-bar/get.js";
|
import type { MonitorBarResponse, BarData } from "$lib/server/api-server/monitor-bar/get.js";
|
||||||
|
import { resolve } from "$app/paths";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
tag: string;
|
tag: string;
|
||||||
@@ -52,7 +53,7 @@
|
|||||||
try {
|
try {
|
||||||
const endOfDayTodayAtTz = getEndOfDayAtTz($selectedTimezone);
|
const endOfDayTodayAtTz = getEndOfDayAtTz($selectedTimezone);
|
||||||
const response = await fetch(
|
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) {
|
if (!response.ok) {
|
||||||
throw new Error("Failed to fetch monitor data");
|
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 { redirect } from "@sveltejs/kit";
|
||||||
import type { PageServerLoad } from "./$types";
|
import type { PageServerLoad } from "./$types";
|
||||||
import dotenv from "dotenv";
|
|
||||||
import { VerifyToken } from "$lib/server/controllers/controller.js";
|
import { VerifyToken } from "$lib/server/controllers/controller.js";
|
||||||
import db from "$lib/server/db/db.js";
|
import db from "$lib/server/db/db.js";
|
||||||
|
import serverResolve from "$lib/server/resolver.js";
|
||||||
dotenv.config();
|
|
||||||
|
|
||||||
export const load: PageServerLoad = async ({ cookies }) => {
|
export const load: PageServerLoad = async ({ cookies }) => {
|
||||||
const tokenData = cookies.get("kener-user");
|
const tokenData = cookies.get("kener-user");
|
||||||
if (tokenData) {
|
if (tokenData) {
|
||||||
const tokenUser = await VerifyToken(tokenData);
|
const tokenUser = await VerifyToken(tokenData);
|
||||||
if (!tokenUser) {
|
if (!tokenUser) {
|
||||||
throw redirect(302, "/account/logout");
|
throw redirect(302, serverResolve("/account/logout"));
|
||||||
}
|
}
|
||||||
const userDB = await db.getUserByEmail(tokenUser.email);
|
const userDB = await db.getUserByEmail(tokenUser.email);
|
||||||
if (userDB) {
|
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 { error } from "@sveltejs/kit";
|
||||||
import type { PageServerLoad } from "./$types";
|
import type { PageServerLoad } from "./$types";
|
||||||
import { GetPageDashboardData } from "$lib/server/controllers/dashboardController.js";
|
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 }) => {
|
export const load: PageServerLoad = async ({ url, params }) => {
|
||||||
const pagePath = url.pathname.substring(1); // Remove leading slash
|
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 dashboardData = await GetPageDashboardData(pagePath);
|
const normalizedPagePath = base && pagePath.startsWith(base) ? pagePath.substring(base.length) : pagePath;
|
||||||
|
const dashboardData = await GetPageDashboardData(normalizedPagePath);
|
||||||
if (!dashboardData) {
|
if (!dashboardData) {
|
||||||
throw error(404, "Page Not Found");
|
throw error(404, "Page Not Found");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Bell from "@lucide/svelte/icons/bell";
|
import Bell from "@lucide/svelte/icons/bell";
|
||||||
import { t } from "$lib/stores/i18n";
|
import { t } from "$lib/stores/i18n";
|
||||||
|
|
||||||
import * as Item from "$lib/components/ui/item/index.js";
|
import * as Item from "$lib/components/ui/item/index.js";
|
||||||
|
import { resolve } from "$app/paths";
|
||||||
import EventsCard from "$lib/components/EventsCard.svelte";
|
import EventsCard from "$lib/components/EventsCard.svelte";
|
||||||
import MonitorBar from "$lib/components/MonitorBar.svelte";
|
import MonitorBar from "$lib/components/MonitorBar.svelte";
|
||||||
import ThemePlus from "$lib/components/ThemePlus.svelte";
|
import ThemePlus from "$lib/components/ThemePlus.svelte";
|
||||||
|
|||||||
@@ -18,8 +18,6 @@ function getAllowedHost(origin: string): string | undefined {
|
|||||||
|
|
||||||
export default defineConfig(({ mode }) => {
|
export default defineConfig(({ mode }) => {
|
||||||
const port = Number(process.env.PORT) || 3000;
|
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 buildEnv = process.env.VITE_BUILD_ENV || mode || "development";
|
||||||
const isProduction = buildEnv === "production";
|
const isProduction = buildEnv === "production";
|
||||||
@@ -28,7 +26,6 @@ export default defineConfig(({ mode }) => {
|
|||||||
const allowedHost = getAllowedHost(origin);
|
const allowedHost = getAllowedHost(origin);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
base: viteBase,
|
|
||||||
optimizeDeps: {
|
optimizeDeps: {
|
||||||
include: ["rrule"],
|
include: ["rrule"],
|
||||||
exclude: [
|
exclude: [
|
||||||
|
|||||||
Reference in New Issue
Block a user