Skip to content

feat: support configurable hosted upstream#93

Open
jiechic wants to merge 1 commit into
ricardoboss:mainfrom
jiechic:main
Open

feat: support configurable hosted upstream#93
jiechic wants to merge 1 commit into
ricardoboss:mainfrom
jiechic:main

Conversation

@jiechic

@jiechic jiechic commented Jun 10, 2026

Copy link
Copy Markdown

Summary

This PR makes PubNet's hosted upstream configurable instead of hardcoding https://pub.dev/api/.

It adds support for configuring the upstream through application configuration or environment variables, while keeping https://pub.dev/api/ as the fallback default.

Why

Today, PubNet can fall back to pub.dev for package metadata and archive downloads, but the upstream is fixed in code.

Making the upstream configurable improves deployment flexibility in cases such as:

  • falling back to an existing unpub deployment during a migration to PubNet
  • using an internal hosted repository as the upstream source
  • using a regional mirror when direct access to pub.dev is slow or unreliable
  • using a China-based mirror or another regional mirror where direct access to pub.dev is unstable

This makes it easier to use PubNet as the main entry point while still relying on another hosted repository for packages that are not stored
locally.

Changes

  • Add configurable HostedUpstream:BaseUrl
  • Support environment variable override via HostedUpstream__BaseUrl
  • Fall back to https://pub.dev/api/ when the configured value is missing or invalid
  • Switch upstream client requests to relative paths so path-prefixed upstream base URLs work correctly
  • Document JSON configuration, environment variable usage, and Docker Compose examples in the README

Notes on URL handling

The upstream client now uses relative request paths such as:

  • packages/{name}
  • packages/{name}/versions/{version}

instead of absolute paths starting with /api/....

This is important because absolute paths would ignore any configured path prefix in BaseAddress. Using relative paths ensures values like:

  • https://pub.dev/api/
  • https://unpub.example.com/api/
  • https://example.com/some/prefix/api/

all resolve correctly.

Example configuration

{
  "HostedUpstream": {
    "BaseUrl": "https://unpub.example.com/api/"
  }
}

Or through environment variables:

HostedUpstream__BaseUrl=https://unpub.example.com/api/

## Validation

I do not currently have a working verification environment for this repository, so this PR has not been runtime-validated.

Specifically:

- I did not run a full build locally
- I did not run tests
- I did not validate the behavior against a live unpub deployment or a regional mirror

The change was made based on code inspection and URI resolution behavior, but it should still be reviewed and verified in a proper
development environment before merge.

@ricardoboss ricardoboss left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Hi! Thanks for opening a PR.

This is a great suggestion and I want to include it. For options and option validation, we should use .NETs options pattern (linked below).

Comment thread src/PubNet.API/Program.cs
Comment on lines +31 to +57
const string defaultHostedUpstreamBaseUrl = "https://pub.dev/api/";

Uri ResolveHostedUpstreamBaseAddress(IServiceProvider services)
{
var configuration = services.GetRequiredService<IConfiguration>();
var logger = services.GetRequiredService<ILoggerFactory>().CreateLogger("HostedUpstream");
var configuredBaseUrl = configuration["HostedUpstream:BaseUrl"];

if (string.IsNullOrWhiteSpace(configuredBaseUrl))
return new(defaultHostedUpstreamBaseUrl);

if (!Uri.TryCreate(configuredBaseUrl, UriKind.Absolute, out var configuredUri) ||
(configuredUri.Scheme != Uri.UriSchemeHttp && configuredUri.Scheme != Uri.UriSchemeHttps))
{
logger.LogWarning(
"Invalid HostedUpstream:BaseUrl value \"{HostedUpstreamBaseUrl}\". Falling back to default {DefaultHostedUpstreamBaseUrl}",
configuredBaseUrl, defaultHostedUpstreamBaseUrl);

return new(defaultHostedUpstreamBaseUrl);
}

var normalizedBaseUrl = configuredUri.ToString();
if (!normalizedBaseUrl.EndsWith('/'))
normalizedBaseUrl += "/";

return new(normalizedBaseUrl);
}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

This kind of validation should happen using the options pattern (https://learn.microsoft.com/en-us/dotnet/core/extensions/options) + options validation.

Comment on lines +44 to +46
"HostedUpstream": {
"BaseUrl": "https://pub.dev/api/"
},

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

This is the default anyway, no need to put it in here too.

Comment on lines +15 to +17
"HostedUpstream": {
"BaseUrl": "https://pub.dev/api/"
},

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

This is the default, no need to include it here.

Comment on lines +16 to +18
"HostedUpstream": {
"BaseUrl": "https://pub.dev/api/"
},

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Default value; don't include

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.

2 participants