From 396fc5e3c3155402af7f7b6cc4a90f6b2bb1506a Mon Sep 17 00:00:00 2001 From: Raj Nandan Sharma Date: Wed, 25 Feb 2026 19:33:34 +0530 Subject: [PATCH] refactor: Update site configuration documentation and enhance global page visibility settings - Revise site configuration documentation to clarify page visibility behavior. - Introduce global page visibility settings with detailed descriptions and functionality. - Modify API server to dynamically select the correct specification path based on environment. - Streamline event fetching logic in event pages to improve performance and maintainability. - Remove unused vault secret management code from the manage API. - Enhance customizations page to support global page visibility settings. - Create new guides for adding custom fonts and custom JavaScript/CSS. - Implement server-side logic for handling events by month with improved date validation. --- .claude/skills/code-architecture/SKILL.md | 188 ----------- .claude/skills/code-context/SKILL.md | 182 +++++++++++ .codearch/localization.md | 9 - .codearch/versioning.md | 10 - .github/copilot-instructions.md | 20 +- AGENTS.md | 20 +- .../20260109120000_add_maintenances_tables.ts | 2 + ...0260123110239_maintenace_monitor_status.ts | 2 + ...0128120000_redesign_subscription_system.ts | 84 ++++- ...260219165446_add_global_colum_incidents.ts | 10 +- scripts/index-docs.ts | 15 +- src/lib/components/KenerNav.svelte | 12 +- .../components/NotificationsPopover.svelte | 15 +- src/lib/components/ThemePlus.svelte | 5 +- .../server/api-server/events-by-month/post.ts | 31 +- .../server/controllers/layoutController.ts | 4 +- .../server/controllers/siteDataController.ts | 2 + src/lib/server/controllers/siteDataKeys.ts | 5 + src/lib/server/controllers/vaultController.ts | 180 ----------- src/lib/server/db/dbimpl.ts | 29 -- src/lib/server/db/repositories/incidents.ts | 14 +- .../server/db/repositories/maintenances.ts | 14 +- src/lib/server/db/repositories/vault.ts | 128 -------- src/lib/server/db/seedSiteData.ts | 4 + src/lib/types/site.ts | 5 + src/routes/(docs)/docs.json | 8 + .../(docs)/docs/content/v3/monitors-api.md | 12 +- .../docs/content/v3/monitors-gamedig.md | 4 +- .../(docs)/docs/content/v3/monitors-ping.md | 8 +- .../(docs)/docs/content/v3/monitors-tcp.md | 8 +- .../docs/content/v4/changelogs/v4.0.0.md | 56 +++- .../content/v4/getting-started/basic-setup.md | 4 - .../v4/getting-started/introduction.md | 2 +- .../docs/content/v4/guides/custom-fonts.md | 61 ++++ .../content/v4/guides/custom-js-css-guide.md | 77 +++++ .../docs/content/v4/setup/customizations.md | 10 +- .../content/v4/setup/site-configuration.md | 47 ++- .../(docs)/docs/spec/v4/spec.json/+server.ts | 5 +- .../events/[MMMM]-[YYYY]/+page.server.ts | 46 +++ .../events/[MMMM]-[YYYY]/+page.svelte | 292 ++++++++++++++++++ .../(kener)/events/[MMMM]-[YYYY]/+page.svelte | 44 +-- src/routes/(manage)/manage/api/+server.ts | 49 --- .../manage/app/customizations/+page.svelte | 4 +- .../app/site-configurations/+page.svelte | 140 +++++++-- 44 files changed, 1160 insertions(+), 707 deletions(-) delete mode 100644 .claude/skills/code-architecture/SKILL.md create mode 100644 .claude/skills/code-context/SKILL.md delete mode 100644 .codearch/localization.md delete mode 100644 .codearch/versioning.md delete mode 100644 src/lib/server/controllers/vaultController.ts delete mode 100644 src/lib/server/db/repositories/vault.ts create mode 100644 src/routes/(docs)/docs/content/v4/guides/custom-fonts.md create mode 100644 src/routes/(docs)/docs/content/v4/guides/custom-js-css-guide.md create mode 100644 src/routes/(kener)/[page_path]/events/[MMMM]-[YYYY]/+page.server.ts create mode 100644 src/routes/(kener)/[page_path]/events/[MMMM]-[YYYY]/+page.svelte diff --git a/.claude/skills/code-architecture/SKILL.md b/.claude/skills/code-architecture/SKILL.md deleted file mode 100644 index 9919c633..00000000 --- a/.claude/skills/code-architecture/SKILL.md +++ /dev/null @@ -1,188 +0,0 @@ ---- -name: code-architecture -description: Persistent project memory via a `.codearch/` folder. -user-invokable: false -metadata: - category: architecture ---- - -# code-architecture — Project Memory Skill - -Use this skill at the START and END of every coding session, agentic task, or multi-step file operation. Trigger whenever the user asks you to work on a codebase, fix a bug, refactor, review code, or continue work from a previous session. Also trigger if the user mentions "context," "memory," "remember this," "pick up where we left off," or references prior work. This skill tells you how to read relevant context before acting and how to compress + save what you learned after acting. Always use it — skipping it loses hard-won context. - -`.codearch/` is a folder that lives at the project root. It stores compressed context files so you can resume work without re-deriving what was already figured out. - ---- - -## Before Every Run — Load Context - -**Do this before writing any code or answering any question about the codebase.** - -### Step 1: Scan the folder - -```bash -ls .codearch/ -``` - -If the folder doesn't exist, skip to the run. You'll create it after. - -### Step 2: Find relevant files - -Two approaches — use both if unsure: - -**A. File name scan** — Read the file names. Match against the current task (e.g., working on auth? look for `auth.md`, `session.md`, `jwt.md`). - -**B. Text search** — Search inside files for keywords from the task. - -```bash -grep -ril "" .codearch/ -``` - -Run multiple greps if the task spans several concepts. - -### Step 3: Read relevant files - -Read only the files that match. Don't load the entire folder into context unnecessarily. - -```bash -cat .codearch/.md -``` - -### Step 4: Apply the context - -Use what you read to inform your approach. Don't re-derive decisions already documented. If a file says "we chose X over Y because of Z," respect that unless the user explicitly overrides it. - ---- - -## After Every Run — Save Context - -**Do this after completing the task, before ending the session.** - -### Step 1: Write a summary - -Compress what happened into a short summary. Cover: - -- What the task was -- What you changed or built -- Key decisions made (and why) -- Gotchas, constraints, or warnings for next time -- Files/modules touched -- Anything left unresolved - -Keep it under 200 words. Dense is better than verbose. - -**Example summary:** - -``` -Task: Refactored auth middleware to support refresh tokens. -Changes: Modified src/middleware/auth.go and src/handlers/refresh.go. -Decisions: Used Redis for token storage (not DB) — avoids write contention under load. -Gotcha: JWT secret is loaded from env at startup only; restart required after rotation. -Unresolved: Rate limiting on /refresh endpoint not yet implemented. -``` - -### Step 2: Find the right file to update - -Check if the summary fits an existing `.codearch/` file: - -```bash -ls .codearch/ -grep -ril "" .codearch/ -``` - -**Match logic:** - -| Situation | Action | -| ---------------------------------- | --------------------------- | -| File exists and topic matches | Append summary to that file | -| File exists but topic is a stretch | Create a new file | -| No file matches | Create a new file | - -### Step 3: Write or update the file - -**Appending to an existing file:** - -```bash -cat >> .codearch/.md << 'EOF' - ---- -## Session: - - -EOF -``` - -**Creating a new file:** - -Name it after the topic, not the date. Good names: `auth.md`, `database-schema.md`, `api-design.md`, `deployment.md`. Bad names: `session-2024-01-15.md`, `notes.md`, `misc.md`. - -```bash -cat > .codearch/.md << 'EOF' -# - -## Session: - - -EOF -``` - -### Step 4: Create the folder if it doesn't exist - -```bash -mkdir -p .codearch -``` - -Do this before writing if the folder is missing. - ---- - -## File Naming Rules - -- One file per logical domain (`auth`, `billing`, `database`, `api`, `deployment`, `testing`) -- Use kebab-case for multi-word topics: `rate-limiting.md`, `background-jobs.md` -- Never use dates as file names -- Never use generic names: `notes`, `misc`, `temp`, `context` -- If a file grows beyond ~300 lines, split it by subtopic - ---- - -## What Makes a Good `.codearch` Entry - -**Include:** - -- Decisions and their rationale -- Non-obvious constraints (env vars, external dependencies, config quirks) -- What was tried and rejected -- Known bugs or tech debt left behind -- Key file paths and what they do - -**Exclude:** - -- Code snippets (link to files instead) -- Step-by-step logs of what you typed -- Anything that's obvious from reading the code - ---- - -## Example `.codearch/` Structure - -``` -.codearch/ -├── auth.md # JWT strategy, refresh token design, middleware notes -├── database-schema.md # Table decisions, migration gotchas, index rationale -├── api-design.md # REST conventions, versioning decisions, error formats -├── deployment.md # Docker setup, env var requirements, CI notes -└── background-jobs.md # Queue design, retry logic, failure handling -``` - ---- - -## Quick Reference - -| When | What to do | -| ---------------------- | ------------------------------------------------------------ | -| Start of task | `ls .codearch/` → grep for keywords → read matches → proceed | -| End of task | Write summary → find matching file → append or create | -| Folder missing | `mkdir -p .codearch` then proceed | -| No relevant file found | Skip loading; create one after the run | -| Ambiguous match | Read both files; create a new one if neither fits well | diff --git a/.claude/skills/code-context/SKILL.md b/.claude/skills/code-context/SKILL.md new file mode 100644 index 00000000..d9337c19 --- /dev/null +++ b/.claude/skills/code-context/SKILL.md @@ -0,0 +1,182 @@ +--- +name: code-context +description: Persistent code architecture documentation via a `.codecontext/` folder. +user-invokable: false +metadata: + category: architecture +--- + +# Code Architecture Documentation Skill + +Use this skill to **read architecture docs before work** and **document architecture after work** using the `.codecontext/` folder. + +`.codecontext/` is a living architecture reference — it helps new developers onboard and coding agents pick up where previous sessions left off. It is **NOT** a session log, changelog, or task diary. + +--- + +## What Belongs in `.codecontext/` + +Only document **architecture-level knowledge** that would take significant effort to rediscover by reading code alone. + +### Include + +- **Code architecture** — how modules/components are structured, layered, and why +- **Code flow** — request lifecycle, data flow between layers, event/cron pipelines +- **Component relationships** — which modules depend on each other, call chains, shared state +- **Edge cases and gotchas** — non-obvious behaviors, race conditions, ordering constraints +- **Design decisions and rationale** — why a pattern was chosen over alternatives +- **Integration points** — how external services, databases, queues connect +- **Invariants and constraints** — rules that must hold (e.g., "timestamps are always UTC seconds", "all DB access goes through db singleton") +- **Error handling patterns** — how errors propagate, retry logic, fallback behavior +- **Key file map** — which files own which responsibilities (only when non-obvious) + +### Exclude + +- Session logs, changelogs, or diary-style entries +- What files were changed in a specific task +- Raw terminal output or build logs +- Code snippets (reference file paths + line ranges instead) +- Obvious facts that can be inferred from reading one file +- Task status, TODO lists, or progress tracking +- Anything already covered in README, AGENTS.md, or inline comments + +--- + +## Trigger Conditions + +Run this skill at the **start and end** of any coding task that touches architecture: + +- Feature implementations spanning multiple files/modules +- Refactors that change module boundaries or data flow +- Bug fixes that reveal non-obvious system behavior +- New integrations or service connections +- Discovery of undocumented edge cases or invariants + +**Skip** for trivial changes (typo fixes, single-line edits, style-only changes). + +--- + +## Phase A — Read Architecture Docs (Before Acting) + +### A1) Discover docs + +```bash +ls .codecontext/ +``` + +If `.codecontext/` does not exist, continue the task and create it in Phase B. + +### A2) Find relevant docs + +```bash +grep -ril "" .codecontext/ +``` + +Use keywords from the feature area you are working on (e.g., "alerting", "auth", "monitors", "cron"). + +### A3) Read and apply + +Read only relevant files. Extract: + +- Architecture constraints that affect your implementation +- Code flow you need to hook into or extend +- Edge cases to preserve or handle +- Integration points to respect + +If existing docs conflict with current code, trust the code — update docs in Phase B. + +--- + +## Phase B — Document Architecture (Before Ending) + +Only write/update docs if the task revealed architecture knowledge worth preserving. + +### B1) Decide what to document + +Ask: *"Would a new developer or future agent need to re-discover this to work in this area?"* + +If yes, document it. If no, skip Phase B entirely. + +### B2) Write architecture documentation + +Structure each doc as a **reference document**, not a session diary. + +Template (use only the sections that apply): + +```markdown +# + +## Overview +Brief description of what this area does and its role in the system. + +## Architecture +How the components are structured, key abstractions, layers. + +## Code Flow +Step-by-step flow for the primary operations (e.g., "How a monitor check executes"). + +## Key Files +| File | Responsibility | +|------|---------------| +| `src/lib/server/...` | Does X | + +## Edge Cases and Gotchas +- Non-obvious behavior 1 +- Constraint that must be preserved + +## Design Decisions +- Why X was chosen over Y (if non-obvious) +``` + +Not all sections are required — include only what is relevant. Keep each doc under **300 lines**. + +### B3) Pick target file + +```bash +ls .codecontext/ +grep -ril "" .codecontext/ +``` + +| Condition | Action | +| ---------------------------------- | ------------------------------------- | +| Existing doc covers this domain | Update/rewrite relevant sections | +| Different domain | Create new file | +| No match | Create new file | + +When updating, **replace outdated sections** rather than appending session entries. The doc should always read as a clean, current architecture reference. + +### B4) Persist + +```bash +mkdir -p .codecontext +``` + +Create or overwrite the file so it reads as a standalone reference: + +```bash +cat > .codecontext/.md +``` + +--- + +## Naming Rules + +- Name by domain/feature area: `alerting.md`, `auth.md`, `monitor-execution.md`, `incident-lifecycle.md` +- Use kebab-case for multi-word topics +- Never use generic names: `notes.md`, `misc.md`, `context.md`, `session-1.md` +- One file per bounded domain — split if a file exceeds ~300 lines + +--- + +## Fast Checklist + +Before coding: + +- [ ] Checked `.codecontext/` for relevant architecture docs +- [ ] Applied constraints and patterns from existing docs + +Before finishing: + +- [ ] Documented any new architecture knowledge discovered +- [ ] Updated outdated docs if current code contradicts them +- [ ] Doc reads as a clean architecture reference, not a session log diff --git a/.codearch/localization.md b/.codearch/localization.md deleted file mode 100644 index 274a40f7..00000000 --- a/.codearch/localization.md +++ /dev/null @@ -1,9 +0,0 @@ -# Localization - -## Session: locale-key refresh (2026-02-24) - -Task: Updated 18 locale files under `src/lib/locales` for 8 specific UI keys. -Changes: Replaced English placeholders with localized values in `de.json`, `dk.json`, `es.json`, `fa.json`, `fr.json`, `hi.json`, `it.json`, `ja.json`, `ko.json`, `nb-NO.json`, `nl.json`, `pl.json`, `pt-BR.json`, `ru.json`, `sk.json`, `tr.json`, `vi.json`, and `zh-CN.json`. -Decisions: Kept formatting and structure intact (2-space JSON indentation, only target keys edited). Adjusted French `Notifications` to `Alertes` to satisfy non-English-equality validation. -Validation: Ran a script checking the 8 target keys; final result reports no values equal to the original English keys. -Gotcha: Shared terminal got stuck in heredoc/REPL during checks; recovered and reran validation with a robust one-liner. diff --git a/.codearch/versioning.md b/.codearch/versioning.md deleted file mode 100644 index 9e771a3a..00000000 --- a/.codearch/versioning.md +++ /dev/null @@ -1,10 +0,0 @@ -# Versioning - -## Session: startup banner version source - -Task: Clarify runtime version source for startup figlet/banner and align with project architecture. -Changes: Reverted `src/lib/server/startup.ts` to use shared `version()` from `src/lib/version.ts` instead of reading `package.json` directly with fs path logic. -Decision: Keep `src/lib/version.ts` as the single source for version resolution across server/client contexts. This respects code architecture and avoids duplicate version loading paths. -Gotcha: If version appears stale at runtime, ensure the app is rebuilt/restarted after bumping `package.json` so injected/runtime values update. -Files touched: `src/lib/server/startup.ts`. -Verification: `npm run check` passed with 0 errors (existing unrelated warnings only). diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 8f6f79bc..ba0d7e92 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -119,4 +119,22 @@ Always use `import type { ... }` when importing types to avoid accidental runtim # Other skills -Read files in .claude/skills for more instructions on specific tasks or file types. \ No newline at end of file +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/AGENTS.md b/AGENTS.md index 57c58bd7..c864a568 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -30,8 +30,22 @@ When the user asks to write or edit documentation, follow the skill file: This is mandatory for docs-related tasks. Prioritize short, clear, action-oriented docs and avoid bloat. -## Code architecture skill - Important for all coding tasks +## Code architecture docs skill - Important for all tasks -Always try to use the code architecture skill at the start and end of coding sessions: +Always try to use the code-context skill at the start and end of coding sessions: -- `.claude/skills/code-architecture/SKILL.md` +- `.claude/skills/code-context/SKILL.md` + +## Code architecture enforcement (mandatory) + +The code-context skill is not optional. Agents MUST do both: + +1. **Before coding**: load relevant architecture docs from `.codecontext/`. +2. **Before final response**: if the task revealed new architecture knowledge (code flow, edge cases, component relationships), update or create a `.codecontext/*.md` entry. Skip if the task was trivial. + +Required output evidence in the final response: + +- `Context loaded:` list of `.codecontext` files read (or `none found`). +- `Context updated:` exact `.codecontext` file path written (or `skipped — no architecture changes`). + +The `.codecontext/` folder documents **code architecture only** — not session logs, changelogs, or task summaries. diff --git a/migrations/20260109120000_add_maintenances_tables.ts b/migrations/20260109120000_add_maintenances_tables.ts index 75e24429..a0d4b309 100644 --- a/migrations/20260109120000_add_maintenances_tables.ts +++ b/migrations/20260109120000_add_maintenances_tables.ts @@ -10,6 +10,7 @@ export async function up(knex: Knex): Promise { table.string("rrule", 500).notNullable(); table.integer("duration_seconds").notNullable(); table.string("status", 50).notNullable().defaultTo("ACTIVE"); + table.string("is_global", 15).notNullable().defaultTo("YES"); table.timestamp("created_at").defaultTo(knex.fn.now()); table.timestamp("updated_at").defaultTo(knex.fn.now()); }); @@ -20,6 +21,7 @@ export async function up(knex: Knex): Promise { table.increments("id").primary(); table.integer("maintenance_id").unsigned().notNullable(); table.string("monitor_tag", 255).notNullable(); + table.string("monitor_impact").defaultTo("MAINTENANCE").notNullable(); table.timestamp("created_at").defaultTo(knex.fn.now()); table.timestamp("updated_at").defaultTo(knex.fn.now()); diff --git a/migrations/20260123110239_maintenace_monitor_status.ts b/migrations/20260123110239_maintenace_monitor_status.ts index 62cc45cb..b155536a 100644 --- a/migrations/20260123110239_maintenace_monitor_status.ts +++ b/migrations/20260123110239_maintenace_monitor_status.ts @@ -1,6 +1,7 @@ import type { Knex } from "knex"; export async function up(knex: Knex): Promise { + if (!(await knex.schema.hasTable("maintenance_monitors"))) return; const hasCol = await knex.schema.hasColumn("maintenance_monitors", "monitor_impact"); if (!hasCol) { await knex.schema.alterTable("maintenance_monitors", (table) => { @@ -10,6 +11,7 @@ export async function up(knex: Knex): Promise { } export async function down(knex: Knex): Promise { + if (!(await knex.schema.hasTable("maintenance_monitors"))) return; await knex.schema.alterTable("maintenance_monitors", (table) => { table.dropColumn("monitor_impact"); }); diff --git a/migrations/20260128120000_redesign_subscription_system.ts b/migrations/20260128120000_redesign_subscription_system.ts index 844aa6af..841346bf 100644 --- a/migrations/20260128120000_redesign_subscription_system.ts +++ b/migrations/20260128120000_redesign_subscription_system.ts @@ -27,12 +27,46 @@ export async function up(knex: Knex): Promise { table.text("meta").nullable(); table.timestamp("created_at").defaultTo(knex.fn.now()); table.timestamp("updated_at").defaultTo(knex.fn.now()); + }); + } + + // Add indexes, unique constraints, and foreign keys for subscriber_methods (idempotent) + try { + await knex.schema.alterTable("subscriber_methods", (table) => { table.index(["subscriber_user_id"]); + }); + } catch (_e) { + /* index already exists */ + } + try { + await knex.schema.alterTable("subscriber_methods", (table) => { table.index(["method_type"]); + }); + } catch (_e) { + /* index already exists */ + } + try { + await knex.schema.alterTable("subscriber_methods", (table) => { table.index(["status"]); - table.unique(["subscriber_user_id", "method_type", "method_value"]); + }); + } catch (_e) { + /* index already exists */ + } + try { + await knex.schema.alterTable("subscriber_methods", (table) => { + table.unique(["subscriber_user_id", "method_type", "method_value"], { + indexName: "sub_methods_user_type_value_unique", + }); + }); + } catch (_e) { + /* unique constraint already exists */ + } + try { + await knex.schema.alterTable("subscriber_methods", (table) => { table.foreign("subscriber_user_id").references("id").inTable("subscriber_users").onDelete("CASCADE"); }); + } catch (_e) { + /* foreign key already exists */ } // 3. Create user_subscriptions_v2 table @@ -45,14 +79,60 @@ export async function up(knex: Knex): Promise { table.string("status", 20).notNullable().defaultTo("ACTIVE"); table.timestamp("created_at").defaultTo(knex.fn.now()); table.timestamp("updated_at").defaultTo(knex.fn.now()); + }); + } + + // Add indexes, unique constraints, and foreign keys for user_subscriptions_v2 (idempotent) + try { + await knex.schema.alterTable("user_subscriptions_v2", (table) => { table.index(["subscriber_user_id"]); + }); + } catch (_e) { + /* index already exists */ + } + try { + await knex.schema.alterTable("user_subscriptions_v2", (table) => { table.index(["subscriber_method_id"]); + }); + } catch (_e) { + /* index already exists */ + } + try { + await knex.schema.alterTable("user_subscriptions_v2", (table) => { table.index(["event_type"]); + }); + } catch (_e) { + /* index already exists */ + } + try { + await knex.schema.alterTable("user_subscriptions_v2", (table) => { table.index(["status"]); - table.unique(["subscriber_user_id", "subscriber_method_id", "event_type"]); + }); + } catch (_e) { + /* index already exists */ + } + try { + await knex.schema.alterTable("user_subscriptions_v2", (table) => { + table.unique(["subscriber_user_id", "subscriber_method_id", "event_type"], { + indexName: "sub_v2_user_method_event_unique", + }); + }); + } catch (_e) { + /* unique constraint already exists */ + } + try { + await knex.schema.alterTable("user_subscriptions_v2", (table) => { table.foreign("subscriber_user_id").references("id").inTable("subscriber_users").onDelete("CASCADE"); + }); + } catch (_e) { + /* foreign key already exists */ + } + try { + await knex.schema.alterTable("user_subscriptions_v2", (table) => { table.foreign("subscriber_method_id").references("id").inTable("subscriber_methods").onDelete("CASCADE"); }); + } catch (_e) { + /* foreign key already exists */ } } diff --git a/migrations/20260219165446_add_global_colum_incidents.ts b/migrations/20260219165446_add_global_colum_incidents.ts index 46f2ffe1..1398cedd 100644 --- a/migrations/20260219165446_add_global_colum_incidents.ts +++ b/migrations/20260219165446_add_global_colum_incidents.ts @@ -6,7 +6,7 @@ export async function up(knex: Knex): Promise { table.string("is_global", 15).notNullable().defaultTo("YES"); }); } - if (!(await knex.schema.hasColumn("maintenances", "is_global"))) { + if ((await knex.schema.hasTable("maintenances")) && !(await knex.schema.hasColumn("maintenances", "is_global"))) { await knex.schema.table("maintenances", (table) => { table.string("is_global", 15).notNullable().defaultTo("YES"); }); @@ -17,7 +17,9 @@ export async function down(knex: Knex): Promise { await knex.schema.table("incidents", (table) => { table.dropColumn("is_global"); }); - await knex.schema.table("maintenances", (table) => { - table.dropColumn("is_global"); - }); + if (await knex.schema.hasTable("maintenances")) { + await knex.schema.table("maintenances", (table) => { + table.dropColumn("is_global"); + }); + } } diff --git a/scripts/index-docs.ts b/scripts/index-docs.ts index 6dc68439..dd0e14f6 100644 --- a/scripts/index-docs.ts +++ b/scripts/index-docs.ts @@ -47,7 +47,8 @@ interface DocsSidebarGroup { interface DocsNavTab { name: string; - sidebar: DocsSidebarGroup[]; + url?: string; + sidebar?: DocsSidebarGroup[]; } interface DocsVersion { @@ -164,14 +165,16 @@ async function main(): Promise { process.exit(1); } - const primaryTabSidebar = latestVersion.content.navigation?.tabs?.[0]?.sidebar ?? []; - const sidebar = normalizeSidebar(primaryTabSidebar); + const tabs = latestVersion.content.navigation?.tabs ?? []; const documents: DocsSearchDocument[] = []; - // Collect all pages from sidebar + // Collect all pages from all tabs' sidebars const allPages: Array<{ page: DocsPageSource; group: string }> = []; - for (const sidebarGroup of sidebar) { - collectPages(sidebarGroup.pages, sidebarGroup.group, allPages); + for (const tab of tabs) { + const sidebar = normalizeSidebar(tab.sidebar ?? []); + for (const sidebarGroup of sidebar) { + collectPages(sidebarGroup.pages, sidebarGroup.group, allPages); + } } console.log(`[index-docs] Indexing version ${latestVersion.slug}`); diff --git a/src/lib/components/KenerNav.svelte b/src/lib/components/KenerNav.svelte index e562f325..cd1b92a9 100644 --- a/src/lib/components/KenerNav.svelte +++ b/src/lib/components/KenerNav.svelte @@ -11,7 +11,15 @@ let { data } = page; const navItems: { name: string; url: string; iconURL: string }[] = data.navItems || []; - const { siteName, siteUrl, logo } = data; + const { siteName, logo, globalPageVisibilitySettings } = data; + + const brandPath = $derived.by(() => { + if (globalPageVisibilitySettings?.forceExclusivity) { + const currentPagePath = page.params?.page_path?.trim(); + return currentPagePath ? `/${currentPagePath}` : "/"; + } + return "/"; + }); function trackBrandClick() { trackEvent("nav_brand_clicked", { name: siteName }); @@ -29,7 +37,7 @@ > import { resolve } from "$app/paths"; + import { page } from "$app/state"; import { Button } from "$lib/components/ui/button/index.js"; import * as Popover from "$lib/components/ui/popover/index.js"; import { Spinner } from "$lib/components/ui/spinner/index.js"; @@ -9,6 +10,7 @@ import { t } from "$lib/stores/i18n"; import type { NotificationEvent } from "$lib/server/controllers/dashboardController.js"; import Calendar from "@lucide/svelte/icons/calendar-1"; + import { format } from "date-fns"; import { onMount } from "svelte"; interface Props { @@ -22,6 +24,17 @@ let notifications = $state([]); let loading = $state(false); + const defaultEventsPath = $derived(`/events/${format(new Date(), "MMMM-yyyy")}`); + + const resolvedEventsPath = $derived.by(() => { + const finalEventsPath = eventsPath || defaultEventsPath; + if (page.data?.globalPageVisibilitySettings?.forceExclusivity) { + const currentPagePath = page.params?.page_path?.trim(); + return currentPagePath ? `/${currentPagePath}${finalEventsPath}` : finalEventsPath; + } + return finalEventsPath; + }); + async function fetchNotifications() { loading = true; try { @@ -76,7 +89,7 @@ >

{$t("Events")}

-
diff --git a/src/lib/components/ThemePlus.svelte b/src/lib/components/ThemePlus.svelte index eb286d55..703cc9a8 100644 --- a/src/lib/components/ThemePlus.svelte +++ b/src/lib/components/ThemePlus.svelte @@ -10,7 +10,6 @@ import Sun from "@lucide/svelte/icons/sun"; import Moon from "@lucide/svelte/icons/moon"; import Share from "@lucide/svelte/icons/share-2"; - import ChevronLeft from "@lucide/svelte/icons/chevron-left"; import { format } from "date-fns"; import SubscribeMenu from "$lib/components/SubscribeMenu.svelte"; import CopyButton from "$lib/components/CopyButton.svelte"; @@ -79,7 +78,9 @@
- + {#if !!!page.data.globalPageVisibilitySettings.forceExclusivity && page.data.globalPageVisibilitySettings.showSwitcher} + + {/if}
{#if page.data.isSubsEnabled && page.data.canSendEmail}