diff --git a/.github/copilot-api-instructions.md b/.github/copilot-api-instructions.md index 7b78b004..3c086f90 100644 --- a/.github/copilot-api-instructions.md +++ b/.github/copilot-api-instructions.md @@ -24,18 +24,29 @@ src/routes/(api)/api/ - **Repository**: `src/lib/server/db/repositories/*.ts` - Database operations - **DbImpl**: `src/lib/server/db/dbimpl.ts` - Bindings for repository methods +### Current Locals (set by middleware in `hooks.server.ts`) +```typescript +interface Locals { + user?: SessionUser; // Auth session + monitor?: MonitorRecordTyped; // /api/monitors/:monitor_tag/* + incident?: IncidentRecord; // /api/incidents/:incident_id/* + maintenance?: MaintenanceRecord; // /api/maintenances/:maintenance_id/* + page?: PageRecord; // /api/pages/:page_path/* +} +``` + ## Naming Conventions ### Use snake_case for API payloads ```typescript -// ✅ Correct +// Correct interface CreateMonitorRequest { monitor_tag: string; start_date_time: number; duration_seconds: number; } -// ❌ Wrong +// Wrong interface CreateMonitorRequest { monitorTag: string; startDateTime: number; @@ -251,7 +262,7 @@ export const DELETE: RequestHandler = async ({ locals }) => { // Delete related records first (cascade) await db.deleteResourceRelatedRecords(resource.id); - + // Delete the resource itself await db.deleteResource(resource.id); @@ -275,8 +286,8 @@ const normalizedTs = GetMinuteStartTimestampUTC(body.start_date_time); const now = GetNowTimestampUTC(); // For optional timestamp with fallback -const timestamp = body.timestamp !== undefined - ? GetMinuteStartTimestampUTC(body.timestamp) +const timestamp = body.timestamp !== undefined + ? GetMinuteStartTimestampUTC(body.timestamp) : GetMinuteStartNowTimestampUTC(); ``` @@ -300,8 +311,8 @@ if (typeof body.count !== "number" || isNaN(body.count) || body.count <= 0) { ```typescript const VALID_STATUSES = ["ACTIVE", "INACTIVE"]; if (body.status && !VALID_STATUSES.includes(body.status)) { - return json({ - error: { code: "BAD_REQUEST", message: `status must be one of: ${VALID_STATUSES.join(", ")}` } + return json({ + error: { code: "BAD_REQUEST", message: `status must be one of: ${VALID_STATUSES.join(", ")}` } }, { status: 400 }); } ``` @@ -311,8 +322,8 @@ if (body.status && !VALID_STATUSES.includes(body.status)) { if (body.monitor_tag) { const monitor = await db.getMonitorByTag(body.monitor_tag); if (!monitor) { - return json({ - error: { code: "BAD_REQUEST", message: `Monitor with tag '${body.monitor_tag}' not found` } + return json({ + error: { code: "BAD_REQUEST", message: `Monitor with tag '${body.monitor_tag}' not found` } }, { status: 400 }); } } @@ -324,7 +335,7 @@ if (body.items !== undefined) { if (!Array.isArray(body.items)) { return json({ error: { code: "BAD_REQUEST", message: "items must be an array" } }, { status: 400 }); } - + for (const item of body.items) { if (!item.tag || typeof item.tag !== "string") { return json({ error: { code: "BAD_REQUEST", message: "Each item must have a valid tag" } }, { status: 400 }); diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index ba0d7e92..6311bc0c 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -2,25 +2,37 @@ ## Project Overview -Kener is an open-source status page application built with **SvelteKit 2.x** (**Svelte 5**) and **Node.js**, and is migrating to a **TypeScript-first** codebase. It provides real-time monitoring, uptime tracking, incident management, and customizable dashboards. +Kener is an open-source status page application built with **SvelteKit 2.x** (**Svelte 5**) and **Node.js/Express**. It is a **TypeScript-first** codebase providing real-time monitoring, uptime tracking, incident management, and customizable dashboards. ## Architecture -### Entry Points -- **`main.js`** - Production server entry: Express + SvelteKit handler + cron scheduler -- **`src/lib/server/startup.js`** - Cron job scheduler for monitors (runs every minute) +### Dual Process Model + +In development, `npm run dev` runs two parallel processes: +1. **SvelteKit dev server** (`vite dev`) - serves the frontend with HMR +2. **Cron scheduler** (`vite-node src/lib/server/startup.ts`) - runs monitor checks, maintenance scheduling, daily cleanup + +In production, **`scripts/main.ts`** is the single entry point: Express server + SvelteKit handler + migrations + seeds + scheduler startup. Built output runs via `node build/main.js`. ### Route Groups (SvelteKit) - **`(kener)/`** - Public status page routes - **`(manage)/`** - Admin dashboard (requires authentication) - **`(embed)/`** - Embeddable widgets - **`(docs)/`** - Documentation pages +- **`(api)/`** - SvelteKit API routes +- **`(account)/`** - Account/auth pages +- **`(ext)/`** - External integrations +- **`(assets)/`** - Asset serving ### Core Server Components -- **`src/lib/server/controllers/controller.js`** - Main business logic (~1700 lines), handles monitors, incidents, auth, email -- **`src/lib/server/db/dbimpl.js`** - Database abstraction layer using Knex.js -- **`src/lib/server/services/`** - Monitor type implementations: API, Ping, TCP, DNS, SSL, SQL, Heartbeat, GameDig, Group -- **`src/lib/server/cron-minute.js`** - Per-monitor cron execution logic +- **`src/lib/server/controllers/`** - Domain-split controllers (18 TypeScript files): `apiController.ts`, `incidentController.ts`, `monitorsController.ts`, `maintenanceController.ts`, `pagesController.ts`, `userController.ts`, `dashboardController.ts`, `emailController.ts`, `siteDataController.ts`, `validators.ts`, etc. +- **`src/lib/server/db/dbimpl.ts`** - Database abstraction layer using Knex.js with repository composition pattern +- **`src/lib/server/db/repositories/`** - Domain-driven repositories: `monitors.ts`, `incidents.ts`, `maintenances.ts`, `pages.ts`, `users.ts`, `alerts.ts`, `monitoring.ts`, `images.ts`, `subscriptionSystem.ts`, `emailTemplateConfig.ts`, `monitorAlertConfig.ts`, `site-data.ts` +- **`src/lib/server/services/`** - Monitor type implementations (all TypeScript): `apiCall.ts`, `pingCall.ts`, `tcpCall.ts`, `dnsCall.ts`, `sslCall.ts`, `sqlCall.ts`, `heartbeatCall.ts`, `gamedigCall.ts`, `groupCall.ts`, `grpcCall.ts`, `noneCall.ts` +- **`src/lib/server/schedulers/`** - Scheduling via `croner`: `appScheduler.ts`, `monitorSchedulers.ts`, `maintenanceScheduler.ts`, `dailyCleanup.ts`, `shutdown.ts` +- **`src/lib/server/queues/`** - Job queues via **BullMQ** + **Redis**: `monitorExecuteQueue.ts`, `monitorResponseQueue.ts`, `alertingQueue.ts`, `emailQueue.ts`, `subscriberQueue.ts` +- **`src/lib/server/api-server/`** - Express-side API handlers with file-based routing (directory/method pattern: e.g., `monitor-bar/get.ts`) +- **`src/lib/server/cron-minute.ts`** - Per-monitor cron execution logic ### Database - Supports SQLite (default), PostgreSQL, MySQL via **Knex.js** @@ -28,83 +40,97 @@ Kener is an open-source status page application built with **SvelteKit 2.x** (** - Migrations in `/migrations/`, seeds in `/seeds/` - Run migrations: `npm run migrate` or auto-runs on `npm start` +### 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` + ## Development Commands ```bash -npm run dev # Start dev server with hot reload + cron scheduler -npm run build # Production build -npm run preview # Preview production build -npm run check # Typecheck + Svelte checks (uses tsconfig) +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 ``` ## Key Patterns ### Svelte 5 + TypeScript conventions -- Prefer **TypeScript** for new/modified code (`.ts`, and `.svelte` with `lang="ts"`). -- Prefer **Svelte 5 runes** for component state/effects in new code (e.g. `$state`, `$derived`, `$effect`). -- Prefer Svelte 5 props via `$props()` in new components. Keep existing `export let` props where already used to avoid churn. -- For SvelteKit route typing, prefer generated `$types` (e.g. `import type { PageServerLoad } from './$types'`). -- Avoid packages that hard-require Svelte 4 (they can break or force `--legacy-peer-deps`). +- Use **TypeScript** for all code (`.ts`, and `.svelte` with `lang="ts"`). +- Use **Svelte 5 runes** (`$state`, `$derived`, `$effect`, `$props()`) in components. +- For SvelteKit route typing, use generated `$types` (e.g. `import type { PageServerLoad } from './$types'`). +- Avoid packages that hard-require Svelte 4. ### Monitor Types -Defined in `src/lib/server/services/service.js`. Each type has its own implementation file: -```javascript -// Supported: API, PING, TCP, DNS, GROUP, SSL, SQL, HEARTBEAT, GAMEDIG +Defined in `src/lib/server/services/service.ts`. Each type has its own implementation file: +```typescript +// Supported: API, PING, TCP, DNS, GROUP, SSL, SQL, HEARTBEAT, GAMEDIG, GRPC, NONE ``` ### Status Constants -Use constants from `src/lib/server/constants`: -```javascript -import { UP, DOWN, DEGRADED, MAINTENANCE, NO_DATA } from "./constants"; +Use constants from `src/lib/global-constants.ts`: +```typescript +// In Svelte/client code: +import { UP, DOWN, DEGRADED, MAINTENANCE, NO_DATA } from "$lib/global-constants"; + +// In server code (use relative path): +import { UP, DOWN, DEGRADED, MAINTENANCE, NO_DATA } from "./global-constants"; ``` ### API Authentication APIs use Bearer token auth verified via `VerifyAPIKey()`: -```javascript -import { VerifyAPIKey } from "$lib/server/controllers/controller.js"; +```typescript +import { VerifyAPIKey } from "$lib/server/controllers/apiController"; ``` ### Database Queries Always use the db singleton, never instantiate Knex directly: -```javascript +```typescript import db from "$lib/server/db/db"; const monitor = await db.getMonitorByTag(tag); ``` ### Timestamps -All timestamps are **UTC seconds** (not milliseconds). Use helpers from `src/lib/server/tool.js`: -```javascript -import { GetMinuteStartNowTimestampUTC, GetNowTimestampUTC } from "./tool"; +All timestamps are **UTC seconds** (not milliseconds). Use helpers from `src/lib/server/tool.ts`: +```typescript +import { GetMinuteStartTimestampUTC, GetNowTimestampUTC } from "$lib/server/tool"; ``` ### i18n -Locales are in `src/lib/locales/`. Add new translations by creating `{code}.json` and updating `locales.json`. +21 locale files in `src/lib/locales/` (en, de, fr, es, hi, ja, ko, zh-CN, zh-TW, pt-BR, ru, etc.). Add new translations by creating `{code}.json` and updating `locales.json`. ## UI Components -Uses **shadcn-svelte** components in `src/lib/components/ui/`. Import pattern: -```javascript +Uses **shadcn-svelte** components in `src/lib/components/ui/` (40+ components). Import pattern: +```typescript import { Button } from "$lib/components/ui/button"; ``` -Styling: **TailwindCSS** with HSL CSS variables for theming (see `tailwind.config.js`). +Styling: **Tailwind CSS v4** with CSS-based configuration (no `tailwind.config.js`). Theme uses HSL CSS variables defined in `src/routes/layout.css`. ## Environment Variables -Required in `.env`: -- `KENER_SECRET_KEY` - JWT secret for auth +Required: +- `KENER_SECRET_KEY` - Secret key for auth - `ORIGIN` - Site URL (e.g., `http://localhost:3000`) -- `DATABASE_URL` - Database connection string +- `REDIS_URL` - Redis connection string (required for BullMQ job queues) Optional: +- `DATABASE_URL` - Database connection string (defaults to SQLite) - `KENER_BASE_PATH` - Base path for reverse proxy +- `PORT` - Server port (default 3000) - `RESEND_API_KEY` / `RESEND_SENDER_EMAIL` - Email notifications ## File Conventions - Server-only code: `src/lib/server/` - Shared utilities: `src/lib/` (except `server/`) -- Route data loading: `+page.server.ts` / `+layout.server.ts` (and client-side `+page.ts` / `+layout.ts` when needed) +- Client utilities: `src/lib/client/` +- Route data loading: `+page.server.ts` / `+layout.server.ts` - API endpoints: `+server.ts` files returning `json()` ## Types & Interfaces @@ -112,29 +138,7 @@ Optional: Place types and interfaces in the appropriate folder based on where they are used: - **`src/lib/types/`** - Shared types (safe to import from both server and client code). Use for domain models, DTOs, API response types, and anything needed on both sides. -- **`src/lib/server/types/`** - Server-only types. Use for DB models, internal service types, auth/session types, and anything that uses `$env/static/private` or Node-only APIs. -- **`src/lib/client/types/`** - Client-only types. Use for UI-specific types, component prop types, and anything that relies on browser/DOM APIs. +- **`src/lib/server/types/`** - Server-only types (`db.ts`, `auth.ts`, `monitor.ts`, `api-server.ts`). Use for DB models, internal service types, auth/session types. +- **`src/lib/client/types/`** - Client-only types (`ui.ts`). Use for UI-specific types, component prop types. Always use `import type { ... }` when importing types to avoid accidental runtime imports. - -# Other skills - -Read files in .claude/skills for more instructions on specific tasks or file types. - -## Code Architecture Documentation (MUST) - -For every coding task that touches architecture (multi-file features, refactors, new integrations): - -1. **Before edits** - - Read and apply `.claude/skills/code-context/SKILL.md`. - - Load relevant architecture docs from `.codecontext/` when present. - -2. **Before finishing the response** - - If the task revealed new architecture knowledge (code flow, edge cases, component relationships, design decisions), write/update a `.codecontext/*.md` entry as a clean reference doc. - - Skip if the task was trivial (typo fixes, single-line edits). - -3. **Final response contract** - - Include a short line: `Context loaded: ...` - - Include a short line: `Context updated: ...` - -`.codecontext/` documents **code architecture only** — not session logs, changelogs, or task summaries. \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md index 435060c3..8f524cf2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## 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**. +Kener is an open-source status page application built with **SvelteKit 2.x (Svelte 5)** and **Node.js/Express**. It is a **TypeScript-first** codebase providing real-time monitoring, uptime tracking, incident management, and customizable dashboards. ## Development Commands @@ -51,7 +51,7 @@ In production, `scripts/main.ts` is the single entry point: Express server + Sve Each monitor type has a dedicated implementation in `src/lib/server/services/`: -- Types: API, Ping, TCP, DNS, SSL, SQL, Heartbeat, GameDig, Group +- Types: API, Ping, TCP, DNS, SSL, SQL, Heartbeat, GameDig, Group, gRPC, None - Scheduled via `src/lib/server/schedulers/` using `croner` - Job queues managed with **BullMQ** + **Redis** (`src/lib/server/queues/`) @@ -83,21 +83,21 @@ All timestamps are **UTC seconds** (not milliseconds). Use helpers from `src/lib ### Status Constants -#### When svelte code +Constants are exported as a **default export** from `src/lib/global-constants.ts`: ```typescript -import { UP, DOWN, DEGRADED, MAINTENANCE, NO_DATA } from "$lib/server/global-constants.ts" -``` +// In Svelte/client code or SvelteKit routes: +import GC from "$lib/global-constants"; +// Usage: GC.UP, GC.DOWN, GC.DEGRADED, GC.MAINTENANCE, GC.NO_DATA -#### When server code use directory traversal - -```typescript -import { UP, DOWN, DEGRADED, MAINTENANCE, NO_DATA } from "./global-constants.ts" +// In server code (use relative path): +import GC from "../../global-constants.js"; +// Usage: GC.UP, GC.DOWN, etc. ``` ### API Authentication -APIs use Bearer token auth: `import { VerifyAPIKey } from "$lib/server/controllers/controller.js"` +APIs use Bearer token auth: `import { VerifyAPIKey } from "$lib/server/controllers/apiController"` ### Types Location @@ -111,8 +111,8 @@ Locale files in `src/lib/locales/`. Add translations by creating `{code}.json` a ## Environment Variables -Required: `KENER_SECRET_KEY`, `ORIGIN`, `DATABASE_URL` -Optional: `KENER_BASE_PATH`, `PORT` (default 3000), `RESEND_API_KEY`, `RESEND_SENDER_EMAIL` +Required: `KENER_SECRET_KEY`, `ORIGIN`, `REDIS_URL` +Optional: `DATABASE_URL` (defaults to SQLite), `KENER_BASE_PATH`, `PORT` (default 3000), `RESEND_API_KEY`, `RESEND_SENDER_EMAIL` ## Skills @@ -121,4 +121,3 @@ 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/`