Skip to content

Add BaseUrl field to domain settings for headless CMS frontend URL support - #549

Merged
patrickdemooij9 merged 6 commits into
dev/mainfrom
copilot/add-baseurl-field-domain-settings
Aug 6, 2026
Merged

patrickdemooij9 merged 6 commits into
dev/mainfrom
copilot/add-baseurl-field-domain-settings

Conversation

Copilot AI commented Jul 6, 2026 •

Copy link
Copy Markdown
Contributor

Headless CMS setups use the API/CMS domain in sitemap.xml, robots.txt sitemap references, and canonical URLs instead of the actual frontend domain. This adds a per-domain-collection BaseUrl field that overrides the host portion of generated URLs.

Data layer

  • Added BaseUrl (varchar 500, nullable) column to SeoDomainCollectionEntity and SeoDomainCollection
  • Database migration (state-7) adds the column to SeoToolkitDomainCollections
  • Repository updated to persist/load the field

URL rewriting

  • New BaseUrlHelper.ApplyBaseUrl(url, baseUrl) utility replaces scheme+host while preserving path/query
  • If BaseUrl is empty/null, original URL is returned unchanged (backward compatible)

Consumers updated

  • SitemapGenerator: applies BaseUrl to page URLs and alternate page hreflang URLs
  • SitemapIndexGenerator: resolves SeoDomainCollection per domain, applies BaseUrl
  • SitemapMiddleware: resolves domain collection, passes BaseUrl to generator via SitemapGeneratorOptions
  • RobotsSitemapProvider: applies BaseUrl to sitemap URLs in robots.txt output
  • TextSeoValueConverter: applies BaseUrl when resolving %CurrentUrl% (canonical)

Usage

