-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdocusaurus.config.js
More file actions
383 lines (370 loc) · 21.7 KB
/
Copy pathdocusaurus.config.js
File metadata and controls
383 lines (370 loc) · 21.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
/**
* conduction.nl, the Conduction company hub.
*
* Single Docusaurus build that also serves the connext.conduction.nl and
* commonground.conduction.nl vanity entry points (Cloudflare 301-redirects
* to /connext and /commonground paths, with locale awareness via the
* Accept-Language header). The navbar logo + title swap based on pathname
* (see src/theme/Navbar/Logo).
*
* Deployed to https://www.conduction.nl via GitHub Pages from the build
* output (see .github/workflows/documentation.yml + static/CNAME).
*
* Built on @conduction/docusaurus-preset for brand defaults; everything
* site-specific (URL, navbar items, footer, plugins, locale set) is
* passed in here.
*/
const {createConfig} = require('@conduction/docusaurus-preset');
/* AI-crawler baseline (Organization + WebSite JSON-LD, og:image,
twitter meta, FAQPage schema from <FAQ>, SoftwareApplication schema
from <DetailHero>) comes from @conduction/docusaurus-preset >= 3.4.0
automatically. We override only the bits that are specific to this
site (robots.txt + llms.txt in static/, the sitemap config below
because we pass our own classic-preset overrides). */
module.exports = createConfig({
title: 'Conduction',
tagline: 'Open-source apps voor de Nextcloud-werkplek.',
/* Must match static/CNAME (www.conduction.nl) — GitHub Pages serves
only on the CNAME host and 301-redirects the apex to it. Using the
bare apex here made every canonical/og:url/sitemap entry point at
conduction.nl, adding an apex→www hop on top of whatever the
Cloudflare vanity worker already does — a redirect-loop amplifier
on connext./commonground. (trailingSlash is true, set by the
preset, so the worker must target https://www.conduction.nl/connext/
— with the www and the trailing slash — not /connext.) */
url: 'https://www.conduction.nl',
baseUrl: '/',
organizationName: 'ConductionNL',
projectName: 'conduction-website',
/* Public lead-intake endpoint. The support + partner forms POST here to
create a `lead` object in the Pipelinq CRM (OpenRegister, public-create
schema, anonymous rate-limited). Set PIPELINQ_LEAD_ENDPOINT at build time
to point at a real instance.
The fallback below is a DEV-ONLY convenience so the forms work out of the
box against a local docker environment. It is NOT a production value:
`localhost` is the visitor's own machine, and an http endpoint on an https
page is blocked as mixed content. LeadForm detects that at runtime and
degrades to an email fallback rather than accepting a submit it cannot
deliver — so shipping without PIPELINQ_LEAD_ENDPOINT is safe, just inert. */
customFields: {
pipelinqLeadEndpoint:
process.env.PIPELINQ_LEAD_ENDPOINT
|| 'http://localhost:8080/index.php/apps/openregister/api/objects/pipelinq/lead',
},
/* Two locales, English default. URL shape:
/ → English (canonical)
/nl/ → Nederlands
The Cloudflare Worker on connext/commonground vanity domains picks
the locale from Accept-Language and 301-redirects accordingly. */
i18n: {
defaultLocale: 'en',
locales: ['en', 'nl'],
localeConfigs: {
en: {label: 'English', htmlLang: 'en-GB', direction: 'ltr'},
nl: {label: 'Nederlands', htmlLang: 'nl-NL', direction: 'ltr'},
},
},
/* Override the preset's classic preset to drop docs. Blog plugin is
scoped to /academy/ and renders the academy section: blogs, guides,
case studies, webinars, tutorials. Posts live in academy/<slug>/.
The swizzles in src/theme/BlogListPage and src/theme/BlogPostPage
replace Docusaurus's defaults with the academy components.
/blog/* legacy paths redirect to /academy/* via static stubs in
static/blog/. */
presets: [
[
'classic',
{
docs: false,
blog: {
path: 'academy',
routeBasePath: '/academy',
showReadingTime: true,
blogTitle: 'Conduction Academy',
blogDescription: 'Blogs, guides, case studies, webinars, tutorials. One feed, all open-source.',
postsPerPage: 'ALL',
blogSidebarCount: 0,
feedOptions: {
type: ['rss', 'atom'],
title: 'Conduction Academy',
description: 'New blogs, guides, case studies, webinars, and tutorials from Conduction.',
copyright: `Conduction B.V. © ${new Date().getFullYear()}`,
},
},
theme: {
customCss: [require.resolve('./src/css/site.css')],
},
/* Built-in @docusaurus/plugin-sitemap (loaded via the classic
preset). Each locale outputs its own sitemap.xml (en at /,
nl at /nl/) — both are advertised in static/robots.txt so AI
crawlers without locale-suffix discovery still see Dutch
pages. /academy/tags/* is excluded because tag pages are
thin and confuse AI summarisers more than they help SEO.
ignorePatterns matches route paths *after* the locale prefix
is applied, so we list both forms. */
sitemap: {
changefreq: 'weekly',
priority: 0.5,
ignorePatterns: ['/academy/tags/**', '/nl/academy/tags/**'],
filename: 'sitemap.xml',
},
},
],
],
/* Keep the canal-footer's Privacy / Terms / ISO links on relative
routes. The preset's default ships absolute https://www.conduction.nl/*
URLs so per-app subdomain footers don't 404, but this IS the
marketing site that hosts those pages, so a relative link is the
cleaner UX (no needless cross-host hop). */
legalLinks: {
privacy: '/privacy',
terms: '/terms',
iso: '/quality',
},
/* Search Console / Bing Webmaster / etc. verification tokens are
filled by ops once the property has been claimed; the preset
emits a meta tag for each token present. Leave keys absent until
a real token is available, otherwise a stale placeholder ends up
in the production HTML. */
// searchConsoleVerification: {
// google: '...',
// bing: '...',
// },
/* Brand top-navbar pattern: five left-side section links + locale
dropdown + Partners ghost + Install primary CTA on the right.
Title text is set here as the default ("Conduction"); the swizzled
theme/Navbar/Logo overrides it on /connext/* and /commonground/*. */
navbar: {
title: 'Conduction',
items: [
{to: '/apps', label: 'Apps', position: 'left'},
{to: '/solutions', label: 'Solutions', position: 'left'},
{to: '/academy', label: 'Academy', position: 'left'},
{to: '/support', label: 'Support', position: 'left'},
{to: '/about', label: 'About', position: 'left'},
{type: 'localeDropdown', position: 'right'},
{to: '/install', label: 'Install', position: 'right', cta: true},
],
},
/* Brand footer link grid + Conduction tells in the copyright row.
Body copy here is the default Conduction face; the brand-strip
pass on the shared MDX pages is tracked separately. */
footer: {
links: [
{
title: 'Apps',
items: [
{label: 'OpenCatalogi', href: 'https://opencatalogi.conduction.nl/'},
{label: 'OpenRegister', href: 'https://openregister.conduction.nl/'},
{label: 'Integriq', href: 'https://openconnector.conduction.nl/'},
{label: 'Filinq', href: 'https://docudesk.conduction.nl/'},
{label: 'LaunchPad', href: 'https://launchpad.conduction.nl/'},
],
},
{
title: 'Solutions',
items: [
{label: 'OpenWoo', to: '/solutions/openwoo'},
{label: 'Support', to: '/support'},
{label: 'ConNext', to: '/connext'},
{label: 'Common Ground', to: '/commonground'},
],
},
{
title: 'Resources',
items: [
{label: 'Partners', to: '/partners'},
{label: 'Build an app', to: '/build'},
{label: 'Blogs', to: '/academy?type=blog'},
{label: 'Guides', to: '/academy?type=guide'},
{label: 'Tutorials', to: '/academy?type=tutorial'},
{label: 'Webinars', to: '/academy?type=webinar'},
],
},
{
title: 'Conduction',
items: [
{label: 'About', to: '/about'},
{label: 'Open source', to: '/about#opensource'},
{label: 'Team', to: '/about#team'},
{label: 'Way of Work', href: 'https://docs.conduction.nl/WayOfWork/way-of-work/'},
{label: 'Quality', to: '/quality'},
{label: 'How we use AI', to: '/ai'},
{label: 'Identity', href: 'https://identity.conduction.nl/'},
/* Cookies belongs in the legal bar next to Privacy / Terms,
but the preset's Footer hardcodes exactly three legal slots
(privacyTo / termsTo / isoTo) — adding a `cookies` key to
themeConfig.legalLinks renders nothing at all. Until the
preset grows a slot, the link lives here so the consent
banner is reachable after a choice has been stored. */
{label: 'Cookies', to: '/privacy#cookies'},
],
},
],
copyright: `Conduction B.V. · KvK 76741850 · BTW NL860784241B01 · IBAN NL51 ABNA 0868951550 · Lauriergracht 14h, 1016 RR Amsterdam · © ${new Date().getFullYear()}`,
},
/* OpenCatalogi content plugin slot, wired in via env once it exists. */
plugins: [
/* academy-modules: scans academy/*\/index.mdx frontmatter and emits
module → ordered parts global data. Consumed by BlogListPage
(composite ModuleCards + module pill row) and per-module MDX
index pages at /academy/modules/{slug}. */
[
require.resolve('./plugins/academy-modules'),
{contentDir: 'academy', routeBasePath: '/academy'},
],
/* INTERIM: Hermiq glyph override until the preset can publish to npm
again (trusted publishing not configured). See the plugin header. */
require.resolve('./plugins/hermiq-glyph-interim'),
/* INTERIM: apps-registry descriptions + AppCrossLinks description
rendering until the preset ships both. See the plugin header. */
require.resolve('./plugins/apps-registry-interim'),
// [
// '@conduction/docusaurus-plugin-opencatalogi',
// {
// apiUrl: process.env.OPENCATALOGI_URL || 'http://localhost:8080/index.php/apps/openregister/api',
// register: 'www-content',
// schema: 'page',
// locales: ['en', 'nl'],
// },
// ],
/* Reclaim SEO equity from URLs Google has in its index from the
pre-Docusaurus / pre-subdomain layout. The plugin emits one
static HTML page per `from` with a <meta http-equiv="refresh">
and a `<link rel="canonical">` to the `to` target, which Google
treats as a 301 signal. Only the URLs in this list have current
equivalents worth redirecting; the rest (componenten catalogue,
WP placeholders, retired training pages) are let to 404 naturally. */
[
'@docusaurus/plugin-client-redirects',
{
redirects: [
/* -iq product rename (2026-08-23): the twelve renamed apps moved
from /apps/<old-repo-slug> to /apps/<new-name>. Every one of these
URLs was live and is linked from the apps grid, the academy product
filter and the apps-registry shipped by
@conduction/docusaurus-preset — which still points at the old
paths, so these redirects are what keeps the preset's cross-links
working until it ships a release with the new ones.
The PRESENTATION keys in src/data/apps-catalog.js deliberately did
NOT move. They are the join key against `apps[].id` in
data/app-downloads.json, which is the NEXTCLOUD APP-STORE id, not
the GitHub repo slug — and the store still lists the old ids.
getApps() filters out any entry with no store match and no
downloads, so renaming those keys would have removed all twelve
apps from /apps entirely, silently. Only the href moved. */
{from: '/apps/openconnector', to: '/apps/integriq'},
{from: '/apps/app-versions', to: '/apps/versioniq'},
{from: '/apps/decidesk', to: '/apps/decidiq'},
{from: '/apps/docudesk', to: '/apps/filinq'},
{from: '/apps/doriath', to: '/apps/keepiq'},
{from: '/apps/hrmq', to: '/apps/humaniq'},
{from: '/apps/larpingapp', to: '/apps/larpinq'},
{from: '/apps/nldesign', to: '/apps/thematiq'},
{from: '/apps/openbuild', to: '/apps/buildiq'},
{from: '/apps/planix', to: '/apps/planninq'},
{from: '/apps/procest', to: '/apps/dossiq'},
{from: '/apps/scholiq', to: '/apps/learniq'},
{from: '/apps/softwarecatalog', to: '/apps/stackiq'},
/* Governance blog retitled to the two-wolves frame (2026-08-10).
Both earlier URLs were briefly live; point each straight at the
final slug, no chains. */
{from: '/academy/governance-open-source-nix-implosion', to: '/academy/opinion/feed-the-wolf-that-ships'},
{from: '/academy/opinion/governance-is-the-silent-killer-of-open-source', to: '/academy/opinion/feed-the-wolf-that-ships'},
/* "This is going to hurt" reclassified from opinion to blog
(2026-08-11). The six parts shipped at /academy/opinion/… on
2026-08-10 and were live for a day, so every old URL points at
its new /academy/blog/ address. /academy/blog/ itself is a
static stub → /academy/?type=blog (see static/academy/blog/,
same pattern as static/academy/opinion/). */
{from: '/academy/opinion/this-is-going-to-hurt-part-1-your-work', to: '/academy/blog/this-is-going-to-hurt-part-1-your-work'},
{from: '/academy/opinion/this-is-going-to-hurt-part-2-the-big-three', to: '/academy/blog/this-is-going-to-hurt-part-2-the-big-three'},
{from: '/academy/opinion/this-is-going-to-hurt-part-3-the-end-of-ownership', to: '/academy/blog/this-is-going-to-hurt-part-3-the-end-of-ownership'},
{from: '/academy/opinion/this-is-going-to-hurt-part-4-security-at-machine-speed', to: '/academy/blog/this-is-going-to-hurt-part-4-security-at-machine-speed'},
{from: '/academy/opinion/this-is-going-to-hurt-part-5-the-singularity', to: '/academy/blog/this-is-going-to-hurt-part-5-the-singularity'},
{from: '/academy/opinion/this-is-going-to-hurt-part-6-actually-it-might-help', to: '/academy/blog/this-is-going-to-hurt-part-6-actually-it-might-help'},
/* All opinion posts moved under /academy/opinion/ (2026-08-10).
Keep the published URLs working. /academy/opinion/ itself is a
static stub → /academy/?type=opinion (see static/academy/opinion/,
same pattern as the /blog/* legacy stubs). */
{from: '/academy/the-platform-moment', to: '/academy/opinion/the-platform-moment'},
{from: '/academy/government-open-source-needs-an-engine', to: '/academy/opinion/government-open-source-needs-an-engine'},
{from: '/academy/opinion/this-is-going-to-hurt-part-1-your-work', to: '/academy/blog/this-is-going-to-hurt-part-1-your-work'},
{from: '/academy/opinion/this-is-going-to-hurt-part-2-the-big-three', to: '/academy/blog/this-is-going-to-hurt-part-2-the-big-three'},
{from: '/academy/opinion/this-is-going-to-hurt-part-3-the-end-of-ownership', to: '/academy/blog/this-is-going-to-hurt-part-3-the-end-of-ownership'},
{from: '/academy/opinion/this-is-going-to-hurt-part-4-security-at-machine-speed', to: '/academy/blog/this-is-going-to-hurt-part-4-security-at-machine-speed'},
{from: '/academy/opinion/this-is-going-to-hurt-part-5-the-singularity', to: '/academy/blog/this-is-going-to-hurt-part-5-the-singularity'},
{from: '/academy/opinion/this-is-going-to-hurt-part-6-actually-it-might-help', to: '/academy/blog/this-is-going-to-hurt-part-6-actually-it-might-help'},
/* OpenSpec academy series moved from Dutch to English slugs
(2026-06-11). Parts 1 and 2 were already published, so keep
inbound links working. Parts 0/3/4 are new and never shipped a
Dutch slug, so they need no redirect. */
{from: '/academy/openspec-tutorial-1-wat-is-openspec', to: '/academy/openspec-tutorial-1-what-is-openspec'},
{from: '/academy/openspec-tutorial-2-eerste-change', to: '/academy/openspec-tutorial-2-first-change'},
/* Fleet-wide Dutch-to-English academy slug rename (2026-06-11):
every tutorial slug is now English. Keep the published Dutch
URLs working. */
{from: '/academy/nextcloud-lokaal-draaien', to: '/academy/run-nextcloud-locally'},
{from: '/academy/woo-bestanden-uploaden', to: '/academy/woo-upload-files'},
{from: '/academy/woo-register-opzetten', to: '/academy/woo-set-up-register'},
{from: '/academy/nextcloud-app-store-publiceren', to: '/academy/publish-to-nextcloud-app-store'},
{from: '/academy/haven-en-kind', to: '/academy/haven-and-haven-plus'},
{from: '/academy/hydra-tutorial-1-wat-is-hydra', to: '/academy/hydra-tutorial-1-what-is-hydra'},
{from: '/academy/hydra-tutorial-2-drie-pipelines', to: '/academy/hydra-tutorial-2-three-pipelines'},
{from: '/academy/hydra-tutorial-5-een-hydra-run-starten', to: '/academy/hydra-tutorial-5-starting-a-run'},
{from: '/academy/hydra-tutorial-6-troubleshooting-escalatie', to: '/academy/hydra-tutorial-6-troubleshooting-escalation'},
{from: '/academy/claude-skills-tutorial-1-wat-zijn-claude-skills', to: '/academy/claude-skills-tutorial-1-what-are-claude-skills'},
{from: '/academy/claude-skills-tutorial-2-je-eerste-skill', to: '/academy/claude-skills-tutorial-2-your-first-skill'},
{from: '/academy/workstation-tutorial-1-wat-installeer-je', to: '/academy/workstation-tutorial-1-what-to-install'},
{from: '/academy/workstation-tutorial-2-basis-inrichten', to: '/academy/workstation-tutorial-2-base-setup'},
{from: '/academy/workstation-tutorial-5-nextcloud-lokaal', to: '/academy/workstation-tutorial-5-nextcloud-locally'},
{from: '/academy/workstation-tutorial-6-klaar-wat-nu', to: '/academy/workstation-tutorial-6-done-what-next'},
/* Old Dutch "about us" slug. The English /about/ route is
the canonical replacement; NL translation pass will land
/nl/about/ later. */
{from: '/over-ons', to: '/about/'},
/* The OpenConnector page used to live on the apex; everything
moved to its own subdomain in the 2026-02 split. Send the
two indexed entry points to the canonical app site. */
{from: '/openconnector', to: 'https://openconnector.conduction.nl/'},
{from: '/openconnector/support', to: 'https://openconnector.conduction.nl/support/'},
/* OpenWoo brand rename: /solutions/woo conflated the law (Wet
open overheid) with the product. The page lives at
/solutions/openwoo since the fold-openwoo-into-fleet change;
keep inbound links working from anywhere that already shipped
the old URL (presentations, partner pages, press). The `nl`
locale doesn't emit page-route variants for src/pages/*.mdx
so /nl/solutions/openwoo doesn't exist as a target — same
pattern as the /over-ons → /about/ entry above. */
{from: '/solutions/woo', to: '/solutions/openwoo'},
/* /iso renamed to /quality (2026-05-19): the page was about
ISO 9001 + 27001 only, but we now treat ISO as one tool inside
a broader quality story (pentest-tools.com, GitHub workflow,
policy statements). The NL page lives at /nl/quality (same
slug as EN; the page title is localised to "Kwaliteit").
Only the EN /iso redirect is emitted here — /nl/iso 404s
gracefully because there are no inbound links to it. */
{from: '/iso', to: '/quality'},
/* App rename 2026-05-30: MyDash → LaunchPad and OpenBuilt → OpenBuild.
The old /apps/mydash and /apps/openbuilt URLs were indexed in
search engines and shipped on partner sites; redirect them to
the new canonical app pages. */
{from: '/apps/mydash', to: '/apps/launchpad'},
{from: '/apps/openbuilt', to: '/apps/buildiq'},
/* Academy series rename 2026-06-01: deskdesk-tutorial →
build-an-app-tutorial. The seven existing parts (0–6) were
linked from earlier blog posts, partner decks, and the
previous build-an-app standalone teaser; redirect each one
to its new home so external links keep working. */
{from: '/academy/deskdesk-tutorial-0-three-paths', to: '/academy/build-an-app-tutorial-0-three-paths'},
{from: '/academy/deskdesk-tutorial-1-scaffold', to: '/academy/build-an-app-tutorial-1-scaffold'},
{from: '/academy/deskdesk-tutorial-2-schemas-manifest', to: '/academy/build-an-app-tutorial-2-schemas-manifest'},
{from: '/academy/deskdesk-tutorial-3-calendar', to: '/academy/build-an-app-tutorial-3-calendar'},
{from: '/academy/deskdesk-tutorial-4-knowledge-and-ship', to: '/academy/build-an-app-tutorial-4-knowledge-and-ship'},
{from: '/academy/deskdesk-tutorial-5-advanced-manifest', to: '/academy/build-an-app-tutorial-5-advanced-manifest'},
{from: '/academy/deskdesk-tutorial-6-integrate', to: '/academy/build-an-app-tutorial-6-integrate'},
],
},
],
],
});