From 9e8b0962a0b4a5c9975198e8a8891021d60ea2ad Mon Sep 17 00:00:00 2001 From: Raj Nandan Sharma Date: Mon, 9 Feb 2026 08:26:16 +0530 Subject: [PATCH] changes --- check-output.txt | 17 +- main.ts | 135 ----------- package.json | 8 +- scripts/build-server.js | 52 +++++ scripts/build-sveltekit.js | 51 +++++ scripts/index-docs.ts | 159 +++++++++++++ scripts/main.ts | 83 +++++++ src/hooks.server.ts | 4 - .../AllMaintenanceMonitorGrid.svelte | 6 +- src/lib/components/LanguageSelector.svelte | 2 +- src/lib/components/MinuteGrid.svelte | 2 +- src/lib/components/MonitorDayDetail.svelte | 6 +- src/lib/components/ThemePlus.svelte | 4 +- src/lib/i18n/client.ts | 114 ---------- src/lib/i18n/i18n.ts | 23 -- src/lib/i18n/locales/en.json | 9 - src/lib/i18n/server.ts | 45 ---- src/lib/locales/de.json | 92 -------- src/lib/locales/dk.json | 92 -------- src/lib/locales/en.json | 211 ++++++++++-------- src/lib/locales/es.json | 90 -------- src/lib/locales/fa.json | 92 -------- src/lib/locales/fr.json | 92 -------- src/lib/locales/hi.json | 211 ++++++++++-------- src/lib/locales/it.json | 92 -------- src/lib/locales/ja.json | 92 -------- src/lib/locales/ko.json | 92 -------- src/lib/locales/locales.json | 74 ------ src/lib/locales/nb-NO.json | 92 -------- src/lib/locales/nl.json | 92 -------- src/lib/locales/pl.json | 92 -------- src/lib/locales/pt-BR.json | 92 -------- src/lib/locales/ru.json | 211 ++++++++++-------- src/lib/locales/tr.json | 92 -------- src/lib/locales/vi.json | 92 -------- src/lib/locales/zh-CN.json | 92 -------- src/lib/server/docs-search.ts | 94 ++------ src/lib/stores/i18n.ts | 63 +++--- src/routes/(docs)/docs.json | 4 + .../(with-sidebar)/[...slug]/+page.svelte | 8 +- src/routes/(docs)/docs/+page.svelte | 9 +- src/routes/(docs)/docs/DocsNavbar.svelte | 7 +- .../(docs)/docs/DocsPageNavigation.svelte | 4 +- src/routes/(docs)/docs/DocsSearch.svelte | 5 +- src/routes/(docs)/docs/DocsSidebar.svelte | 4 +- .../(docs)/docs/DocsTableOfContents.svelte | 25 ++- .../(docs)/docs/content/architecture.md | 205 +++++++++++++++++ src/routes/(docs)/docs/docs-utils.server.ts | 47 ++-- .../incidents/[incident_id]/+page.svelte | 10 +- .../[maintenance_id]/+page.svelte | 6 +- src/routes/(manage)/+layout.server.ts | 1 - .../app/internationalization/+page.svelte | 6 +- static/locales/en.json | 120 ---------- static/locales/hi.json | 120 ---------- static/locales/ru.json | 120 ---------- 55 files changed, 1071 insertions(+), 2592 deletions(-) delete mode 100644 main.ts create mode 100644 scripts/build-server.js create mode 100644 scripts/build-sveltekit.js create mode 100644 scripts/index-docs.ts create mode 100644 scripts/main.ts delete mode 100644 src/lib/i18n/client.ts delete mode 100644 src/lib/i18n/i18n.ts delete mode 100644 src/lib/i18n/locales/en.json delete mode 100644 src/lib/i18n/server.ts delete mode 100644 src/lib/locales/de.json delete mode 100644 src/lib/locales/dk.json delete mode 100644 src/lib/locales/es.json delete mode 100644 src/lib/locales/fa.json delete mode 100644 src/lib/locales/fr.json delete mode 100644 src/lib/locales/it.json delete mode 100644 src/lib/locales/ja.json delete mode 100644 src/lib/locales/ko.json delete mode 100644 src/lib/locales/locales.json delete mode 100644 src/lib/locales/nb-NO.json delete mode 100644 src/lib/locales/nl.json delete mode 100644 src/lib/locales/pl.json delete mode 100644 src/lib/locales/pt-BR.json delete mode 100644 src/lib/locales/tr.json delete mode 100644 src/lib/locales/vi.json delete mode 100644 src/lib/locales/zh-CN.json create mode 100644 src/routes/(docs)/docs/content/architecture.md delete mode 100644 static/locales/en.json delete mode 100644 static/locales/hi.json delete mode 100644 static/locales/ru.json diff --git a/check-output.txt b/check-output.txt index 811f6b20..aa62ead3 100644 --- a/check-output.txt +++ b/check-output.txt @@ -2,24 +2,23 @@ > kener@4.0.0 check > svelte-kit sync && svelte-check --tsconfig ./tsconfig.json -[dotenv@17.2.3] injecting env (18) from .env -- tip: 🔄 add secrets lifecycle management: https://dotenvx.com/ops +[dotenv@17.2.3] injecting env (18) from .env -- tip: ⚙️ enable debug logging with { debug: true } Loading svelte-check in workspace: /Users/rajnandan1/Code/kener Getting Svelte diagnostics... -[dotenv@17.2.3] injecting env (18) from .env -- tip: 👥 sync secrets across teammates & machines: https://dotenvx.com/ops -[dotenv@17.2.3] injecting env (0) from .env -- tip: ⚙️ specify custom .env file path with { path: '/custom/path/.env' } -[dotenv@17.2.3] injecting env (0) from .env -- tip: ⚙️ override existing env vars with { override: true } +[dotenv@17.2.3] injecting env (18) from .env -- tip: 🔐 prevent committing .env to code: https://dotenvx.com/precommit +[dotenv@17.2.3] injecting env (0) from .env -- tip: 🔐 prevent building .env in docker: https://dotenvx.com/prebuild +[dotenv@17.2.3] injecting env (0) from .env -- tip: 🔑 add access controls to secrets: https://dotenvx.com/ops /Users/rajnandan1/Code/kener/src/lib/components/ui/chart/chart-container.svelte:20:28 Warn: This reference only captures the initial value of `id`. Did you mean to reference it inside a closure instead? https://svelte.dev/e/state_referenced_locally (svelte) const chartId = `chart-${id || uid.replace(/:/g, "")}`; -/Users/rajnandan1/Code/kener/src/lib/server/api-server/monitor-data/post.ts:6:10 -Error: Module '"$lib/server/controllers/monitorsController"' has no exported member 'CalculateUptimeByTags'. -import { ParseUptime } from "$lib/server/tool"; -import { CalculateUptimeByTags, GetLatestMonitoringData } from "$lib/server/controllers/monitorsController"; -import GC from "$lib/global-constants"; +/Users/rajnandan1/Code/kener/src/routes/(manage)/+layout.server.ts:1:18 +Error: Cannot find module '$lib/i18n/server' or its corresponding type declarations. +import i18n from "$lib/i18n/server"; +import { redirect } from "@sveltejs/kit"; /Users/rajnandan1/Code/kener/src/routes/(docs)/docs/DocsNavbar.svelte:126:3 Warn: Unused CSS selector ".active" diff --git a/main.ts b/main.ts deleted file mode 100644 index ee3890ea..00000000 --- a/main.ts +++ /dev/null @@ -1,135 +0,0 @@ -// import { handler } from "./build/handler.js"; -import { apiReference } from "@scalar/express-api-reference"; -import dotenv from "dotenv"; -dotenv.config(); -import express from "express"; -import Startup from "./src/lib/server/startup.ts"; -import shutdownSchedulers from "./src/lib/server/schedulers/shutdown.ts"; -import shutdownQueues from "./src/lib/server/queues/shutdown.ts"; -import dbInstance from "./src/lib/server/db/db.ts"; -import fs from "fs-extra"; -import knex from "knex"; -import knexOb from "./knexfile.js"; - -const PORT = process.env.PORT || 3000; -const base = process.env.KENER_BASE_PATH || ""; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const app: any = express(); -const db = knex(knexOb); - -if (process.env.ORIGIN) { - process.env.SVELTEKIT_ORIGIN = process.env.ORIGIN; -} - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -app.use((req: any, res: any, next: any) => { - if (req.path.startsWith("/embed")) { - res.setHeader("Content-Security-Policy", "frame-ancestors *"); - } - res.setHeader("X-Powered-By", "Kener"); - next(); -}); -// eslint-disable-next-line @typescript-eslint/no-explicit-any -app.get(base + "/healthcheck", (req: any, res: any) => { - res.end("ok"); -}); - -//part /uploads server static files from static/uploads - -//set env variable for upload path -process.env.UPLOAD_PATH = "./uploads"; - -app.use(base + "/uploads", express.static("uploads")); - -try { - const openapiJSON = fs.readFileSync("./openapi.json", "utf-8"); - app.use( - "/api-reference", - apiReference({ - spec: { - content: openapiJSON, - }, - theme: "alternate", - hideModels: true, - hideTestRequestButton: true, - darkMode: true, - metaData: { - title: "Kener API Reference", - description: "Kener free open source status page API Reference", - ogDescription: "Kener free open source status page API Reference", - ogTitle: "Kener API Reference", - ogImage: "https://kener.ing/newbg.png", - twitterCard: "summary_large_image", - twitterTitle: "Kener API Reference", - twitterDescription: "Kener free open source status page API Reference", - twitterImage: "https://kener.ing/newbg.png", - }, - favicon: "https://kener.ing/logo96.png", - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } as any), - ); -} catch (e) { - console.warn("Error loading openapi.json, but that is okay."); -} - -// app.use(handler); - -//migrations -async function runMigrations() { - try { - console.log("Running migrations..."); - await db.migrate.latest(); // Runs migrations to the latest state - console.log("Migrations completed successfully!"); - } catch (err) { - console.error("Error running migrations:", err); - } -} - -//seed -async function runSeed() { - try { - console.log("Running seed..."); - await db.seed.run(); // Runs seed to the latest state - console.log("Seed completed successfully!"); - } catch (err) { - console.error("Error running seed:", err); - } -} - -app.listen(PORT, async () => { - await runMigrations(); - await runSeed(); - await db.destroy(); - Startup(); - console.log("Kener is running on port " + PORT + "!"); -}); - -// Graceful shutdown handler -async function gracefulShutdown(signal: string) { - console.log(`\nReceived ${signal}. Starting graceful shutdown...`); - - try { - console.log("Shutting down schedulers..."); - await shutdownSchedulers(); - console.log("Schedulers shut down successfully."); - - console.log("Shutting down queues..."); - await shutdownQueues(); - console.log("Queues shut down successfully."); - - console.log("Closing database connection..."); - await dbInstance.close(); - console.log("Database connection closed successfully."); - - console.log("Graceful shutdown completed."); - process.exit(0); - } catch (err) { - console.error("Error during graceful shutdown:", err); - process.exit(1); - } -} - -// Handle termination signals -process.on("SIGTERM", () => gracefulShutdown("SIGTERM")); -process.on("SIGINT", () => gracefulShutdown("SIGINT")); diff --git a/package.json b/package.json index 98de130e..db3aab74 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,10 @@ "url": "https://github.com/rajnandan1/kener.git" }, "scripts": { - "build": "vite build", + "build": "node scripts/build-sveltekit.js && node scripts/build-server.js", + "build-with-docs": "node scripts/build-sveltekit.js --with-docs && node scripts/build-server.js", + "build:sveltekit": "vite build", + "build:server": "node scripts/build-server.js", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", "configure": "node build.js", @@ -36,6 +39,7 @@ "development": "vite dev", "devschedule": "vite-node src/lib/server/startup.ts", "generate-readme": "node scripts/generate-readme.js", + "index-docs": "vite-node scripts/index-docs.ts", "migrate": "npx knex migrate:latest", "predev": "npm run seed", "prepare": "svelte-kit sync || echo ''", @@ -44,7 +48,7 @@ "preview": "vite preview", "schedule": "vite-node src/lib/server/startup.ts", "seed": "npx knex seed:run", - "start": "npx tsx main.ts" + "start": "node build/main.js" }, "devDependencies": { "@internationalized/date": "^3.10.0", diff --git a/scripts/build-server.js b/scripts/build-server.js new file mode 100644 index 00000000..9660782a --- /dev/null +++ b/scripts/build-server.js @@ -0,0 +1,52 @@ +import * as esbuild from "esbuild"; +import { readFileSync } from "fs"; + +const pkg = JSON.parse(readFileSync("./package.json", "utf8")); + +// Collect all dependency names to externalize, except CJS packages +// that need to be bundled for ESM compatibility +const CJS_PACKAGES_TO_BUNDLE = ["rrule"]; + +const externalDeps = [...Object.keys(pkg.dependencies || {}), ...Object.keys(pkg.devDependencies || {})].filter( + (dep) => !CJS_PACKAGES_TO_BUNDLE.includes(dep), +); + +await esbuild.build({ + entryPoints: ["scripts/main.ts"], + bundle: true, + platform: "node", + target: "node20", + format: "esm", + outfile: "build/main.js", + // Externalize all node_modules except CJS packages that break ESM named imports + external: externalDeps, + alias: { + // Map SvelteKit's $lib alias so server code resolves correctly + $lib: "./src/lib", + }, + define: { + // Inject version at build time so src/lib/version.ts resolves it + // without relying on vite-plugin-package-version at runtime + "import.meta.env.PACKAGE_VERSION": JSON.stringify(pkg.version), + }, + plugins: [ + { + name: "rewrite-build-imports", + setup(build) { + // Since the output lives in build/, rewrite ../build/X → ./X + // so that handler.js (SvelteKit output) resolves correctly + build.onResolve({ filter: /^\.\.\/build\// }, (args) => { + return { + path: args.path.replace(/^\.\.\/build\//, "./"), + external: true, + }; + }); + }, + }, + ], + banner: { + js: "// Kener production server – built with esbuild", + }, +}); + +console.log(`Server build completed: build/main.js (v${pkg.version})`); diff --git a/scripts/build-sveltekit.js b/scripts/build-sveltekit.js new file mode 100644 index 00000000..b289b9aa --- /dev/null +++ b/scripts/build-sveltekit.js @@ -0,0 +1,51 @@ +/** + * SvelteKit build script that optionally excludes docs routes. + * + * Usage: + * node scripts/build-sveltekit.js # build WITHOUT docs + * node scripts/build-sveltekit.js --with-docs # build WITH docs + */ +import { execSync } from "child_process"; +import { renameSync, existsSync, rmSync } from "fs"; +import { resolve, dirname } from "path"; +import { fileURLToPath } from "url"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const rootDir = resolve(__dirname, ".."); + +const withDocs = process.argv.includes("--with-docs"); + +const docsDir = resolve(rootDir, "src/routes/(docs)"); +const docsHiddenDir = resolve(rootDir, ".docs-excluded"); + +function moveDocs(from, to) { + if (existsSync(from)) { + renameSync(from, to); + } +} + +function build() { + if (!withDocs) { + console.log("[build] Excluding docs routes from build..."); + moveDocs(docsDir, docsHiddenDir); + // Clean generated route types so stale docs routes don't persist + const svelteKitDir = resolve(rootDir, ".svelte-kit"); + if (existsSync(svelteKitDir)) { + rmSync(svelteKitDir, { recursive: true, force: true }); + } + } else { + console.log("[build] Including docs routes in build..."); + } + + try { + execSync("npx vite build", { cwd: rootDir, stdio: "inherit" }); + } finally { + // Always restore docs folder, even if build fails + if (!withDocs) { + moveDocs(docsHiddenDir, docsDir); + console.log("[build] Restored docs routes."); + } + } +} + +build(); diff --git a/scripts/index-docs.ts b/scripts/index-docs.ts new file mode 100644 index 00000000..dfc44ecc --- /dev/null +++ b/scripts/index-docs.ts @@ -0,0 +1,159 @@ +/** + * Standalone script to index documentation content into Redis for full-text search. + * + * This script reads docs.json and all markdown files from disk, converts them + * to plain text, and stores the search documents in Redis. It should be run + * manually whenever documentation content changes. + * + * Usage: npm run index-docs + */ + +import fs from "fs"; +import path from "path"; +import { fileURLToPath } from "url"; +import fm from "front-matter"; +import IORedis from "ioredis"; +import dotenv from "dotenv"; +import { marked } from "marked"; +import plaintify from "marked-plaintify"; +import { mdToText } from "../src/lib/marked"; + +dotenv.config(); + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const DOCS_JSON_PATH = path.join(__dirname, "../src/routes/(docs)/docs.json"); +const CONTENT_DIR = path.join(__dirname, "../src/routes/(docs)/docs/content"); +const REDIS_DOCS_KEY = "kener-docs:search:documents"; + +interface DocsPage { + title: string; + slug: string; + pages?: DocsPage[]; +} + +interface DocsSidebarGroup { + group: string; + pages: DocsPage[]; +} + +interface DocsConfig { + sidebar: DocsSidebarGroup[]; +} + +interface DocsSearchDocument { + id: string; + title: string; + slug: string; + group: string; + content: string; + rawContent: string; +} + +interface DocsSearchIndexData { + documents: DocsSearchDocument[]; + lastUpdated: number; +} + +/** + * Read markdown content for a slug (body only, without frontmatter) + */ +function getMarkdownContent(slug: string): string | null { + const directPath = path.join(CONTENT_DIR, `${slug}.md`); + + try { + let rawContent: string | null = null; + + if (fs.existsSync(directPath)) { + rawContent = fs.readFileSync(directPath, "utf-8"); + } else { + const indexPath = path.join(CONTENT_DIR, slug, "index.md"); + if (fs.existsSync(indexPath)) { + rawContent = fs.readFileSync(indexPath, "utf-8"); + } + } + + if (!rawContent) return null; + + const parsed = fm(rawContent); + return parsed.body; + } catch { + return null; + } +} + +/** + * Recursively collect all pages from sidebar groups (including nested pages) + */ +function collectPages(pages: DocsPage[], group: string, result: Array<{ page: DocsPage; group: string }>): void { + for (const page of pages) { + result.push({ page, group }); + if (page.pages && page.pages.length > 0) { + collectPages(page.pages, group, result); + } + } +} + +async function main(): Promise { + // Validate Redis URL + if (!process.env.REDIS_URL) { + console.error("Error: REDIS_URL environment variable is not set."); + process.exit(1); + } + + // Read docs.json + if (!fs.existsSync(DOCS_JSON_PATH)) { + console.error(`Error: docs.json not found at ${DOCS_JSON_PATH}`); + process.exit(1); + } + + const config: DocsConfig = JSON.parse(fs.readFileSync(DOCS_JSON_PATH, "utf-8")); + const documents: DocsSearchDocument[] = []; + + // Collect all pages from sidebar + const allPages: Array<{ page: DocsPage; group: string }> = []; + for (const sidebarGroup of config.sidebar) { + collectPages(sidebarGroup.pages, sidebarGroup.group, allPages); + } + + console.log(`[index-docs] Found ${allPages.length} pages to index`); + + for (const { page, group } of allPages) { + const markdownContent = getMarkdownContent(page.slug); + if (markdownContent) { + const plainContent = mdToText(markdownContent); + documents.push({ + id: page.slug, + title: page.title, + slug: page.slug, + group, + content: plainContent, + rawContent: markdownContent, + }); + } else { + console.warn(`[index-docs] No content found for slug: ${page.slug}`); + } + } + + console.log(`[index-docs] Indexed ${documents.length} documents`); + + // Store in Redis + const redis = new IORedis(process.env.REDIS_URL, { maxRetriesPerRequest: null }); + + const indexData: DocsSearchIndexData = { + documents, + lastUpdated: Date.now(), + }; + + await redis.set(REDIS_DOCS_KEY, JSON.stringify(indexData)); + console.log(`[index-docs] Stored ${documents.length} documents in Redis (key: ${REDIS_DOCS_KEY})`); + + await redis.quit(); + console.log("[index-docs] Done."); +} + +main().catch((err) => { + console.error("[index-docs] Fatal error:", err); + process.exit(1); +}); diff --git a/scripts/main.ts b/scripts/main.ts new file mode 100644 index 00000000..95c20cf3 --- /dev/null +++ b/scripts/main.ts @@ -0,0 +1,83 @@ +import { handler } from "../build/handler.js"; +import { apiReference } from "@scalar/express-api-reference"; +import dotenv from "dotenv"; +dotenv.config(); +import express from "express"; +import Startup from "../src/lib/server/startup.ts"; +import shutdownSchedulers from "../src/lib/server/schedulers/shutdown.ts"; +import shutdownQueues from "../src/lib/server/queues/shutdown.ts"; +import dbInstance from "../src/lib/server/db/db.ts"; +import fs from "fs-extra"; +import knex from "knex"; +import knexOb from "../knexfile.js"; + +const PORT = process.env.PORT || 3000; +const base = process.env.KENER_BASE_PATH || ""; + +const app: any = express(); +const db = knex(knexOb); + +app.get(base + "/healthcheck", (req: any, res: any) => { + res.end("ok"); +}); + +app.use(handler); + +//migrations +async function runMigrations() { + try { + console.log("Running migrations..."); + await db.migrate.latest(); // Runs migrations to the latest state + console.log("Migrations completed successfully!"); + } catch (err) { + console.error("Error running migrations:", err); + } +} + +//seed +async function runSeed() { + try { + console.log("Running seed..."); + await db.seed.run(); // Runs seed to the latest state + console.log("Seed completed successfully!"); + } catch (err) { + console.error("Error running seed:", err); + } +} + +app.listen(PORT, async () => { + await runMigrations(); + await runSeed(); + await db.destroy(); + Startup(); + console.log("Kener is running on port " + PORT + "!"); +}); + +// Graceful shutdown handler +async function gracefulShutdown(signal: string) { + console.log(`\nReceived ${signal}. Starting graceful shutdown...`); + + try { + console.log("Shutting down schedulers..."); + await shutdownSchedulers(); + console.log("Schedulers shut down successfully."); + + console.log("Shutting down queues..."); + await shutdownQueues(); + console.log("Queues shut down successfully."); + + console.log("Closing database connection..."); + await dbInstance.close(); + console.log("Database connection closed successfully."); + + console.log("Graceful shutdown completed."); + process.exit(0); + } catch (err) { + console.error("Error during graceful shutdown:", err); + process.exit(1); + } +} + +// Handle termination signals +process.on("SIGTERM", () => gracefulShutdown("SIGTERM")); +process.on("SIGINT", () => gracefulShutdown("SIGINT")); diff --git a/src/hooks.server.ts b/src/hooks.server.ts index 169a8452..fea55c96 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -2,12 +2,8 @@ import { json, type Handle } from "@sveltejs/kit"; import { VerifyAPIKey } from "$lib/server/controllers/apiController"; import db from "$lib/server/db/db"; import type { UnauthorizedResponse, NotFoundResponse } from "$lib/types/api"; -import { initializeSearchIndex } from "$lib/server/docs-search"; import { GetMonitorsParsed } from "$lib/server/controllers/monitorsController"; -// Initialize documentation search index at server startup -initializeSearchIndex(); - const API_PATH_PREFIX = "/api/"; // Paths that don't require authentication diff --git a/src/lib/components/AllMaintenanceMonitorGrid.svelte b/src/lib/components/AllMaintenanceMonitorGrid.svelte index cdd6cd05..ea3d137f 100644 --- a/src/lib/components/AllMaintenanceMonitorGrid.svelte +++ b/src/lib/components/AllMaintenanceMonitorGrid.svelte @@ -1,8 +1,8 @@