diff --git a/package-lock.json b/package-lock.json index 5e434bf..e089863 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.0", "devDependencies": { "@eslint/js": "10.0.1", + "@types/node": "26.1.1", "eslint": "10.7.0", "eslint-plugin-vue": "10.10.0", "feed": "6.0.0", @@ -19,6 +20,9 @@ "typescript-eslint": "8.64.0", "vitepress": "2.0.0-alpha.18", "vue-eslint-parser": "10.4.1" + }, + "engines": { + "node": "^22.13.0 || >=24" } }, "node_modules/@babel/helper-string-parser": { @@ -836,6 +840,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/node": { + "version": "26.1.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.1.1.tgz", + "integrity": "sha512-nxAkRSVkN1Y0JC1W8ky/fTfkGsMmcrRsbx+3XoZE+rMOX71kLYTV7fLXpqud1GpbpP5TuffXFqfX7fH2GgZREw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~8.3.0" + } + }, "node_modules/@types/unist": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", @@ -2831,9 +2845,9 @@ } }, "node_modules/postcss": { - "version": "8.5.20", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.20.tgz", - "integrity": "sha512-lW616l85ucIQL+FocMmL7pQFPqBmwejrCMg+iPxyImlrANNJG9NHq/RkyCZopDhd8C3LA03PHRJDjkbGu8vvug==", + "version": "8.5.21", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.21.tgz", + "integrity": "sha512-v4sDNP3fdNiWMfabO7OwOQdOX8TiQSztKyT1Wj0w+j7LDallJThJRBBBmzVGyYj0crMh7jlV4zepPkiNu9UwDQ==", "dev": true, "funding": [ { @@ -3205,6 +3219,13 @@ "typescript": ">=4.8.4 <6.1.0" } }, + "node_modules/undici-types": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz", + "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==", + "dev": true, + "license": "MIT" + }, "node_modules/unist-util-is": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", diff --git a/package.json b/package.json index 2fb9438..1387131 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ }, "devDependencies": { "@eslint/js": "10.0.1", + "@types/node": "26.1.1", "eslint": "10.7.0", "eslint-plugin-vue": "10.10.0", "feed": "6.0.0", diff --git a/site/.vitepress/config.ts b/site/.vitepress/config.ts index 2edbca5..1ef645d 100644 --- a/site/.vitepress/config.ts +++ b/site/.vitepress/config.ts @@ -1,13 +1,22 @@ import { defineConfig } from 'vitepress'; import { genFeed } from './genFeed.js'; +const SITE_HOSTNAME = process.env.SITE_HOSTNAME || 'https://wrensecurity.org'; +const TWITTER_HANDLE = process.env.TWITTER_HANDLE || '@WrenSecurity'; +const GITHUB_URL = process.env.GITHUB_URL || 'https://github.com/WrenSecurity'; +const TWITTER_URL = process.env.TWITTER_URL || 'https://twitter.com/WrenSecurity'; + export default defineConfig({ base: '/', title: 'Wren Security', description: 'The Wren Security Suite.', + sitemap: { + hostname: SITE_HOSTNAME, + }, head: [ ['meta', { name: 'viewport', content: 'width=device-width, initial-scale=1' } ], ['meta', { name: 'theme-color', content: '#c12233' } ], + ['link', { rel: 'alternate', type: 'application/rss+xml', title: 'Wren Security Blog', href: '/feed.rss' }], [ 'script', { src: 'https://cdn.usefathom.com/script.js', @@ -119,8 +128,8 @@ export default defineConfig({ ], }, socialLinks: [ - { icon: 'github', link: 'https://github.com/WrenSecurity' }, - { icon: 'x', link: 'https://twitter.com/WrenSecurity' }, + { icon: 'github', link: GITHUB_URL }, + { icon: 'x', link: TWITTER_URL }, { icon: { svg: '' }, link: 'https://gitter.im/WrenSecurity/Lobby' }, ], }, @@ -128,5 +137,24 @@ export default defineConfig({ // Ignore all localhost links /^https?:\/\/localhost/, ], + transformHead({ description, page, pageData, siteData }) { + const image = pageData.frontmatter.image || '/wrensec-logo.png'; + const htmlPath = page.replace(/\.md$/, '.html'); + // The deployed site only serves clean (extensionless) URLs for index pages; other pages keep the .html extension. + const cleanPath = htmlPath === 'index.html' ? '' : htmlPath.replace(/index\.html$/, ''); + const url = `${SITE_HOSTNAME}/${cleanPath}`; + + return [ + ['link', { rel: 'canonical', href: url }], + ['meta', { property: 'og:type', content: 'website' }], + ['meta', { property: 'og:url', content: url }], + ['meta', { property: 'og:site_name', content: siteData.title }], + ['meta', { property: 'og:title', content: pageData.title }], + ['meta', { property: 'og:description', content: description }], + ['meta', { property: 'og:image', content: `${SITE_HOSTNAME}${image}` }], + ['meta', { name: 'twitter:card', content: 'summary_large_image' }], + ['meta', { name: 'twitter:site', content: TWITTER_HANDLE }], + ]; + }, buildEnd: genFeed }); diff --git a/site/.vitepress/genFeed.ts b/site/.vitepress/genFeed.ts index 46f04d9..8f29d24 100644 --- a/site/.vitepress/genFeed.ts +++ b/site/.vitepress/genFeed.ts @@ -3,7 +3,7 @@ import { writeFileSync } from 'fs'; import { Feed } from 'feed'; import { createContentLoader, type SiteConfig } from 'vitepress'; -const baseUrl = `https://wrensecurity.org`; +const baseUrl = process.env.SITE_HOSTNAME || 'https://wrensecurity.org'; export async function genFeed(config: SiteConfig) { const feed = new Feed({ @@ -12,7 +12,7 @@ export async function genFeed(config: SiteConfig) { id: baseUrl, link: baseUrl, language: 'en', - image: 'https://wrensecurity.org/wrensec-logo.png', + image: `${baseUrl}/wrensec-logo.png`, favicon: `${baseUrl}/favicon.ico`, copyright: 'Copyright © 2017-present Wren Security' @@ -39,7 +39,7 @@ export async function genFeed(config: SiteConfig) { author: [ { name: 'Wren Security', - link: 'https://wrensecurity.org' + link: baseUrl } ], date: frontmatter.date diff --git a/site/index.md b/site/index.md index 3ca92fd..60d7560 100644 --- a/site/index.md +++ b/site/index.md @@ -1,6 +1,8 @@ --- layout: home -title: 'Home' +title: 'Wren Security | Open-Source Identity & Access Management Platform' +titleTemplate: false +description: 'An open-source, enterprise-ready IAM platform designed for long-term reliability. Modular, secure by design, and built on open standards.' hero: name: Identity & Access Management text: Open-Source Platform diff --git a/site/public/robots.txt b/site/public/robots.txt new file mode 100644 index 0000000..3ecedfb --- /dev/null +++ b/site/public/robots.txt @@ -0,0 +1,4 @@ +User-agent: * +Allow: / + +Sitemap: https://wrensecurity.org/sitemap.xml diff --git a/tsconfig.json b/tsconfig.json index 0e0dc2c..d23981f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,7 +9,8 @@ "allowJs": true, "strict": true, "jsx": "preserve", - "baseUrl": "." + "baseUrl": ".", + "types": ["node"] }, "include": ["shims-vue.d.ts", "site/.vitepress/**/*"] }