TBMQ related info migration to new website - #585
Conversation
…t at root tbmq.io is a TBMQ-only site: its docs live at /docs/<slug> and the product landing is the site root, not /product/. Update TBMQ_DOCS_ROOT and TBMQ_URLS.product accordingly, route the Try it now features/deployGroups and description links through the helpers, and repoint the two cross-product DocLinks in the MQTT integration guide at tbmq.io.
Route every local /docs/mqtt-broker/ and /products/mqtt-broker/ link in the TBMQ pricing surfaces (CE, self-managed PAYG/perpetual, private cloud FAQs, the CE Install CTA and the See full details button) through the external-sites helpers so they resolve to the segment-free tbmq.io URLs.
… and sequencing details
…flare edge Replace the four TBMQ catch-all groups with five ordered cross-origin splats in DYNAMIC_REDIRECTS (Jekyll install/ renames before the generic rules) and rewrite the 27 TBMQ single redirects to absolute one-hop tbmqDocsUrl targets, so no request ever chains. external-sites.ts gains a Node-safe env guard and a TBMQ_DOCS_BASE export for splat templates, since redirects.ts is loaded by plain Node in the generator and by Astro's config loader. The splats stay out of astro.redirects.ts on purpose: local TBMQ pages remain browsable in dev and lint:linkcheck stays green until the content is deleted. Dynamic rule budget after regeneration: 47 of Cloudflare's 100. Deploy gate: tbmq.io must serve its docs at the new URL shape before this reaches production — the edge 301s fire regardless of target availability.
Collapse the five-rule TBMQ cutover group to a two-rule identity forward (/docs/mqtt-broker/* and /docs/pe/mqtt-broker/* pass through with the segment kept) and drop the 27 cross-origin singles: tbmq.io keys its full legacy redirect table on the original URL shape and strips the segment on its own edge, so every rename lives in one repo. Also send /products/mqtt-broker/ to the tbmq.io root, which is its product landing.
The identity-forward splats resolve every /docs/mqtt-broker/* and /products/mqtt-broker/ URL at the edge, and lint:linkcheck replays public/_redirects, so the pricing FAQ/CTA links and the two mqtt.mdx DocLinks can stay local instead of spelling tbmq.io URLs. The contact-us ?subject=TBMQ preselection is kept. The removal inventory now notes that the mqtt.mdx DocLinks are a build-time dependency on Products.TBMQ and must be re-pointed when the enum entries are removed.
…o edge redirects Revert the six link/component files to the merge-base state so the PR no longer touches them: mega-menu and docs-selector TBMQ items, home ecosystem card, Try-it-now panel, version switcher and the MQTT integration include all keep their local TBMQ URLs. The identity-forward rules in public/_redirects are the single cutover mechanism.
The plan, spec and removal inventory described the superseded segment-stripping redirect design and in-flight link repointing that was since reverted — the shipped mechanism is the two-rule identity forward in public/_redirects, documented inline in src/data/redirects.ts.
With all entry-point links restored to local paths, redirects.ts was the module's only consumer — replace it with a hardcoded TBMQ_ORIGIN const. Points at the tbmq.tbqa.cloud staging origin for now; swap to https://tbmq.io and regenerate when the production DNS goes live.
The two-rule identity forward kept the mqtt-broker/ segment and relied on tbmq.io to resolve the legacy shape, so every docs hit paid a second redirect on the far side. Strip the segment at our edge instead: five ordered cutover splats (Jekyll-era install/ renames first — Cloudflare takes the first match and never chains) plus the 28 rewritten singles, so every legacy URL resolves in one hop to its final tbmq.io page. tbmqDocsUrl is a local one-liner on TBMQ_ORIGIN rather than a revived external-sites module.
- Delete /docs/mqtt-broker/ and /docs/mqtt-broker/pe/ page trees and their _includes - Delete TBMQ docs image tree, orphaned schema SVGs, tbmq-ce install icon, and bulk-import CSV resource - Remove TBMQ sidebars, tab links, and prefix mappings from astro.sidebar.ts - Drop TBMQ PE key-page entry from llms.txt
vvlladd28
left a comment
There was a problem hiding this comment.
Review summary
Reviewed 6 changed files in TBMQ related info migration to new website. Left 7 comment(s) inline.
Reviewer questions answered
"Was any redirect lost?" — No. I diffed the set of source paths in public/_redirects between main and this branch: every source present on main is still present here, and three are new (/docs/mqtt-broker/*, /docs/pe/mqtt-broker/image-gallery/, /products/mqtt-broker/). The four deleted CATCH_ALL_REDIRECTS prefix groups (mqtt-broker/install, mqtt-broker/pe/install, pe/mqtt-broker/install, pe/mqtt-broker) are each replaced by an equivalent DYNAMIC_REDIRECTS splat, and the ordering is right — the three install/* rules precede the two generic ones, which matters because Cloudflare takes the first match and never chains. No redirect target anywhere in _redirects or redirects.json still points into a local /docs/mqtt-broker/… path, and scripts/check-redirect-chains.ts passes (✓ No redirect chains found across 1040 SINGLE + 52 NON_DOCS targets). Rule budget is fine: 47 dynamic (cap 100) and 1135 total (cap 2100).
"Do the pages correctly move /docs/mqtt-broker/* → tbmq.io /docs/*?" — Structurally yes. The segment-stripping is correct and every URL resolves in one hop:
| Source | Target |
|---|---|
/docs/mqtt-broker/* |
…/docs/:splat (also covers /docs/mqtt-broker/pe/* → …/docs/pe/*) |
/docs/pe/mqtt-broker/* (Jekyll) |
…/docs/pe/:splat |
/docs/mqtt-broker/install/* |
…/docs/installation/:splat |
/docs/mqtt-broker/pe/install/*, /docs/pe/mqtt-broker/install/* |
…/docs/pe/installation/:splat |
27 special-case singles (api→rest-api, faq→why-tbmq, troubleshooting→help, …) |
preserved 1:1 |
The caveat is that the two generic splats assume tbmq.io mirrors thingsboard.io's TBMQ slugs 1:1 for all 174 local TBMQ doc pages (84 CE + 90 PE). Anything renamed on the new site becomes a 301-into-404 with no signal from CI — worth a scripted spot-check of that slug list against the live tree before merge.
Additional findings
These observations are about existing code outside the PR's diff — spotted while reading surrounding context.
- astro.redirects.ts:24 —
devFallbackRedirects['/docs/pe/mqtt-broker/search/'] = '/docs/mqtt-broker/pe/search/'mirrored the now-deletedpe/mqtt-brokercatch-all. It still resolves to a local path, so dev and prod now disagree for this URL, and it becomes dangling once the follow-up PR deletes the TBMQ docs. Dev-only, but it should go with the same change. - config/integrations/sitemap.ts:59 —
isIndexableCanonicalPageincludes any built page that is self-canonical and notnoindex. All 174 TBMQ doc pages still build, so the production sitemap will keep advertising URLs that Cloudflare 301s off-domain. That makes the follow-up removal PR a prerequisite for a clean cutover rather than optional cleanup — worth calling out in the PR body so the two don't drift apart in time. - scripts/generate-redirects.ts:257-266 — the script computes
dynamicRuleCountand prints it, then exits 0 regardless. Since exceeding Cloudflare's 100-dynamic cap silently drops rules rather than erroring, aprocess.exit(1)above 100 (or a warning at ~80) next to that existingconsole.logwould be a cheap tripwire. Not a problem at 47, but this PR is a natural moment to add it while dynamic rules are being appended. - scripts/lint-linkcheck.ts:111 — the
{ from: '/docs/mqtt-broker/', to: '/docs/mqtt-broker/pe/' }CE↔PE pairing entry becomes meaningless once the tree is gone; another item for the follow-up. - PR description — it states "TBMQ pricing FAQs: documentation/CTA links point at tbmq.io". They don't in this diff:
refactor(tbmq): restore local TBMQ links covered by edge redirectsreverted that, and only the?subject=TBMQchange landed. Worth correcting the body so reviewers of the follow-up aren't misled about what's already done.
This review was auto-generated. Findings may contain errors — please verify before applying changes.
| * GONE — page removed, redirect to fallback | ||
| */ | ||
| // Staging origin until tbmq.io DNS goes live — swap to https://tbmq.io then. | ||
| const TBMQ_ORIGIN = 'https://tbmq.tbqa.cloud'; |
There was a problem hiding this comment.
This is the one thing I'd block on. As written, merging this ships 301s from the entire TBMQ URL space on thingsboard.io to a staging hostname — /docs/mqtt-broker/*, /docs/pe/mqtt-broker/*, and /products/mqtt-broker/ all land on tbmq.tbqa.cloud. A 301 is cacheable and browsers hold onto it aggressively, so anyone who hits a TBMQ URL between merge and the DNS cutover keeps going to staging from their own cache long after the const is flipped.
Two things would make the swap safe:
- The comment should say that flipping the const is only half the job —
pnpm generate:redirectshas to be re-run and the regeneratedpublic/_redirects+public/redirects.jsoncommitted, or the edge keeps servingtbmq.tbqa.cloudwhile the source claims otherwise. - Could this be an assertion rather than a note? A couple of lines in
scripts/check-redirect-chains.ts(or the generator) that fail whenTBMQ_ORIGINstill containstbqa.cloudand, say,ALLOW_STAGING_ORIGIN=1isn't set would turn a silent regression into a red build.
Or — is there a reason not to point at https://tbmq.io right now and let the DNS cutover be the thing that flips? Until tbmq.io serves content the redirect target is dead either way, and a temporary 301-to-nothing seems less costly than a cached 301-to-staging.
There was a problem hiding this comment.
Went with your third option: TBMQ_ORIGIN now points at https://tbmq.io directly (3a94981), so the DNS cutover is what activates the redirects and there is no flip step left in the runbook. The caching asymmetry decides it, as you said — a cached 301 to the permanent address is harmless, a cached 301 to staging would linger in browsers and crawlers. The operative constraint is documented on the const and in the PR body: don't deploy to production before tbmq.io DNS resolves; in that window TBMQ URLs 301 to a dead host, which fails loud and self-heals at cutover.
On the assertion: considered it, but kept the generator untouched in this PR — with the staging host gone from the redirect data entirely, it no longer has anything to guard. And to de-risk the 1:1-slug assumption from your summary: all 174 local TBMQ doc URLs (84 CE + 90 PE) plus the 28 legacy single-redirect targets were script-checked through the _redirects rules against the site tbmq.io will serve (its staging mirror, since DNS isn't live yet) — every target returns 200.
|
|
||
| // tbmq.io is TBMQ-only: its docs tree carries no mqtt-broker/ segment, so | ||
| // thingsboard.io's /docs/mqtt-broker/<slug> is /docs/<slug> there. | ||
| const tbmqDocsUrl = (slug: string): string => `${TBMQ_ORIGIN}/docs/${slug}`; |
There was a problem hiding this comment.
The parameter is named slug, but every one of the 27 call sites actually passes a path with a trailing slash ('rest-api/', 'pe/installation/upgrade-instructions/'), and that trailing slash is load-bearing for the redirect target under trailingSlash: 'always'. Nothing in the signature says so — compare RedirectEntry.target just below, which spells out "Absolute target path with trailing slash". Either normalize inside the helper (append / when absent) or rename to path and document the convention, so call site 28 doesn't quietly drop it.
There was a problem hiding this comment.
Renamed to path with the convention documented (bb9d515): the value is appended verbatim — page paths carry their trailing slash, and :splat passes through untouched (the splat capture already carries the request's trailing slash). Documenting rather than normalizing was deliberate: appending / inside the helper would corrupt :splat targets now that the dynamic entries route through it too.
| '/services/support/': '/services/', | ||
|
|
||
| // TBMQ — moved to tbmq.io, whose site root is the product landing | ||
| '/products/mqtt-broker/': `${TBMQ_ORIGIN}/`, |
There was a problem hiding this comment.
This is the only key in NON_DOCS_REDIRECTS that collides with a real page — I checked all 52 against src/pages/, and /products/mqtt-broker/ → src/pages/products/mqtt-broker/index.astro is the sole hit. That collision isn't a no-op: in astro/dist/core/routing/create-manifest.js (v6.3.8, lines 474-477) file-based routes whose route matches a redirect route are filtered out, so the redirect wins and the TBMQ product landing stops being built at all. In pnpm dev/preview the page becomes unreachable and bounces to the staging host.
If that's intended, fine — but it's worth stating, because the group comment on the DYNAMIC_REDIRECTS block below says the TBMQ rules are "Deliberately absent from astro.redirects.ts so the local TBMQ pages stay browsable in dev", and that rationale doesn't survive here: astro.redirects.ts spreads NON_DOCS_REDIRECTS and public/redirects.json (which carries the 27 rewritten singles), so only the five splats are actually dev-exempt.
Separately — /products/mqtt-broker/privacy-policy/ and /products/mqtt-broker/terms-of-use/ exist as pages and are not redirected, so the parent moves to tbmq.io while its two legal children stay on thingsboard.io. Intentional (legal pages often need to stay put), or an oversight?
There was a problem hiding this comment.
Intended, and now stated on the entry (bb9d515): the redirect deliberately supersedes src/pages/products/mqtt-broker/index.astro — Astro drops the colliding file-based route, so dev/preview bounce to the new origin just like prod. The dynamic-group comment is fixed too: it now says only the five splats are dev-exempt, and that the TBMQ singles / /products/mqtt-broker/ reach dev via redirects.json / NON_DOCS_REDIRECTS — none of which shadows a local page.
Legal children: deliberate. tbmq.io has no privacy-policy / terms-of-use equivalents (checked — 404 on the new site), so both stay on thingsboard.io; also noted in the comment.
| 'first match and never chains. Deliberately absent from astro.redirects.ts so ' + | ||
| 'the local TBMQ pages stay browsable in dev until deleted.', | ||
| entries: [ | ||
| { source: '/docs/mqtt-broker/pe/install/*', target: `${TBMQ_ORIGIN}/docs/pe/installation/:splat` }, |
There was a problem hiding this comment.
tbmqDocsUrl was introduced a few hundred lines up specifically to encode "tbmq.io's docs tree has no mqtt-broker/ segment", but these five entries bypass it and re-open-code ${TBMQ_ORIGIN}/docs/…. That leaves two spellings of the same rule in one file, so if tbmq.io ever changes its docs base (adds a version segment, drops /docs), the next person has to find and fix both shapes. tbmqDocsUrl('pe/installation/:splat') reads the same and keeps the knowledge in one place — the helper is plain concatenation, so :splat passes straight through.
There was a problem hiding this comment.
Done (bb9d515) — all five entries now go through tbmqDocsUrl(...), :splat included.
| { source: '/docs/pe/mqtt-broker/install/*', target: `${TBMQ_ORIGIN}/docs/pe/installation/:splat` }, | ||
| { source: '/docs/mqtt-broker/install/*', target: `${TBMQ_ORIGIN}/docs/installation/:splat` }, | ||
| { source: '/docs/pe/mqtt-broker/*', target: `${TBMQ_ORIGIN}/docs/pe/:splat` }, | ||
| { source: '/docs/mqtt-broker/*', target: `${TBMQ_ORIGIN}/docs/:splat` }, |
There was a problem hiding this comment.
Worth a line in the group comment, because this splat quietly disables the repo's own safety net for the follow-up PR: scripts/lint-linkcheck.ts passes redirectsFilePath: './public/_redirects' and replays these rules before the on-disk lookup, so from here on every internal /docs/mqtt-broker/… link resolves to an off-origin URL and stops being existence-checked.
There are still 126 such links across 25 files outside the TBMQ docs tree — src/data/installations.ts, src/data/navigation.ts, src/data/pricing/**, src/pages/products/mqtt-broker/index.astro, src/components/MqttBroker/*, src/components/starlight/Header.astro, and 7 published blog posts. The practical effect is that linkcheck stays green through the deletion PR whether or not those links were repointed — exactly when you'd most want it to shout. (check-redirect-chains.ts has the same blind spot by construction: it only matches targets against local /docs/… patterns, so cross-origin targets are invisible to it.) Neither is broken, but it's worth either noting it here or adding a temporary check that the TBMQ links resolve on the new origin.
There was a problem hiding this comment.
Noted in the group comment now (bb9d515), including that the follow-up deletion PR must repoint the remaining local TBMQ links itself since linkcheck won't flag them. Instead of wiring a remote-origin check into CI (a flaky external dependency), I ran the scripted verification: all 174 local TBMQ doc URLs plus the 28 legacy single targets resolve 200 on the TBMQ origin's staging mirror.
| id: 'tbmq-perp-setup-help', | ||
| question: 'How can I get help with installation and setup?', | ||
| answer: `<p>All perpetual license packages provide dedicated support with predefined response time and access to the ThingsBoard Support Portal. Our expert support team is available to assist you with system deployment by following our recommended <a target="_blank" href="/docs/mqtt-broker/installation/" rel="noopener noreferrer">installation methods</a> and architecture, ensuring a smooth and efficient setup. For custom installation scripts or alternative deployment scenarios, additional support options are available and you can <a target="_blank" href="/contact-us/" rel="noopener noreferrer">contact us</a> to discuss your needs.</p>`, | ||
| answer: `<p>All perpetual license packages provide dedicated support with predefined response time and access to the ThingsBoard Support Portal. Our expert support team is available to assist you with system deployment by following our recommended <a target="_blank" href="/docs/mqtt-broker/installation/" rel="noopener noreferrer">installation methods</a> and architecture, ensuring a smooth and efficient setup. For custom installation scripts or alternative deployment scenarios, additional support options are available and you can <a target="_blank" href="/contact-us/?subject=TBMQ" rel="noopener noreferrer">contact us</a> to discuss your needs.</p>`, |
There was a problem hiding this comment.
This answer was edited here — the contact-us link at the end gained ?subject=TBMQ — but the docs link earlier in the same string still points at /docs/mqtt-broker/installation/, which now takes a 301 hop to tbmq.io. Given that scripts/check-redirect-chains.ts exists precisely to keep upstream targets pointing at the final destination, migrating the CTA while leaving the doc link for the edge to rewrite reads inconsistent within one line.
Same pattern elsewhere in the pricing tree: src/data/pricing/tbmq-ce.ts:15 (ctaHref), tbmq-self-managed-payg.ts:16,21,280, tbmq-ce.ts:26,46,67,77,103,108,123,144, tbmq-private-cloud.ts:243. I get the deliberate decision to leave entry-point links alone until the pages are deleted — the ask is just to be explicit, since the PR body currently claims these already point at tbmq.io. If the FAQs are being touched anyway, repointing their TBMQ docs links in the same pass would spare users the hop and keep the file internally consistent.
There was a problem hiding this comment.
The docs links stay local on purpose — repointing just the FAQs would fragment the "all local TBMQ links cut over at the edge, the follow-up repoints everything in one pass" strategy (126 links across 25 files, as you counted). The ?subject=TBMQ addition isn't a partial migration of that line — it's a contact-form UX tweak that stands on its own. The PR description was the actual bug here, and it's fixed: it no longer claims the FAQ docs links point at tbmq.io, and it now calls out the follow-up as a cutover prerequisite.
| id: 'tbmq-ce-migration-pe', | ||
| question: 'Is it possible to migrate from the Community Edition to the self-managed TBMQ Professional Edition?', | ||
| answer: '<p>Yes, you can upgrade from TBMQ Community Edition to Professional Edition without losing any data and/or configurations. The upgrade process preserves your existing setup, ensuring a seamless transition. However, please note that any custom modifications made directly to the source code of the Community Edition will be removed during the upgrade process. For more information about the migration procedure, please <a target="_blank" href="/contact-us/" rel="noopener noreferrer">contact us</a>.</p>', | ||
| answer: '<p>Yes, you can upgrade from TBMQ Community Edition to Professional Edition without losing any data and/or configurations. The upgrade process preserves your existing setup, ensuring a seamless transition. However, please note that any custom modifications made directly to the source code of the Community Edition will be removed during the upgrade process. For more information about the migration procedure, please <a target="_blank" href="/contact-us/?subject=TBMQ" rel="noopener noreferrer">contact us</a>.</p>', |
There was a problem hiding this comment.
That's 22 hand-written copies of /contact-us/?subject=TBMQ across the three files (5 + 6 + 11), and the wider repo has no shared helper for contact-us URLs — every caller open-codes the query string with drifting encodings: ?subject=ThingsBoard%20Products in src/data/pricing/tb-self-managed.ts, encodeURIComponent('TBMQ') in src/scripts/pricing/calc-tbmq-perp.ts, ?subject=TBMQ&tbmqorder&message=… in src/pages/products/mqtt-broker/index.astro.
A small contactUsUrl({ subject, message }) in src/util/ would collapse all of it and — with a union type on subject — guarantee the value matches an actual <option> in ContactForm.astro. I did verify TBMQ matches (ContactForm.astro:88), and that the prefill at line 291 is a bare sel.value = subject, so a typo silently no-ops with no visible error. Trade-off: these answers are raw HTML string literals and tbmq-ce.ts uses single quotes, so a helper means switching it to template literals (the other two files already use backticks). If that churn isn't wanted now, even a module-level const TBMQ_CONTACT = '/contact-us/?subject=TBMQ' per file makes a future change one edit instead of 22.
There was a problem hiding this comment.
Took the lighter option: a shared TBMQ_CONTACT_US_URL in the new src/models/tbmq.ts (0107957), used by all three FAQ files — one edit instead of 22. The tbmq-ce.ts answers touched by it moved to template literals; rendered HTML is byte-identical (verified by evaluating the modules). Passed on the full contactUsUrl({ subject, message }) helper for now: the remaining open-coded call sites have divergent query shapes (&tbmqorder&message=…, calculator-built messages) and several sit on TBMQ surfaces the follow-up may move wholesale — a union-typed helper is a good refactor once that dust settles. src/models/tbmq.ts is also the intended home for tbmq.io link helpers when the follow-up repoints local links.
…mments - Route the five TBMQ dynamic targets through tbmqDocsUrl so the "no mqtt-broker/ segment on tbmq.io" rule has one spelling; rename the helper param to `path` and document the load-bearing trailing-slash / :splat convention. - Document the two-step staging-origin swap (flip const, regenerate, commit) next to TBMQ_ORIGIN. - State that /products/mqtt-broker/ intentionally supersedes the local landing route (Astro drops file-based routes that collide with a redirect) and that its legal children stay local on purpose. - Correct the dynamic-group comment: only the five splats are dev-exempt, and note the linkcheck blind spot for internal /docs/mqtt-broker/ links. - Drop the /docs/pe/mqtt-broker/search/ dev fallback that mirrored the deleted pe/mqtt-broker catch-all and still resolved to a local path.
Follow-up to the TBMQ docs removal, addressing review feedback: - delete zero-reference orphans: LiveDemoCard.astro (+ its tbmq-demo-root-ca.pem asset, now redirected to the TBMQ site's own copy), TbmqIntegrations.astro, product-selector/tbmq.svg, and the TBMQ_VER / TBMQ_PE_VER / TBMQ_BRANCH version constants - retarget the two cross-product DocLinks in the MQTT integration guide to absolute tbmq.io URLs (verified both pages exist on the new site) - drop TBMQ from live docs navigation: VersionSwitcher product family, SearchButton prefix routes, Header GitHub-platform detection - reword the llms.txt site description to point TBMQ at tbmq.io - remove the dev-only search fallback that targeted a deleted page
Collapse 22 hand-written copies of /contact-us/?subject=TBMQ across the three TBMQ FAQ files into TBMQ_CONTACT_US_URL so a future change is one edit. The subject value must match an <option> in ContactForm.astro — the prefill silently no-ops on unknown values. tbmq-ce.ts answers touched by this switch move from single-quoted strings to template literals; rendered HTML is unchanged.
Swap TBMQ_ORIGIN from the tbqa.cloud staging host to https://tbmq.io, per review. The DNS cutover is now what activates the redirects, removing the flip-regenerate-commit step from the cutover runbook entirely. Trade-off: this PR must not reach production before tbmq.io DNS resolves — until then TBMQ URLs 301 to a dead host, which fails loud and self-heals at cutover, whereas a cacheable 301 to staging would linger in browsers and crawlers long after a later flip. All 60 rewritten targets in public/_redirects + public/redirects.json are a verified host-only swap (paths identical).
|
Thanks for the thorough pass — everything actionable landed in bb9d515 / 0107957 / 3a94981; per-item responses are on the inline threads. On the additional findings:
On the summary's 1:1-slug caveat: verified — all 174 local TBMQ doc URLs (84 CE + 90 PE) and the 28 legacy single targets were mapped through |
# Conflicts: # public/_redirects
feat(tbmq): remove TBMQ docs content, sidebar config, and doc assets
The edge splats still cover every old URL, but the ecosystem cards, nav mega-menu, pricing FAQ, blog and installations links now target tbmq.io directly via a shared TBMQ_SITE_URL constant instead of paying the 301 hop through the retired local pages. Drop the OG override for the superseded /products/mqtt-broker/ landing.
Description
Migrates the TBMQ (MQTT Broker) web presence from thingsboard.io to the dedicated site tbmq.io.
Net changes in this PR:
/docs/mqtt-broker/*and/docs/mqtt-broker/pe/*(plus the Jekyll-era/docs/pe/mqtt-broker/*shapes) are forwarded to the corresponding tbmq.io docs, and/products/mqtt-broker/is forwarded to the tbmq.io landing (src/data/redirects.ts+ regeneratedpublic/_redirects/public/redirects.json). Its legal children (privacy-policy/,terms-of-use/) intentionally stay on thingsboard.io — tbmq.io has no equivalents for them yet.https://tbmq.iodirectly — the DNS cutover is what activates them, so there is no origin flip left in the cutover runbook.TBMQ_CONTACT_US_URLconstant. Docs links inside the FAQs intentionally stay local — like all other TBMQ entry-point links (nav, installations, home ecosystem card), the cutover happens at the edge via the redirects above, and the follow-up deletion PR repoints them.Verification: every local TBMQ doc URL (84 CE + 90 PE = 174) plus all 28 legacy single-redirect targets were script-checked through the
_redirectsrules against the TBMQ site (via its tbqa.cloud staging mirror, since tbmq.io DNS is not live yet) — each mapped target returns 200, so the generic splats introduce no 301-into-404s.Type of change
src/content/docs/**)src/content/_includes/**)src/components/**,src/styles/**)src/pages/**,src/data/**)src/data/redirects.ts)releaseskill)Affected products
TBMQ (CE + PE). No other product pages are touched.
Related issues
tbmq-migration). This is a cutover prerequisite, not optional cleanup: until it lands, the 174 TBMQ doc pages still build and sit in the production sitemap while the edge 301s them off-domain. It targets this branch, so both changes reachmaintogether.Checklist
pnpm checkpasses (Astro / TypeScript)pnpm lint:eslintpassespnpm lint:slugcheckpasses (required if pages were added/renamed/moved across languages)pnpm lint:linkcheckpasses locally — required to merge; run it before requesting review (usepnpm lint:linkcheck:nobuildif you already ran a build)src/data/redirects.ts, andpnpm generate:redirectswas runsrc/data/versions.ts