diff --git a/site/astro.config.mjs b/site/astro.config.mjs index beefe60..691e905 100644 --- a/site/astro.config.mjs +++ b/site/astro.config.mjs @@ -4,7 +4,10 @@ import sitemap from '@astrojs/sitemap'; import icon from 'astro-icon'; import { parse as parseYaml } from 'yaml'; import { readFileSync } from 'fs'; -import { resolve } from 'path'; +import { resolve, dirname } from 'path'; +import { fileURLToPath } from 'url'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); /** Custom Vite plugin that parses YAML files using the 'yaml' package, * which tolerates duplicate keys (last one wins) unlike js-yaml 4.x. */ @@ -23,13 +26,72 @@ function yamlPlugin() { }; } +const toSlug = (name) => + name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, ''); + +/** Build redirect map: old flat /rules/{service} paths → new /rules/{group}/{service}/ paths */ +function buildRedirects(base) { + try { + const rulesPath = resolve(__dirname, '../_data/rules.yml'); + const raw = readFileSync(rulesPath, 'utf-8'); + const { groups } = parseYaml(raw, { merge: true, strict: false, uniqueKeys: false }); + const redirects = {}; + for (const group of groups) { + const groupSlug = toSlug(group.name); + for (const service of group.services) { + const serviceSlug = toSlug(service.name); + // Old anchor slug (spaces → hyphens only, no other substitutions) + const oldSlug = service.name.replace(/ /g, '-').toLowerCase(); + const newPath = `${base}/rules/${groupSlug}/${serviceSlug}/`; + // Redirect from flat old path (with and without trailing slash) + for (const oldPath of [`${base}/rules/${oldSlug}`, `${base}/rules/${oldSlug}/`]) { + if (oldPath !== newPath && oldPath !== newPath.slice(0, -1)) { + redirects[oldPath] = { destination: newPath, status: 301 }; + } + } + } + } + return redirects; + } catch { + return {}; + } +} + +const base = '/awesome-prometheus-alerts'; + export default defineConfig({ site: 'https://samber.github.io', - base: '/awesome-prometheus-alerts', + base, + redirects: buildRedirects(base), output: 'static', integrations: [ tailwind({ applyBaseStyles: false }), - sitemap(), + sitemap({ + serialize(item) { + const path = new URL(item.url).pathname; + const segments = path.replace(/^\/|\/$/g, '').split('/').filter(Boolean); + // segments[0] = 'awesome-prometheus-alerts', [1] = 'rules'|guide, [2] = group, [3] = service + + if (segments.length <= 1) { + // Homepage + return { ...item, changefreq: 'weekly', priority: 1.0, lastmod: new Date() }; + } + if (segments.length === 2 && segments[1] === 'rules') { + // /rules/ index + return { ...item, changefreq: 'weekly', priority: 0.9, lastmod: new Date() }; + } + if (segments.length === 3 && segments[1] === 'rules') { + // /rules/[group]/ index + return { ...item, changefreq: 'monthly', priority: 0.7, lastmod: new Date() }; + } + if (segments.length === 4 && segments[1] === 'rules') { + // /rules/[group]/[service]/ — main content pages + return { ...item, changefreq: 'monthly', priority: 0.8, lastmod: new Date() }; + } + // Guide pages and others + return { ...item, changefreq: 'yearly', priority: 0.6, lastmod: new Date() }; + }, + }), icon(), ], vite: { diff --git a/site/public/favicon.ico b/site/public/favicon.ico deleted file mode 100644 index bf23ccf..0000000 Binary files a/site/public/favicon.ico and /dev/null differ diff --git a/site/public/favicon.svg b/site/public/favicon.svg new file mode 100644 index 0000000..0470226 --- /dev/null +++ b/site/public/favicon.svg @@ -0,0 +1,22 @@ + diff --git a/site/public/manifest.json b/site/public/manifest.json new file mode 100644 index 0000000..aa4da0d --- /dev/null +++ b/site/public/manifest.json @@ -0,0 +1,19 @@ +{ + "name": "Awesome Prometheus Alerts", + "short_name": "Prom Alerts", + "description": "Collection of copy-pasteable Prometheus alerting rules for 90+ services.", + "start_url": "/awesome-prometheus-alerts/", + "scope": "/awesome-prometheus-alerts/", + "display": "browser", + "background_color": "#0f172a", + "theme_color": "#E6522C", + "lang": "en", + "icons": [ + { + "src": "/awesome-prometheus-alerts/favicon.svg", + "type": "image/svg+xml", + "sizes": "any", + "purpose": "any maskable" + } + ] +} diff --git a/site/public/robots.txt b/site/public/robots.txt index f3776b3..eff0940 100644 --- a/site/public/robots.txt +++ b/site/public/robots.txt @@ -1,4 +1,28 @@ User-agent: * Allow: / +# AI search bots — explicitly allowed for citation +User-agent: GPTBot +Allow: / + +User-agent: ChatGPT-User +Allow: / + +User-agent: PerplexityBot +Allow: / + +User-agent: ClaudeBot +Allow: / + +User-agent: anthropic-ai +Allow: / + +User-agent: Google-Extended +Allow: / + +User-agent: Bingbot +Allow: / + Sitemap: https://samber.github.io/awesome-prometheus-alerts/sitemap-index.xml +LLMs: https://samber.github.io/awesome-prometheus-alerts/llms.txt +LLMs-full: https://samber.github.io/awesome-prometheus-alerts/llms-full.txt diff --git a/site/src/components/Breadcrumbs.astro b/site/src/components/Breadcrumbs.astro index 0ddf1ad..e1f57f5 100644 --- a/site/src/components/Breadcrumbs.astro +++ b/site/src/components/Breadcrumbs.astro @@ -9,8 +9,9 @@ interface Props { base: string; } +import { SITE_ORIGIN } from '../data/site'; + const { items, base } = Astro.props; -const siteUrl = 'https://samber.github.io'; const allItems = [{ label: 'Home', href: `${base}/` }, ...items]; @@ -21,7 +22,7 @@ const jsonLd = { '@type': 'ListItem', position: i + 1, name: item.label, - ...(item.href ? { item: `${siteUrl}${item.href}` } : {}), + ...(item.href ? { item: `${SITE_ORIGIN}${item.href}` } : {}), })), }; --- diff --git a/site/src/components/CautionBanner.astro b/site/src/components/CautionBanner.astro new file mode 100644 index 0000000..29ff95d --- /dev/null +++ b/site/src/components/CautionBanner.astro @@ -0,0 +1,12 @@ +--- +--- +
+ Alert thresholds depend on the nature of your applications. + Some queries may have arbitrary tolerance thresholds. + Building an efficient monitoring platform takes time. 😉 +
+{allRulesYaml}
diff --git a/site/src/components/Footer.astro b/site/src/components/Footer.astro
index e49c553..80bf8bd 100644
--- a/site/src/components/Footer.astro
+++ b/site/src/components/Footer.astro
@@ -1,33 +1,45 @@
---
+import { sponsors } from '../data/sponsors';
+import { getPopularServices, data, getGroupSlug } from '../data/rules';
+import { SITE_NAME, SITE_URL, GITHUB_URL, GITHUB_CONTRIBUTING_URL, GITHUB_LICENSE_URL, AUTHOR_NAME, AUTHOR_GITHUB_URL, TWITTER_HANDLE, LICENSE_CC_BY_NAME } from '../data/site';
+
interface Props {
base: string;
}
const { base } = Astro.props;
+
+const popularServices = getPopularServices();
+
+const featuredGroupSlugs = [
+ 'basic-resource-monitoring',
+ 'databases',
+ 'orchestrators',
+ 'network-and-security',
+];
+const featuredGroups = data.groups.filter((g) => featuredGroupSlugs.includes(getGroupSlug(g)));
---
{item.acceptedAnswer.text}
++
{ruleCount} Prometheus alerting rule{ruleCount !== 1 ? 's' : ''} for {service.name}. {exporterNames && `Exported via ${exporterNames}.`} These rules cover critical and warning conditions — copy and paste the YAML into your Prometheus configuration. @@ -131,6 +155,27 @@ const jsonLd = [ ); })()} + + + {group.services.length > 1 && ( +