diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..435060c3 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,124 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## What is Kener? + +Kener is an open-source status page application built with **SvelteKit 2.x (Svelte 5)** and **Node.js/Express**. It provides real-time monitoring, uptime tracking, incident management, and customizable dashboards. The codebase is migrating to **TypeScript-first**. + +## Development Commands + +```bash +npm run dev # Start dev server (SvelteKit + cron scheduler in parallel) +npm run build # Production build (SvelteKit then esbuild server bundle) +npm run start # Run production build (node build/main.js) +npm run check # Svelte + TypeScript type checking +npm run prettify # Format all files with Prettier +npm run migrate # Run database migrations via Knex +npm run seed # Run database seeds (migrations run automatically first) +``` + +## Architecture + +### Dual Process Model + +In development, `npm run dev` runs two parallel processes: + +1. **SvelteKit dev server** (`vite dev`) - serves the frontend +2. **Cron scheduler** (`vite-node src/lib/server/startup.ts`) - runs monitor checks, maintenance scheduler, daily cleanup + +In production, `scripts/main.ts` is the single entry point: Express server + SvelteKit handler + migrations + seeds + scheduler startup. + +### SvelteKit Route Groups + +- **`(kener)/`** - Public status page +- **`(manage)/`** - Admin dashboard (authenticated) +- **`(embed)/`** - Embeddable widgets +- **`(docs)/`** - Documentation pages +- **`(api)/`** - SvelteKit API routes; also `src/lib/server/api-server/` for Express-side API handlers (file-based routing: `./action/method.ts`) +- **`(account)/`** - Account/auth pages +- **`(ext)/`** - External integrations +- **`(assets)/`** - Asset serving + +### Database + +- **Knex.js** for query building and migrations. Supports SQLite (default), PostgreSQL, MySQL +- Connection configured via `DATABASE_URL` env var: `sqlite://./path`, `postgresql://...`, `mysql://...` +- Migrations in `/migrations/`, seeds in `/seeds/` +- Always use the db singleton: `import db from "$lib/server/db/db"` + +### Monitor Services + +Each monitor type has a dedicated implementation in `src/lib/server/services/`: + +- Types: API, Ping, TCP, DNS, SSL, SQL, Heartbeat, GameDig, Group +- Scheduled via `src/lib/server/schedulers/` using `croner` +- Job queues managed with **BullMQ** + **Redis** (`src/lib/server/queues/`) + +### Build System + +`npm run build` is a two-step process: + +1. `scripts/build-sveltekit.js` - Vite build of SvelteKit app (optionally with `--with-docs`) +2. `scripts/build-server.js` - esbuild bundles `scripts/main.ts` into `build/main.js` + +## Key Conventions + +### Svelte 5 + TypeScript + +- Use **TypeScript** for new/modified code +- Use **Svelte 5 runes** (`$state`, `$derived`, `$effect`, `$props()`) in new components +- Use generated `$types` for SvelteKit route typing (`import type { PageServerLoad } from './$types'`) +- Use `import type { ... }` for type imports + +### UI Components + +- **shadcn-svelte** components in `src/lib/components/ui/` +- Import: `import { Button } from "$lib/components/ui/button"` +- Styling: **Tailwind CSS v4** with HSL CSS variables for theming + +### Timestamps + +All timestamps are **UTC seconds** (not milliseconds). Use helpers from `src/lib/server/tool.ts`. + +### Status Constants + +#### When svelte code + +```typescript +import { UP, DOWN, DEGRADED, MAINTENANCE, NO_DATA } from "$lib/server/global-constants.ts" +``` + +#### When server code use directory traversal + +```typescript +import { UP, DOWN, DEGRADED, MAINTENANCE, NO_DATA } from "./global-constants.ts" +``` + +### API Authentication + +APIs use Bearer token auth: `import { VerifyAPIKey } from "$lib/server/controllers/controller.js"` + +### Types Location + +- `src/lib/types/` - Shared types (client + server) +- `src/lib/server/types/` - Server-only types +- `src/lib/client/types/` - Client-only types + +### i18n + +Locale files in `src/lib/locales/`. Add translations by creating `{code}.json` and updating `locales.json`. + +## Environment Variables + +Required: `KENER_SECRET_KEY`, `ORIGIN`, `DATABASE_URL` +Optional: `KENER_BASE_PATH`, `PORT` (default 3000), `RESEND_API_KEY`, `RESEND_SENDER_EMAIL` + +## Skills + +Read `.claude/skills/` for specialized instructions on: + +- **svelte-code-writer** - Svelte component creation/editing +- **documentation-writer** - Editing docs in `src/routes/(docs)/docs/content/` +- **tailwindcss** - Tailwind CSS v4 patterns +- **code-context** - Architecture documentation in `.codecontext/` diff --git a/migrations/20260310120000_alter_page_subheader_to_text.ts b/migrations/20260310120000_alter_page_subheader_to_text.ts new file mode 100644 index 00000000..f77e5c81 --- /dev/null +++ b/migrations/20260310120000_alter_page_subheader_to_text.ts @@ -0,0 +1,13 @@ +import type { Knex } from "knex"; + +export async function up(knex: Knex): Promise { + await knex.schema.alterTable("pages", (table) => { + table.text("page_subheader").alter(); + }); +} + +export async function down(knex: Knex): Promise { + await knex.schema.alterTable("pages", (table) => { + table.string("page_subheader", 255).alter(); + }); +} diff --git a/package-lock.json b/package-lock.json index 83f76bb4..2b9fae2d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -47,7 +47,6 @@ "figlet": "^1.9.4", "flexsearch": "^0.8.212", "front-matter": "^4.0.2", - "fs-extra": "^11.3.2", "gamedig": "^5.3.2", "glob": "^13.0.6", "highlight.js": "^11.11.1", @@ -96,7 +95,6 @@ "@types/d3-shape": "^3.1.8", "@types/dns2": "^2.0.10", "@types/express": "^5.0.6", - "@types/fs-extra": "^11.0.4", "@types/jsonwebtoken": "^9.0.10", "@types/mustache": "^4.2.6", "@types/node": "^25.0.3", @@ -2651,17 +2649,6 @@ "@types/send": "*" } }, - "node_modules/@types/fs-extra": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.4.tgz", - "integrity": "sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/jsonfile": "*", - "@types/node": "*" - } - }, "node_modules/@types/http-cache-semantics": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", @@ -2675,16 +2662,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/jsonfile": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.4.tgz", - "integrity": "sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/jsonwebtoken": { "version": "9.0.10", "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", @@ -5247,20 +5224,6 @@ "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", "license": "MIT" }, - "node_modules/fs-extra": { - "version": "11.3.3", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.3.tgz", - "integrity": "sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==", - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -6361,18 +6324,6 @@ "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", "license": "MIT" }, - "node_modules/jsonfile": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", - "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, "node_modules/jsonwebtoken": { "version": "9.0.3", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", @@ -10327,15 +10278,6 @@ "devOptional": true, "license": "MIT" }, - "node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", diff --git a/package.json b/package.json index 0a4e53ee..924efd62 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,6 @@ "@types/d3-shape": "^3.1.8", "@types/dns2": "^2.0.10", "@types/express": "^5.0.6", - "@types/fs-extra": "^11.0.4", "@types/jsonwebtoken": "^9.0.10", "@types/mustache": "^4.2.6", "@types/node": "^25.0.3", @@ -136,7 +135,6 @@ "figlet": "^1.9.4", "flexsearch": "^0.8.212", "front-matter": "^4.0.2", - "fs-extra": "^11.3.2", "gamedig": "^5.3.2", "glob": "^13.0.6", "highlight.js": "^11.11.1", diff --git a/src/lib/components/ThemePlus.svelte b/src/lib/components/ThemePlus.svelte index 3bd426c9..537f177a 100644 --- a/src/lib/components/ThemePlus.svelte +++ b/src/lib/components/ThemePlus.svelte @@ -78,6 +78,7 @@
+ {#if !!!page.data.globalPageVisibilitySettings.forceExclusivity && page.data.globalPageVisibilitySettings.showSwitcher} {/if} @@ -169,6 +170,7 @@ {/if}
+ {#if !!page.data.announcement && !!page.data.announcement.title && !!page.data.announcement.message} {/if} diff --git a/src/routes/(docs)/docs.json b/src/routes/(docs)/docs.json index 84e8663c..10e0284f 100644 --- a/src/routes/(docs)/docs.json +++ b/src/routes/(docs)/docs.json @@ -269,6 +269,21 @@ "name": "API Reference", "url": "/docs/spec/v4/" }, + { + "name": "Showcase", + "sidebar": [ + { + "group": "Showcase", + "collapsible": false, + "pages": [ + { + "title": "Showcase", + "content": "v4/showcase/showcase" + } + ] + } + ] + }, { "name": "Changelogs", "sidebar": [ diff --git a/src/routes/(docs)/docs/content/v4/showcase/showcase.md b/src/routes/(docs)/docs/content/v4/showcase/showcase.md new file mode 100644 index 00000000..60dfb429 --- /dev/null +++ b/src/routes/(docs)/docs/content/v4/showcase/showcase.md @@ -0,0 +1,24 @@ +--- +title: Showcase +description: Status pages built with Kener by the community +--- + +A collection of status pages built with Kener. Want to add yours? + +> [!IMPORTANT] +> [Submit your status page here](https://app.youform.com/forms/ifsczllv) to get featured on this page. + +
+ +
+ Kener +
+ Kener +
+ +
+ Cashfree Payments +
+ Cashfree Payments +
+