Set the BaseUrl field on a domain collection (e.g. https://sw-unlimited-db.com). All URLs generated for that domain group will use the frontend host:

// Before: https://api.sw-unlimited-db.com/some-page
// After:  https://sw-unlimited-db.com/some-page

Tests

  • 11 unit tests for BaseUrlHelper covering schemes, ports, paths, edge cases
  • 2 integration tests for RobotsSitemapProvider BaseUrl behavior

Copilot AI added 2 commits July 6, 2026 11:38
…pport

- Add BaseUrl column to SeoDomainCollectionEntity and SeoDomainCollection model
- Create database migration for new column
- Add BaseUrlHelper utility to rewrite URLs with configured base URL
- Update SitemapGenerator, SitemapIndexGenerator, and SitemapMiddleware to apply BaseUrl
- Update RobotsSitemapProvider to use BaseUrl for sitemap URLs in robots.txt
- Update TextSeoValueConverter to apply BaseUrl for canonical URLs (%CurrentUrl%)
- Add BaseUrlHelper unit tests and RobotsSitemapProvider BaseUrl tests

Closes #548
Copilot AI changed the title [WIP] Add BaseUrl field to domain settings for sitemap Add BaseUrl field to domain settings for headless CMS frontend URL support Jul 6, 2026
Copilot AI requested a review from patrickdemooij9 July 6, 2026 11:42
@patrickdemooij9
patrickdemooij9 marked this pull request as ready for review August 5, 2026 07:07
@qodo-code-review

Copy link
Copy Markdown
Contributor

PR Summary by Qodo

Support headless frontends via per-domain BaseUrl override in SEO URL generation

✨ Enhancement 🧪 Tests 🕐 40+ Minutes

Grey Divider

AI Description

• Add per-domain-collection BaseUrl setting persisted in DB and editable in backoffice UI.
• Rewrite generated absolute URLs (sitemaps, robots sitemap refs, canonicals) to use BaseUrl host.
• Add unit/integration tests to validate BaseUrl rewriting and robots.txt sitemap output behavior.
Diagram

graph TD
  REQ(["HTTP request"]) --> MW["SitemapMiddleware"] --> GEN["SitemapGenerator/Index"] --> HELPER["BaseUrlHelper"]
  MW --> RES["SeoDomainResolver/Service"] --> DB[("SeoToolkitDomainCollections")]
  ROB["RobotsSitemapProvider"] --> HELPER
  TXT["TextSeoValueConverter"] --> RES
  UI["Backoffice Domain UI"] --> RES

  subgraph Legend
    direction LR
    _req(["Request"]) ~~~ _svc["Service/Module"] ~~~ _db[("Database")]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Rely on forwarded headers / reverse proxy public host
  • ➕ No new persistence surface area (no DB column, no UI field)
  • ➕ Single source of truth for the public host across the entire app
  • ➖ Doesn’t work when the CMS/API host is intentionally different from the frontend host
  • ➖ Often environment-specific and harder to support in multi-domain collections
2. Global BaseUrl setting (not per domain collection)
  • ➕ Simpler configuration and fewer code paths
  • ➕ Lower risk of per-domain mismatch
  • ➖ Insufficient for multi-site/multi-domain setups
  • ➖ Cannot represent multiple frontend hosts within one Umbraco instance
3. Pluggable URL rewriting via interface (IBaseUrlProvider/IUrlRewriter)
  • ➕ More extensible for future needs (per-culture, per-request, custom rules)
  • ➕ Keeps rewriting policy out of generators/middleware
  • ➖ More abstractions/DI complexity for a single override use case
  • ➖ Higher maintenance surface area

Recommendation: The chosen per-domain-collection BaseUrl persisted in storage is the best fit for headless + multi-domain scenarios and keeps backward compatibility (null/empty => no change). The main follow-up to consider is documenting when to prefer proxy/forwarded-header configuration vs BaseUrl, but the implementation strategy is appropriate.

Files changed (17) +299 / -27

Enhancement (12) +117 / -14
BaseUrlHelper.csIntroduce BaseUrlHelper to rewrite absolute URLs +33/-0

Introduce BaseUrlHelper to rewrite absolute URLs

• Adds a utility that replaces scheme/host/port based on a configured BaseUrl while preserving the original path and query. Returns the original URL for null/empty baseUrl or invalid/relative inputs.

src/SeoToolkit.Umbraco.Common.Core/Helpers/BaseUrlHelper.cs

SeoDomainCollection.csExpose BaseUrl on SeoDomainCollection business model +1/-0

Expose BaseUrl on SeoDomainCollection business model

• Adds a nullable BaseUrl property so higher layers can consume the configured frontend host override.

src/SeoToolkit.Umbraco.Common.Core/Models/Business/SeoDomainCollection.cs

SeoDomainCollectionEntity.csMap BaseUrl column on SeoDomainCollectionEntity +5/-0

Map BaseUrl column on SeoDomainCollectionEntity

• Adds BaseUrl persistence mapping with null support and a 500-character length constraint aligned with the migration.

src/SeoToolkit.Umbraco.Common.Core/Models/Database/SeoDomainCollectionEntity.cs

SeoDomainsRepository.csPersist and hydrate BaseUrl in domain collection repository +4/-1

Persist and hydrate BaseUrl in domain collection repository

• Updates repository mapping for Get/GetAll/Save so BaseUrl is read from and written to SeoDomainCollectionEntity.

src/SeoToolkit.Umbraco.Common.Core/Repositories/Domains/SeoDomainsRepository.cs

types.gen.tsExtend generated TS API types with baseUrl +1/-0

Extend generated TS API types with baseUrl

• Adds the optional baseUrl field to the SeoDomainCollection TypeScript type so the UI can read/write the new setting.

src/SeoToolkit.Umbraco.Common/assets/src/api/types.gen.ts

SeoToolkitDomainEditView.element.tsAdd Base URL editor to domain collection backoffice view +24/-0

Add Base URL editor to domain collection backoffice view

• Adds a URL input field that updates the domain model's baseUrl (empty string => null) and styles it for full-width layout. Includes descriptive help text targeted at headless frontend setups.

src/SeoToolkit.Umbraco.Common/assets/src/workspaces/SeoToolkitDomainEditView.element.ts

RobotsSitemapProvider.csApply BaseUrl when generating sitemap URLs for robots.txt +4/-2

Apply BaseUrl when generating sitemap URLs for robots.txt

• Wraps computed sitemap.xml URLs with BaseUrlHelper.ApplyBaseUrl so robots.txt references use the configured frontend host when available.

src/SeoToolkit.Umbraco.Core/Connectors/RobotsSitemapProvider.cs

TextSeoValueConverter.csRewrite %CurrentUrl% canonical using resolved BaseUrl +13/-0

Rewrite %CurrentUrl% canonical using resolved BaseUrl

• Injects ISeoDomainResolver and applies BaseUrlHelper to the absolute current content URL before canonical normalization logic runs.

src/SeoToolkit.Umbraco.MetaFields.Core/Common/Converters/SeoValueConverters/TextSeoValueConverter.cs

SitemapGenerator.csApply BaseUrl to sitemap page and alternate URLs +6/-3

Apply BaseUrl to sitemap page and alternate URLs

• Stores BaseUrl from options and rewrites generated absolute URLs for both primary loc entries and hreflang alternate pages via BaseUrlHelper.

src/SeoToolkit.Umbraco.Sitemap.Core/Common/SitemapGenerators/SitemapGenerator.cs

SitemapIndexGenerator.csApply per-domain BaseUrl when generating sitemap index loc entries +12/-4

Apply per-domain BaseUrl when generating sitemap index loc entries

• Adds ISeoDomainsService dependency to resolve the relevant domain collection for each Umbraco domain and rewrites sitemap.xml loc URLs using BaseUrlHelper.

src/SeoToolkit.Umbraco.Sitemap.Core/Common/SitemapIndexGenerator/SitemapIndexGenerator.cs

SitemapMiddleware.csResolve BaseUrl in middleware and pass through sitemap options +7/-3

Resolve BaseUrl in middleware and pass through sitemap options

• Adds ISeoDomainResolver to the Invoke signature, resolves the current domain collection, and passes BaseUrl into SitemapGeneratorOptions for sitemap generation paths.

src/SeoToolkit.Umbraco.Sitemap.Core/Middleware/SitemapMiddleware.cs

SitemapGeneratorOptions.csExtend SitemapGeneratorOptions with BaseUrl +7/-1

Extend SitemapGeneratorOptions with BaseUrl

• Adds an optional BaseUrl property (default null) to transport the frontend host override into sitemap generation.

src/SeoToolkit.Umbraco.Sitemap.Core/Models/Business/SitemapGeneratorOptions.cs

Tests (3) +160 / -13
BaseUrlHelperTests.csAdd unit tests for BaseUrl URL rewriting behavior +86/-0

Add unit tests for BaseUrl URL rewriting behavior

• Introduces a new test suite validating BaseUrlHelper.ApplyBaseUrl across null/empty input, scheme normalization, path/query preservation, ports, and invalid inputs.

src/SeoToolkit.Tests/SeoToolkit.Tests/BaseUrlHelperTests.cs

RobotsSitemapProviderTests.csAdd RobotsSitemapProvider coverage for BaseUrl usage +60/-1

Add RobotsSitemapProvider coverage for BaseUrl usage

• Adds tests asserting robots.txt sitemap URLs are rewritten to the configured BaseUrl, including the no-domains edge case. Also cleans up an inline comment placement.

src/SeoToolkit.Tests/SeoToolkit.Tests/RobotsSitemapProviderTests.cs

SitemapMiddlewareTests.csUpdate middleware tests for new ISeoDomainResolver dependency +14/-12

Update middleware tests for new ISeoDomainResolver dependency

• Extends test setup to provide an ISeoDomainResolver mock and updates Invoke calls to match the new middleware signature.

src/SeoToolkit.Tests/SeoToolkit.Tests/SitemapMiddlewareTests.cs

Other (2) +22 / -0
SeoToolkitDomainBaseUrlMigration.csAdd migration to introduce BaseUrl column (varchar 500) +21/-0

Add migration to introduce BaseUrl column (varchar 500)

• Adds an async migration that conditionally adds a nullable BaseUrl column to SeoToolkitDomainCollections when the table exists and the column is missing.

src/SeoToolkit.Umbraco.Common.Core/Migrations/SeoToolkitDomainBaseUrlMigration.cs

USeoToolkitMigrationPlan.csRegister BaseUrl migration as state-7 +1/-0

Register BaseUrl migration as state-7

• Extends the migration plan to run SeoToolkitDomainBaseUrlMigration after existing state-6 migrations.

src/SeoToolkit.Umbraco.Common.Core/Migrations/USeoToolkitMigrationPlan.cs

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (1) 📜 Skill insights (0)

Grey Divider


Action required

1. BaseUrl scheme detection bug 🐞 Bug ≡ Correctness
Description
BaseUrlHelper.ApplyBaseUrl() treats any baseUrl starting with "http" as already having a URL scheme,
so hostnames like "http2.example.com" won’t get an "https://" prefix and will fail absolute URI
parsing, causing the configured BaseUrl to be ignored. This breaks the new headless/BaseUrl feature
for a non-obvious class of valid domains.
Code

src/SeoToolkit.Umbraco.Common.Core/Helpers/BaseUrlHelper.cs[R16-19]

+            var normalizedBaseUrl = baseUrl.TrimEnd('/');
+            if (!normalizedBaseUrl.StartsWith("http", StringComparison.OrdinalIgnoreCase))
+            {
+                normalizedBaseUrl = $"https://{normalizedBaseUrl}";
Relevance

●●● Strong

Deterministic edge-case bug; team previously accepted similar StartsWith("http") scheme-handling
fixes.

PR-#497

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The helper only prepends https when the string does not start with "http"; then it requires
UriKind.Absolute. A hostname like http2.example.com starts with "http" but has no scheme, so
parsing fails and the helper returns the original URL unchanged.

src/SeoToolkit.Umbraco.Common.Core/Helpers/BaseUrlHelper.cs[13-22]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`BaseUrlHelper.ApplyBaseUrl` uses `StartsWith("http")` to decide whether to prepend a scheme. This incorrectly classifies hostnames that begin with `http` (e.g., `http2.example.com`) as already schemed, causing `Uri.TryCreate(..., UriKind.Absolute, ...)` to fail and the helper to return the original URL (silently disabling BaseUrl).

## Issue Context
BaseUrl is user-configured and expected to accept values without a scheme (per tests and UI). The current heuristic should detect a *real* scheme, not just an `http` prefix.

## Fix Focus Areas
- src/SeoToolkit.Umbraco.Common.Core/Helpers/BaseUrlHelper.cs[16-22]

### Suggested implementation approach
- Trim whitespace first: `baseUrl.Trim()`.
- Detect scheme by checking for `://` (or `Uri.TryCreate` with `UriKind.Absolute` and validating scheme is http/https).
- If no scheme is present, prepend `https://` and retry parsing.
- Add a regression unit test for `baseUrl = "http2.example.com"` (and optionally leading/trailing whitespace).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Tests added outside SeoToolkit.Tests/ 📘 Rule violation ▣ Testability
Description
New/updated test code is located under src/SeoToolkit.Tests/... instead of a repository-root
SeoToolkit.Tests/ directory. This violates the required test-code placement convention and can
make test discovery/organization inconsistent.
Code

src/SeoToolkit.Tests/SeoToolkit.Tests/BaseUrlHelperTests.cs[R1-4]

+using SeoToolkit.Umbraco.Common.Core.Helpers;
+
+namespace SeoToolkit.Tests
+{
Relevance

●● Moderate

Repo already keeps tests under src/SeoToolkit.Tests in prior merged PRs; convention enforcement
unclear.

PR-#492
PR-#561

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 1560502 requires all automated test source files to live under the repository-root
SeoToolkit.Tests/ directory. The PR adds a new test file under
src/SeoToolkit.Tests/SeoToolkit.Tests/, which is not the required root-level location.

Rule 1560502: Place test code in SeoToolkit.Tests directory
src/SeoToolkit.Tests/SeoToolkit.Tests/BaseUrlHelperTests.cs[1-4]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The PR adds/updates test code under `src/SeoToolkit.Tests/...`, but the compliance requirement mandates that test code resides under a repository-root `SeoToolkit.Tests/` directory.

## Issue Context
This PR adds `BaseUrlHelperTests.cs` (and updates other test files) in the `src/` tree, which is outside the required root-level `SeoToolkit.Tests/` location.

## Fix Focus Areas
- src/SeoToolkit.Tests/SeoToolkit.Tests/BaseUrlHelperTests.cs[1-4]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

3. Extra UmbracoContext scope 🐞 Bug ➹ Performance
Description
SitemapMiddleware.Invoke() resolves the domain via ISeoDomainResolver before entering its own
EnsureUmbracoContext() scope, which acquires an additional UmbracoContextFactory scope on sitemap
requests. Depending on Umbraco context-factory semantics, this may cause unnecessary overhead per
sitemap request.
Code

src/SeoToolkit.Umbraco.Sitemap.Core/Middleware/SitemapMiddleware.cs[R49-51]

            var settings = _sitemapConfigurationService.GetSettings();
+            var seoDomain = seoDomainResolver.ResolveDomain();
+            var baseUrl = seoDomain?.BaseUrl;
Relevance

●● Moderate

Potential extra UmbracoContext scope is semantic/perf-sensitive; no close historical precedent
specifically addressing double EnsureUmbracoContext.

PR-#494
PR-#405

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The middleware now calls ResolveDomain() before its own EnsureUmbracoContext(); the resolver
implementation uses EnsureUmbracoContext() when resolving by request URL, so sitemap requests
acquire two context-factory scopes sequentially.

src/SeoToolkit.Umbraco.Sitemap.Core/Middleware/SitemapMiddleware.cs[49-66]
src/SeoToolkit.Umbraco.Common.Core/Helpers/SeoDomainResolver.cs[25-35]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`SitemapMiddleware.Invoke` calls `seoDomainResolver.ResolveDomain()` before it creates its own UmbracoContext scope, and `SeoDomainResolver` internally calls `EnsureUmbracoContext()` as well. This results in an extra context-factory scope acquisition for sitemap requests.

## Issue Context
SitemapMiddleware already selects the Umbraco `Domain` inside its `EnsureUmbracoContext()` scope and could resolve the matching `SeoDomainCollection` (and BaseUrl) without needing a separate resolver call.

## Fix Focus Areas
- src/SeoToolkit.Umbraco.Sitemap.Core/Middleware/SitemapMiddleware.cs[49-66]
- src/SeoToolkit.Umbraco.Common.Core/Helpers/SeoDomainResolver.cs[25-35]

### Suggested implementation approach
- Move BaseUrl resolution inside the existing `using var ctx = _umbracoContextFactory.EnsureUmbracoContext()`.
- Option A: Inject `ISeoDomainsService` into the middleware and call `GetByDomain(domain.Id)` after `DomainUtilities.SelectDomain(...)`.
- Option B: Add a resolver method that can resolve from an already-available domain id (avoiding `EnsureUmbracoContext()` internally).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context used
✅ Compliance rules (platform): 8 rules

To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Qodo Logo

Comment on lines +1 to +4
using SeoToolkit.Umbraco.Common.Core.Helpers;

namespace SeoToolkit.Tests
{

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

1. Tests added outside seotoolkit.tests/ 📘 Rule violation ▣ Testability

New/updated test code is located under src/SeoToolkit.Tests/... instead of a repository-root
SeoToolkit.Tests/ directory. This violates the required test-code placement convention and can
make test discovery/organization inconsistent.
Agent Prompt
## Issue description
The PR adds/updates test code under `src/SeoToolkit.Tests/...`, but the compliance requirement mandates that test code resides under a repository-root `SeoToolkit.Tests/` directory.

## Issue Context
This PR adds `BaseUrlHelperTests.cs` (and updates other test files) in the `src/` tree, which is outside the required root-level `SeoToolkit.Tests/` location.

## Fix Focus Areas
- src/SeoToolkit.Tests/SeoToolkit.Tests/BaseUrlHelperTests.cs[1-4]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +16 to +19
var normalizedBaseUrl = baseUrl.TrimEnd('/');
if (!normalizedBaseUrl.StartsWith("http", StringComparison.OrdinalIgnoreCase))
{
normalizedBaseUrl = $"https://{normalizedBaseUrl}";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

2. Baseurl scheme detection bug 🐞 Bug ≡ Correctness

BaseUrlHelper.ApplyBaseUrl() treats any baseUrl starting with "http" as already having a URL scheme,
so hostnames like "http2.example.com" won’t get an "https://" prefix and will fail absolute URI
parsing, causing the configured BaseUrl to be ignored. This breaks the new headless/BaseUrl feature
for a non-obvious class of valid domains.
Agent Prompt
## Issue description
`BaseUrlHelper.ApplyBaseUrl` uses `StartsWith("http")` to decide whether to prepend a scheme. This incorrectly classifies hostnames that begin with `http` (e.g., `http2.example.com`) as already schemed, causing `Uri.TryCreate(..., UriKind.Absolute, ...)` to fail and the helper to return the original URL (silently disabling BaseUrl).

## Issue Context
BaseUrl is user-configured and expected to accept values without a scheme (per tests and UI). The current heuristic should detect a *real* scheme, not just an `http` prefix.

## Fix Focus Areas
- src/SeoToolkit.Umbraco.Common.Core/Helpers/BaseUrlHelper.cs[16-22]

### Suggested implementation approach
- Trim whitespace first: `baseUrl.Trim()`.
- Detect scheme by checking for `://` (or `Uri.TryCreate` with `UriKind.Absolute` and validating scheme is http/https).
- If no scheme is present, prepend `https://` and retry parsing.
- Add a regression unit test for `baseUrl = "http2.example.com"` (and optionally leading/trailing whitespace).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines 49 to +51
var settings = _sitemapConfigurationService.GetSettings();
var seoDomain = seoDomainResolver.ResolveDomain();
var baseUrl = seoDomain?.BaseUrl;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Informational

3. Extra umbracocontext scope 🐞 Bug ➹ Performance

SitemapMiddleware.Invoke() resolves the domain via ISeoDomainResolver before entering its own
EnsureUmbracoContext() scope, which acquires an additional UmbracoContextFactory scope on sitemap
requests. Depending on Umbraco context-factory semantics, this may cause unnecessary overhead per
sitemap request.
Agent Prompt
## Issue description
`SitemapMiddleware.Invoke` calls `seoDomainResolver.ResolveDomain()` before it creates its own UmbracoContext scope, and `SeoDomainResolver` internally calls `EnsureUmbracoContext()` as well. This results in an extra context-factory scope acquisition for sitemap requests.

## Issue Context
SitemapMiddleware already selects the Umbraco `Domain` inside its `EnsureUmbracoContext()` scope and could resolve the matching `SeoDomainCollection` (and BaseUrl) without needing a separate resolver call.

## Fix Focus Areas
- src/SeoToolkit.Umbraco.Sitemap.Core/Middleware/SitemapMiddleware.cs[49-66]
- src/SeoToolkit.Umbraco.Common.Core/Helpers/SeoDomainResolver.cs[25-35]

### Suggested implementation approach
- Move BaseUrl resolution inside the existing `using var ctx = _umbracoContextFactory.EnsureUmbracoContext()`.
- Option A: Inject `ISeoDomainsService` into the middleware and call `GetByDomain(domain.Id)` after `DomainUtilities.SelectDomain(...)`.
- Option B: Add a resolver method that can resolve from an already-available domain id (avoiding `EnsureUmbracoContext()` internally).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@patrickdemooij9
patrickdemooij9 merged commit 3d07e2b into dev/main Aug 6, 2026
@patrickdemooij9
patrickdemooij9 deleted the copilot/add-baseurl-field-domain-settings branch August 6, 2026 17:59
patrickdemooij9 added a commit that referenced this pull request Aug 15, 2026
…pport (#549)

* Initial plan

* Add BaseUrl field to domain settings for headless CMS frontend URL support

- Add BaseUrl column to SeoDomainCollectionEntity and SeoDomainCollection model
- Create database migration for new column
- Add BaseUrlHelper utility to rewrite URLs with configured base URL
- Update SitemapGenerator, SitemapIndexGenerator, and SitemapMiddleware to apply BaseUrl
- Update RobotsSitemapProvider to use BaseUrl for sitemap URLs in robots.txt
- Update TextSeoValueConverter to apply BaseUrl for canonical URLs (%CurrentUrl%)
- Add BaseUrlHelper unit tests and RobotsSitemapProvider BaseUrl tests

Closes #548

* Address review: specify column length (500) for BaseUrl migration and entity

* Finish the UI

* Fix tests

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: patrickdemooij9 <11466511+patrickdemooij9@users.noreply.github.com>
Co-authored-by: patrickdemooij9 <patrickdemooij98@hotmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Headless sitemap.xml uses cms url instead of frontend url

2 participants