Files
mattermost/docs/site/docusaurus.config.ts
T
Eva SarafianouandCursor c57df45d9c docs: self-host fonts, images, and OpenAPI icons; block build egress (#39092)
* docs: self-host fonts, images, and OpenAPI icons; block build egress

Stop hotlinking GitHub/user-images and Google Fonts on the docs site, replace
embedded video iframes with links, and fail CI when a build would fetch
off-origin resources (closed allowlist for the security-updates feed).

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: address CodeRabbit egress review findings

Scan an offline (no-Algolia) rebuild in Docs CI, tighten ATTR/getAttr
boundaries, flag remote template-literal prefixes before ${, and add
alt text for the self-hosted plugin-management image.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: replace egress fixture tree with in-memory unit tests

Keep scanner coverage without committing dozens of tiny HTML/CSS/JS
fixture files. scanSources takes string maps; CLI smoke uses a temp dir.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: keep YouTube/Wistia embeds on hosted site

Restore in-page players for the public docs. Allowlist those hosts in
check-egress; the offline packager strips them for airgap.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: simplify hosted-embed allowlist to base domains

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: tighten hosted-embed allowlist to player kinds and paths

Only iframe/script/img player URLs pass; fetch() and other loads to
YouTube/Wistia hosts still fail egress.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: allow Wistia swatch preload in hosted-embed egress check

The organize-conversations embed emits link[rel=preload] for the
swatch; kind-scoped allowlisting missed it and failed Docs CI.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: use getAttr boundary for script hasSrc check

\b matches inside data-src, so the old hasSrc could skip scanning an
inline script body. Align with ATTR/getAttr and cover the case in tests.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-13 17:58:54 +03:00

288 lines
8.9 KiB
TypeScript

import {themes as prismThemes} from 'prism-react-renderer';
import type {Config} from '@docusaurus/types';
import type * as Preset from '@docusaurus/preset-classic';
// Active redirects (legacy Sphinx URLs → migrated MDX paths). Bulk-extracted
// during the docs migration, maintained by hand since — add entries directly
// to the JSON when a page moves or is removed.
import activeRedirects from './sidebars/active-redirects.json';
// Multi-instance docs setup with three top-level navigations:
// / → Documentation (admin / end-user) sources: ../docs
// /developers → Developers (contribute / integrate) sources: ../develop
// /api → API Reference (OpenAPI-generated) sources: ../api
// See PLAN.md §3.1 for the IA, §3.2 for design tokens.
// Docusaurus's Algolia schema rejects an empty `appId`/`apiKey`, so the
// block is only included when both are set. Without credentials the search
// bar is simply omitted (rather than rendered as an inert element), which
// keeps local and CI builds green.
const algoliaAppId = process.env.ALGOLIA_APP_ID;
const algoliaApiKey = process.env.ALGOLIA_SEARCH_API_KEY;
const algoliaThemeConfig =
algoliaAppId && algoliaApiKey
? {
algolia: {
appId: algoliaAppId,
apiKey: algoliaApiKey,
indexName: 'mattermost-docs',
contextualSearch: true,
searchPagePath: 'search',
searchParameters: {
optionalFilters: [
'docusaurus_tag:docs-documentation-current<score=3>',
'docusaurus_tag:docs-developers-current<score=2>',
'docusaurus_tag:docs-api-current<score=1>',
],
},
},
}
: {};
const config: Config = {
title: 'Mattermost Documentation',
tagline: 'Mission in Motion',
favicon: 'img/brand/icon-denim.svg',
future: {
v4: true,
},
url: 'https://docs.mattermost.com',
baseUrl: process.env.BASE_URL ?? '/',
trailingSlash: false,
organizationName: 'mattermost',
projectName: 'mattermost',
// Broken-link / broken-image policy. During the Phase 4 (developer docs)
// and Phase 5 (main docs) migrations, content references images and
// anchors that haven't been migrated yet — `warn` keeps the build green
// while the unconverted set shows up as warnings we can grep for.
// Tighten to `throw` after each migration phase completes.
onBrokenLinks: 'warn',
markdown: {
mermaid: true,
hooks: {
onBrokenMarkdownLinks: 'warn',
onBrokenMarkdownImages: 'warn',
},
},
i18n: {
defaultLocale: 'en',
locales: ['en'],
},
// Fonts are self-hosted from src/css/fonts (see the README there).
// Use the classic preset for the default Documentation instance.
// Developers and API are added as separate plugin instances below
// to give each its own routeBasePath and sidebar.
presets: [
[
'classic',
{
docs: {
id: 'documentation',
path: '../main',
routeBasePath: '/',
sidebarPath: './sidebars/documentation.ts',
editUrl: 'https://github.com/mattermost/mattermost/tree/master/docs/main/',
},
blog: false,
theme: {
customCss: ['./src/css/fonts.css', './src/css/tokens.css', './src/css/custom.css'],
},
} satisfies Preset.Options,
],
],
plugins: [
[
'@docusaurus/plugin-content-docs',
{
id: 'developers',
path: '../develop',
routeBasePath: '/developers',
sidebarPath: './sidebars/developers.ts',
editUrl: 'https://github.com/mattermost/mattermost/tree/master/docs/develop/',
},
],
[
'@docusaurus/plugin-content-docs',
{
id: 'api',
path: '../api',
routeBasePath: '/api',
sidebarPath: './sidebars/api.ts',
editUrl: 'https://github.com/mattermost/mattermost/tree/master/docs/api/',
// Required for endpoint pages generated by docusaurus-plugin-openapi-docs.
docItemComponent: '@theme/ApiItem',
},
],
[
'@docusaurus/plugin-client-redirects',
{
// Legacy URL → migrated MDX path. Pre-filtered to entries whose
// target exists, so the build never breaks on a missing target.
redirects: activeRedirects.redirects,
},
],
// Generates API reference pages from the OpenAPI bundle produced by
// build-openapi.mjs
[
'docusaurus-plugin-openapi-docs',
{
id: 'api-generator',
docsPluginId: 'api',
config: {
mattermost: {
specPath: 'openapi/mattermost-openapi-v4.yaml',
outputDir: '../api/reference',
sidebarOptions: {
groupPathsBy: 'tag',
categoryLinkSource: 'tag',
},
},
},
},
],
],
// Theme for the API endpoint pages (parameter tables, request/response
// schemas, code-sample picker). Layered on the classic preset theme.
themes: ['docusaurus-theme-openapi-docs', '@docusaurus/theme-mermaid'],
themeConfig: {
image: 'img/brand/logo-horizontal-denim.svg',
colorMode: {
defaultMode: 'light',
respectPrefersColorScheme: true,
},
navbar: {
title: '',
logo: {
alt: 'Mattermost',
// Navbar is denim in both light and dark mode, so white logo always.
src: 'img/brand/logo-horizontal-white.svg',
srcDark: 'img/brand/logo-horizontal-white.svg',
},
items: [
{
type: 'docSidebar',
docsPluginId: 'documentation',
sidebarId: 'documentation',
position: 'left',
label: 'Documentation',
},
{
type: 'docSidebar',
docsPluginId: 'developers',
sidebarId: 'developers',
position: 'left',
label: 'Developers',
},
{
type: 'docSidebar',
docsPluginId: 'api',
sidebarId: 'api',
position: 'left',
label: 'API',
},
{
href: 'https://github.com/mattermost/mattermost',
label: 'GitHub',
position: 'right',
},
],
},
footer: {
style: 'dark',
logo: {
alt: 'Mattermost — Mission in Motion',
src: 'img/brand/logo-horizontal-white.svg',
width: 180,
},
links: [
{
title: 'Documentation',
items: [
{label: 'Product Overview', to: '/'},
{label: 'Deployment Guide', to: '/'},
{label: 'Administration Guide', to: '/'},
],
},
{
title: 'Developers',
items: [
{label: 'Contribute', to: '/developers'},
{label: 'Integrate & Extend', to: '/developers'},
{label: 'API Reference', to: '/api'},
],
},
{
title: 'Community',
items: [
{label: 'GitHub', href: 'https://github.com/mattermost/mattermost'},
{label: 'Forum', href: 'https://forum.mattermost.com/'},
],
},
],
copyright: `Copyright © ${new Date().getFullYear()} Mattermost, Inc. All rights reserved.`,
},
...algoliaThemeConfig,
prism: {
theme: prismThemes.github,
darkTheme: prismThemes.dracula,
additionalLanguages: ['bash', 'powershell', 'json', 'yaml', 'go', 'python'],
},
mermaid: {
theme: {light: 'base', dark: 'dark'},
options: {
themeVariables: {
fontSize: '14px',
primaryColor: '#0984e3',
primaryTextColor: '#ffffff',
primaryBorderColor: '#2d3436',
lineColor: '#0745a1',
secondaryColor: '#0984e3',
tertiaryColor: '#d63031',
mainBkg: '#ffffff',
textColor: '#2d3436',
taskBkgColor: '#0984e3',
taskTextColor: '#ffffff',
taskTextLightColor: '#ffffff',
taskTextDarkColor: '#ffffff',
activeTaskBkgColor: '#0984e3',
activeTaskBorderColor: '#2d3436',
doneTaskBkgColor: '#0984e3',
doneTaskBorderColor: '#2d3436',
critBkgColor: '#d63031',
critBorderColor: '#2d3436',
},
gantt: {
fontSize: 14,
sectionFontSize: 14,
barHeight: 28,
barGap: 6,
topPadding: 50,
leftPadding: 75,
gridLineStartPadding: 35,
},
},
},
// Code-sample tabs shown on every endpoint page, in display order.
// First entry is the default selected tab (curl).
languageTabs: [
{highlight: 'bash', language: 'curl', logoClass: 'curl'},
{highlight: 'powershell', language: 'powershell', logoClass: 'powershell'},
{highlight: 'python', language: 'python', logoClass: 'python'},
{highlight: 'javascript', language: 'nodejs', logoClass: 'nodejs', label: 'Node'},
{highlight: 'go', language: 'go', logoClass: 'go'},
],
} satisfies Preset.ThemeConfig,
};
export default config;